aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src
diff options
context:
space:
mode:
authorIgor Aleksanov <[email protected]>2020-10-18 11:09:00 +0100
committerIgor Aleksanov <[email protected]>2020-10-18 11:09:00 +0100
commit9e7c952bbddc2e6763c49f0511a295362e9893d6 (patch)
tree5b144eabe1eaf62aa1ec5b804ee6fff00f5f84e9 /crates/ide/src
parent2067a410f31810f6e1941a86cdea0247c3b7d6f4 (diff)
Extract call_info and completion into separate crates
Diffstat (limited to 'crates/ide/src')
-rw-r--r--crates/ide/src/call_hierarchy.rs4
-rw-r--r--crates/ide/src/call_info.rs742
-rw-r--r--crates/ide/src/completion.rs260
-rw-r--r--crates/ide/src/completion/complete_attribute.rs657
-rw-r--r--crates/ide/src/completion/complete_dot.rs431
-rw-r--r--crates/ide/src/completion/complete_fn_param.rs135
-rw-r--r--crates/ide/src/completion/complete_keyword.rs568
-rw-r--r--crates/ide/src/completion/complete_macro_in_item_position.rs41
-rw-r--r--crates/ide/src/completion/complete_mod.rs324
-rw-r--r--crates/ide/src/completion/complete_pattern.rs88
-rw-r--r--crates/ide/src/completion/complete_postfix.rs454
-rw-r--r--crates/ide/src/completion/complete_postfix/format_like.rs279
-rw-r--r--crates/ide/src/completion/complete_qualified_path.rs755
-rw-r--r--crates/ide/src/completion/complete_record.rs226
-rw-r--r--crates/ide/src/completion/complete_snippet.rs114
-rw-r--r--crates/ide/src/completion/complete_trait_impl.rs733
-rw-r--r--crates/ide/src/completion/complete_unqualified_path.rs679
-rw-r--r--crates/ide/src/completion/completion_config.rs35
-rw-r--r--crates/ide/src/completion/completion_context.rs523
-rw-r--r--crates/ide/src/completion/completion_item.rs384
-rw-r--r--crates/ide/src/completion/generated_features.rs4
-rw-r--r--crates/ide/src/completion/patterns.rs249
-rw-r--r--crates/ide/src/completion/presentation.rs1346
-rw-r--r--crates/ide/src/completion/test_utils.rs125
-rw-r--r--crates/ide/src/display.rs81
-rw-r--r--crates/ide/src/lib.rs10
-rw-r--r--crates/ide/src/syntax_highlighting/injection.rs6
27 files changed, 9 insertions, 9244 deletions
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index d2cf2cc7d..9d6433fe0 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -2,13 +2,13 @@
2 2
3use indexmap::IndexMap; 3use indexmap::IndexMap;
4 4
5use call_info::FnCallNode;
5use hir::Semantics; 6use hir::Semantics;
6use ide_db::RootDatabase; 7use ide_db::RootDatabase;
7use syntax::{ast, match_ast, AstNode, TextRange}; 8use syntax::{ast, match_ast, AstNode, TextRange};
8 9
9use crate::{ 10use crate::{
10 call_info::FnCallNode, display::ToNav, goto_definition, references, FilePosition, 11 display::ToNav, goto_definition, references, FilePosition, NavigationTarget, RangeInfo,
11 NavigationTarget, RangeInfo,
12}; 12};
13 13
14#[derive(Debug, Clone)] 14#[derive(Debug, Clone)]
diff --git a/crates/ide/src/call_info.rs b/crates/ide/src/call_info.rs
deleted file mode 100644
index d7b2b926e..000000000
--- a/crates/ide/src/call_info.rs
+++ /dev/null
@@ -1,742 +0,0 @@
1//! FIXME: write short doc here
2use either::Either;
3use hir::{HasAttrs, HirDisplay, Semantics, Type};
4use ide_db::RootDatabase;
5use stdx::format_to;
6use syntax::{
7 ast::{self, ArgListOwner},
8 match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize,
9};
10use test_utils::mark;
11
12use crate::FilePosition;
13
14/// Contains information about a call site. Specifically the
15/// `FunctionSignature`and current parameter.
16#[derive(Debug)]
17pub struct CallInfo {
18 pub doc: Option<String>,
19 pub signature: String,
20 pub active_parameter: Option<usize>,
21 parameters: Vec<TextRange>,
22}
23
24impl CallInfo {
25 pub fn parameter_labels(&self) -> impl Iterator<Item = &str> + '_ {
26 self.parameters.iter().map(move |&it| &self.signature[it])
27 }
28 pub fn parameter_ranges(&self) -> &[TextRange] {
29 &self.parameters
30 }
31 fn push_param(&mut self, param: &str) {
32 if !self.signature.ends_with('(') {
33 self.signature.push_str(", ");
34 }
35 let start = TextSize::of(&self.signature);
36 self.signature.push_str(param);
37 let end = TextSize::of(&self.signature);
38 self.parameters.push(TextRange::new(start, end))
39 }
40}
41
42/// Computes parameter information for the given call expression.
43pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
44 let sema = Semantics::new(db);
45 let file = sema.parse(position.file_id);
46 let file = file.syntax();
47 let token = file.token_at_offset(position.offset).next()?;
48 let token = sema.descend_into_macros(token);
49
50 let (callable, active_parameter) = call_info_impl(&sema, token)?;
51
52 let mut res =
53 CallInfo { doc: None, signature: String::new(), parameters: vec![], active_parameter };
54
55 match callable.kind() {
56 hir::CallableKind::Function(func) => {
57 res.doc = func.docs(db).map(|it| it.as_str().to_string());
58 format_to!(res.signature, "fn {}", func.name(db));
59 }
60 hir::CallableKind::TupleStruct(strukt) => {
61 res.doc = strukt.docs(db).map(|it| it.as_str().to_string());
62 format_to!(res.signature, "struct {}", strukt.name(db));
63 }
64 hir::CallableKind::TupleEnumVariant(variant) => {
65 res.doc = variant.docs(db).map(|it| it.as_str().to_string());
66 format_to!(
67 res.signature,
68 "enum {}::{}",
69 variant.parent_enum(db).name(db),
70 variant.name(db)
71 );
72 }
73 hir::CallableKind::Closure => (),
74 }
75
76 res.signature.push('(');
77 {
78 if let Some(self_param) = callable.receiver_param(db) {
79 format_to!(res.signature, "{}", self_param)
80 }
81 let mut buf = String::new();
82 for (pat, ty) in callable.params(db) {
83 buf.clear();
84 if let Some(pat) = pat {
85 match pat {
86 Either::Left(_self) => format_to!(buf, "self: "),
87 Either::Right(pat) => format_to!(buf, "{}: ", pat),
88 }
89 }
90 format_to!(buf, "{}", ty.display(db));
91 res.push_param(&buf);
92 }
93 }
94 res.signature.push(')');
95
96 match callable.kind() {
97 hir::CallableKind::Function(_) | hir::CallableKind::Closure => {
98 let ret_type = callable.return_type();
99 if !ret_type.is_unit() {
100 format_to!(res.signature, " -> {}", ret_type.display(db));
101 }
102 }
103 hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
104 }
105 Some(res)
106}
107
108fn call_info_impl(
109 sema: &Semantics<RootDatabase>,
110 token: SyntaxToken,
111) -> Option<(hir::Callable, Option<usize>)> {
112 // Find the calling expression and it's NameRef
113 let calling_node = FnCallNode::with_node(&token.parent())?;
114
115 let callable = match &calling_node {
116 FnCallNode::CallExpr(call) => sema.type_of_expr(&call.expr()?)?.as_callable(sema.db)?,
117 FnCallNode::MethodCallExpr(call) => sema.resolve_method_call_as_callable(call)?,
118 };
119 let active_param = if let Some(arg_list) = calling_node.arg_list() {
120 // Number of arguments specified at the call site
121 let num_args_at_callsite = arg_list.args().count();
122
123 let arg_list_range = arg_list.syntax().text_range();
124 if !arg_list_range.contains_inclusive(token.text_range().start()) {
125 mark::hit!(call_info_bad_offset);
126 return None;
127 }
128 let param = std::cmp::min(
129 num_args_at_callsite,
130 arg_list
131 .args()
132 .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
133 .count(),
134 );
135
136 Some(param)
137 } else {
138 None
139 };
140 Some((callable, active_param))
141}
142
143#[derive(Debug)]
144pub(crate) struct ActiveParameter {
145 pub(crate) ty: Type,
146 pub(crate) name: String,
147}
148
149impl ActiveParameter {
150 pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option<Self> {
151 let sema = Semantics::new(db);
152 let file = sema.parse(position.file_id);
153 let file = file.syntax();
154 let token = file.token_at_offset(position.offset).next()?;
155 let token = sema.descend_into_macros(token);
156 Self::at_token(&sema, token)
157 }
158
159 pub(crate) fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> {
160 let (signature, active_parameter) = call_info_impl(&sema, token)?;
161
162 let idx = active_parameter?;
163 let mut params = signature.params(sema.db);
164 if !(idx < params.len()) {
165 mark::hit!(too_many_arguments);
166 return None;
167 }
168 let (pat, ty) = params.swap_remove(idx);
169 let name = pat?.to_string();
170 Some(ActiveParameter { ty, name })
171 }
172}
173
174#[derive(Debug)]
175pub(crate) enum FnCallNode {
176 CallExpr(ast::CallExpr),
177 MethodCallExpr(ast::MethodCallExpr),
178}
179
180impl FnCallNode {
181 fn with_node(syntax: &SyntaxNode) -> Option<FnCallNode> {
182 syntax.ancestors().find_map(|node| {
183 match_ast! {
184 match node {
185 ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
186 ast::MethodCallExpr(it) => {
187 let arg_list = it.arg_list()?;
188 if !arg_list.syntax().text_range().contains_range(syntax.text_range()) {
189 return None;
190 }
191 Some(FnCallNode::MethodCallExpr(it))
192 },
193 _ => None,
194 }
195 }
196 })
197 }
198
199 pub(crate) fn with_node_exact(node: &SyntaxNode) -> Option<FnCallNode> {
200 match_ast! {
201 match node {
202 ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
203 ast::MethodCallExpr(it) => Some(FnCallNode::MethodCallExpr(it)),
204 _ => None,
205 }
206 }
207 }
208
209 pub(crate) fn name_ref(&self) -> Option<ast::NameRef> {
210 match self {
211 FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()? {
212 ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
213 _ => return None,
214 }),
215
216 FnCallNode::MethodCallExpr(call_expr) => {
217 call_expr.syntax().children().filter_map(ast::NameRef::cast).next()
218 }
219 }
220 }
221
222 fn arg_list(&self) -> Option<ast::ArgList> {
223 match self {
224 FnCallNode::CallExpr(expr) => expr.arg_list(),
225 FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
226 }
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use expect_test::{expect, Expect};
233 use test_utils::mark;
234
235 use crate::fixture;
236
237 fn check(ra_fixture: &str, expect: Expect) {
238 let (analysis, position) = fixture::position(ra_fixture);
239 let call_info = analysis.call_info(position).unwrap();
240 let actual = match call_info {
241 Some(call_info) => {
242 let docs = match &call_info.doc {
243 None => "".to_string(),
244 Some(docs) => format!("{}\n------\n", docs.as_str()),
245 };
246 let params = call_info
247 .parameter_labels()
248 .enumerate()
249 .map(|(i, param)| {
250 if Some(i) == call_info.active_parameter {
251 format!("<{}>", param)
252 } else {
253 param.to_string()
254 }
255 })
256 .collect::<Vec<_>>()
257 .join(", ");
258 format!("{}{}\n({})\n", docs, call_info.signature, params)
259 }
260 None => String::new(),
261 };
262 expect.assert_eq(&actual);
263 }
264
265 #[test]
266 fn test_fn_signature_two_args() {
267 check(
268 r#"
269fn foo(x: u32, y: u32) -> u32 {x + y}
270fn bar() { foo(<|>3, ); }
271"#,
272 expect![[r#"
273 fn foo(x: u32, y: u32) -> u32
274 (<x: u32>, y: u32)
275 "#]],
276 );
277 check(
278 r#"
279fn foo(x: u32, y: u32) -> u32 {x + y}
280fn bar() { foo(3<|>, ); }
281"#,
282 expect![[r#"
283 fn foo(x: u32, y: u32) -> u32
284 (<x: u32>, y: u32)
285 "#]],
286 );
287 check(
288 r#"
289fn foo(x: u32, y: u32) -> u32 {x + y}
290fn bar() { foo(3,<|> ); }
291"#,
292 expect![[r#"
293 fn foo(x: u32, y: u32) -> u32
294 (x: u32, <y: u32>)
295 "#]],
296 );
297 check(
298 r#"
299fn foo(x: u32, y: u32) -> u32 {x + y}
300fn bar() { foo(3, <|>); }
301"#,
302 expect![[r#"
303 fn foo(x: u32, y: u32) -> u32
304 (x: u32, <y: u32>)
305 "#]],
306 );
307 }
308
309 #[test]
310 fn test_fn_signature_two_args_empty() {
311 check(
312 r#"
313fn foo(x: u32, y: u32) -> u32 {x + y}
314fn bar() { foo(<|>); }
315"#,
316 expect![[r#"
317 fn foo(x: u32, y: u32) -> u32
318 (<x: u32>, y: u32)
319 "#]],
320 );
321 }
322
323 #[test]
324 fn test_fn_signature_two_args_first_generics() {
325 check(
326 r#"
327fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
328 where T: Copy + Display, U: Debug
329{ x + y }
330
331fn bar() { foo(<|>3, ); }
332"#,
333 expect![[r#"
334 fn foo(x: i32, y: {unknown}) -> u32
335 (<x: i32>, y: {unknown})
336 "#]],
337 );
338 }
339
340 #[test]
341 fn test_fn_signature_no_params() {
342 check(
343 r#"
344fn foo<T>() -> T where T: Copy + Display {}
345fn bar() { foo(<|>); }
346"#,
347 expect![[r#"
348 fn foo() -> {unknown}
349 ()
350 "#]],
351 );
352 }
353
354 #[test]
355 fn test_fn_signature_for_impl() {
356 check(
357 r#"
358struct F;
359impl F { pub fn new() { } }
360fn bar() {
361 let _ : F = F::new(<|>);
362}
363"#,
364 expect![[r#"
365 fn new()
366 ()
367 "#]],
368 );
369 }
370
371 #[test]
372 fn test_fn_signature_for_method_self() {
373 check(
374 r#"
375struct S;
376impl S { pub fn do_it(&self) {} }
377
378fn bar() {
379 let s: S = S;
380 s.do_it(<|>);
381}
382"#,
383 expect![[r#"
384 fn do_it(&self)
385 ()
386 "#]],
387 );
388 }
389
390 #[test]
391 fn test_fn_signature_for_method_with_arg() {
392 check(
393 r#"
394struct S;
395impl S {
396 fn foo(&self, x: i32) {}
397}
398
399fn main() { S.foo(<|>); }
400"#,
401 expect![[r#"
402 fn foo(&self, x: i32)
403 (<x: i32>)
404 "#]],
405 );
406 }
407
408 #[test]
409 fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
410 check(
411 r#"
412struct S;
413impl S {
414 fn foo(&self, x: i32) {}
415}
416
417fn main() { S::foo(<|>); }
418"#,
419 expect![[r#"
420 fn foo(self: &S, x: i32)
421 (<self: &S>, x: i32)
422 "#]],
423 );
424 }
425
426 #[test]
427 fn test_fn_signature_with_docs_simple() {
428 check(
429 r#"
430/// test
431// non-doc-comment
432fn foo(j: u32) -> u32 {
433 j
434}
435
436fn bar() {
437 let _ = foo(<|>);
438}
439"#,
440 expect![[r#"
441 test
442 ------
443 fn foo(j: u32) -> u32
444 (<j: u32>)
445 "#]],
446 );
447 }
448
449 #[test]
450 fn test_fn_signature_with_docs() {
451 check(
452 r#"
453/// Adds one to the number given.
454///
455/// # Examples
456///
457/// ```
458/// let five = 5;
459///
460/// assert_eq!(6, my_crate::add_one(5));
461/// ```
462pub fn add_one(x: i32) -> i32 {
463 x + 1
464}
465
466pub fn do() {
467 add_one(<|>
468}"#,
469 expect![[r##"
470 Adds one to the number given.
471
472 # Examples
473
474 ```
475 let five = 5;
476
477 assert_eq!(6, my_crate::add_one(5));
478 ```
479 ------
480 fn add_one(x: i32) -> i32
481 (<x: i32>)
482 "##]],
483 );
484 }
485
486 #[test]
487 fn test_fn_signature_with_docs_impl() {
488 check(
489 r#"
490struct addr;
491impl addr {
492 /// Adds one to the number given.
493 ///
494 /// # Examples
495 ///
496 /// ```
497 /// let five = 5;
498 ///
499 /// assert_eq!(6, my_crate::add_one(5));
500 /// ```
501 pub fn add_one(x: i32) -> i32 {
502 x + 1
503 }
504}
505
506pub fn do_it() {
507 addr {};
508 addr::add_one(<|>);
509}
510"#,
511 expect![[r##"
512 Adds one to the number given.
513
514 # Examples
515
516 ```
517 let five = 5;
518
519 assert_eq!(6, my_crate::add_one(5));
520 ```
521 ------
522 fn add_one(x: i32) -> i32
523 (<x: i32>)
524 "##]],
525 );
526 }
527
528 #[test]
529 fn test_fn_signature_with_docs_from_actix() {
530 check(
531 r#"
532struct WriteHandler<E>;
533
534impl<E> WriteHandler<E> {
535 /// Method is called when writer emits error.
536 ///
537 /// If this method returns `ErrorAction::Continue` writer processing
538 /// continues otherwise stream processing stops.
539 fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
540 Running::Stop
541 }
542
543 /// Method is called when writer finishes.
544 ///
545 /// By default this method stops actor's `Context`.
546 fn finished(&mut self, ctx: &mut Self::Context) {
547 ctx.stop()
548 }
549}
550
551pub fn foo(mut r: WriteHandler<()>) {
552 r.finished(<|>);
553}
554"#,
555 expect![[r#"
556 Method is called when writer finishes.
557
558 By default this method stops actor's `Context`.
559 ------
560 fn finished(&mut self, ctx: &mut {unknown})
561 (<ctx: &mut {unknown}>)
562 "#]],
563 );
564 }
565
566 #[test]
567 fn call_info_bad_offset() {
568 mark::check!(call_info_bad_offset);
569 check(
570 r#"
571fn foo(x: u32, y: u32) -> u32 {x + y}
572fn bar() { foo <|> (3, ); }
573"#,
574 expect![[""]],
575 );
576 }
577
578 #[test]
579 fn test_nested_method_in_lambda() {
580 check(
581 r#"
582struct Foo;
583impl Foo { fn bar(&self, _: u32) { } }
584
585fn bar(_: u32) { }
586
587fn main() {
588 let foo = Foo;
589 std::thread::spawn(move || foo.bar(<|>));
590}
591"#,
592 expect![[r#"
593 fn bar(&self, _: u32)
594 (<_: u32>)
595 "#]],
596 );
597 }
598
599 #[test]
600 fn works_for_tuple_structs() {
601 check(
602 r#"
603/// A cool tuple struct
604struct S(u32, i32);
605fn main() {
606 let s = S(0, <|>);
607}
608"#,
609 expect![[r#"
610 A cool tuple struct
611 ------
612 struct S(u32, i32)
613 (u32, <i32>)
614 "#]],
615 );
616 }
617
618 #[test]
619 fn generic_struct() {
620 check(
621 r#"
622struct S<T>(T);
623fn main() {
624 let s = S(<|>);
625}
626"#,
627 expect![[r#"
628 struct S({unknown})
629 (<{unknown}>)
630 "#]],
631 );
632 }
633
634 #[test]
635 fn works_for_enum_variants() {
636 check(
637 r#"
638enum E {
639 /// A Variant
640 A(i32),
641 /// Another
642 B,
643 /// And C
644 C { a: i32, b: i32 }
645}
646
647fn main() {
648 let a = E::A(<|>);
649}
650"#,
651 expect![[r#"
652 A Variant
653 ------
654 enum E::A(i32)
655 (<i32>)
656 "#]],
657 );
658 }
659
660 #[test]
661 fn cant_call_struct_record() {
662 check(
663 r#"
664struct S { x: u32, y: i32 }
665fn main() {
666 let s = S(<|>);
667}
668"#,
669 expect![[""]],
670 );
671 }
672
673 #[test]
674 fn cant_call_enum_record() {
675 check(
676 r#"
677enum E {
678 /// A Variant
679 A(i32),
680 /// Another
681 B,
682 /// And C
683 C { a: i32, b: i32 }
684}
685
686fn main() {
687 let a = E::C(<|>);
688}
689"#,
690 expect![[""]],
691 );
692 }
693
694 #[test]
695 fn fn_signature_for_call_in_macro() {
696 check(
697 r#"
698macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
699fn foo() { }
700id! {
701 fn bar() { foo(<|>); }
702}
703"#,
704 expect![[r#"
705 fn foo()
706 ()
707 "#]],
708 );
709 }
710
711 #[test]
712 fn call_info_for_lambdas() {
713 check(
714 r#"
715struct S;
716fn foo(s: S) -> i32 { 92 }
717fn main() {
718 (|s| foo(s))(<|>)
719}
720 "#,
721 expect![[r#"
722 (S) -> i32
723 (<S>)
724 "#]],
725 )
726 }
727
728 #[test]
729 fn call_info_for_fn_ptr() {
730 check(
731 r#"
732fn main(f: fn(i32, f64) -> char) {
733 f(0, <|>)
734}
735 "#,
736 expect![[r#"
737 (i32, f64) -> char
738 (i32, <f64>)
739 "#]],
740 )
741 }
742}
diff --git a/crates/ide/src/completion.rs b/crates/ide/src/completion.rs
deleted file mode 100644
index 69e875014..000000000
--- a/crates/ide/src/completion.rs
+++ /dev/null
@@ -1,260 +0,0 @@
1mod completion_config;
2mod completion_item;
3mod completion_context;
4mod presentation;
5mod patterns;
6mod generated_features;
7#[cfg(test)]
8mod test_utils;
9
10mod complete_attribute;
11mod complete_dot;
12mod complete_record;
13mod complete_pattern;
14mod complete_fn_param;
15mod complete_keyword;
16mod complete_snippet;
17mod complete_qualified_path;
18mod complete_unqualified_path;
19mod complete_postfix;
20mod complete_macro_in_item_position;
21mod complete_trait_impl;
22mod complete_mod;
23
24use ide_db::RootDatabase;
25
26use crate::{
27 completion::{
28 completion_context::CompletionContext,
29 completion_item::{CompletionKind, Completions},
30 },
31 FilePosition,
32};
33
34pub use crate::completion::{
35 completion_config::CompletionConfig,
36 completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
37};
38
39//FIXME: split the following feature into fine-grained features.
40
41// Feature: Magic Completions
42//
43// In addition to usual reference completion, rust-analyzer provides some ✨magic✨
44// completions as well:
45//
46// Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
47// is placed at the appropriate position. Even though `if` is easy to type, you
48// still want to complete it, to get ` { }` for free! `return` is inserted with a
49// space or `;` depending on the return type of the function.
50//
51// When completing a function call, `()` are automatically inserted. If a function
52// takes arguments, the cursor is positioned inside the parenthesis.
53//
54// There are postfix completions, which can be triggered by typing something like
55// `foo().if`. The word after `.` determines postfix completion. Possible variants are:
56//
57// - `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
58// - `expr.match` -> `match expr {}`
59// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
60// - `expr.ref` -> `&expr`
61// - `expr.refm` -> `&mut expr`
62// - `expr.not` -> `!expr`
63// - `expr.dbg` -> `dbg!(expr)`
64// - `expr.dbgr` -> `dbg!(&expr)`
65// - `expr.call` -> `(expr)`
66//
67// There also snippet completions:
68//
69// .Expressions
70// - `pd` -> `eprintln!(" = {:?}", );`
71// - `ppd` -> `eprintln!(" = {:#?}", );`
72//
73// .Items
74// - `tfn` -> `#[test] fn feature(){}`
75// - `tmod` ->
76// ```rust
77// #[cfg(test)]
78// mod tests {
79// use super::*;
80//
81// #[test]
82// fn test_name() {}
83// }
84// ```
85
86/// Main entry point for completion. We run completion as a two-phase process.
87///
88/// First, we look at the position and collect a so-called `CompletionContext.
89/// This is a somewhat messy process, because, during completion, syntax tree is
90/// incomplete and can look really weird.
91///
92/// Once the context is collected, we run a series of completion routines which
93/// look at the context and produce completion items. One subtlety about this
94/// phase is that completion engine should not filter by the substring which is
95/// already present, it should give all possible variants for the identifier at
96/// the caret. In other words, for
97///
98/// ```no_run
99/// fn f() {
100/// let foo = 92;
101/// let _ = bar<|>
102/// }
103/// ```
104///
105/// `foo` *should* be present among the completion variants. Filtering by
106/// identifier prefix/fuzzy match should be done higher in the stack, together
107/// with ordering of completions (currently this is done by the client).
108pub(crate) fn completions(
109 db: &RootDatabase,
110 config: &CompletionConfig,
111 position: FilePosition,
112) -> Option<Completions> {
113 let ctx = CompletionContext::new(db, position, config)?;
114
115 if ctx.no_completion_required() {
116 // No work required here.
117 return None;
118 }
119
120 let mut acc = Completions::default();
121 complete_attribute::complete_attribute(&mut acc, &ctx);
122 complete_fn_param::complete_fn_param(&mut acc, &ctx);
123 complete_keyword::complete_expr_keyword(&mut acc, &ctx);
124 complete_keyword::complete_use_tree_keyword(&mut acc, &ctx);
125 complete_snippet::complete_expr_snippet(&mut acc, &ctx);
126 complete_snippet::complete_item_snippet(&mut acc, &ctx);
127 complete_qualified_path::complete_qualified_path(&mut acc, &ctx);
128 complete_unqualified_path::complete_unqualified_path(&mut acc, &ctx);
129 complete_dot::complete_dot(&mut acc, &ctx);
130 complete_record::complete_record(&mut acc, &ctx);
131 complete_pattern::complete_pattern(&mut acc, &ctx);
132 complete_postfix::complete_postfix(&mut acc, &ctx);
133 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
134 complete_trait_impl::complete_trait_impl(&mut acc, &ctx);
135 complete_mod::complete_mod(&mut acc, &ctx);
136
137 Some(acc)
138}
139
140#[cfg(test)]
141mod tests {
142 use crate::completion::completion_config::CompletionConfig;
143 use crate::fixture;
144
145 struct DetailAndDocumentation<'a> {
146 detail: &'a str,
147 documentation: &'a str,
148 }
149
150 fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
151 let (analysis, position) = fixture::position(ra_fixture);
152 let config = CompletionConfig::default();
153 let completions = analysis.completions(&config, position).unwrap().unwrap();
154 for item in completions {
155 if item.detail() == Some(expected.detail) {
156 let opt = item.documentation();
157 let doc = opt.as_ref().map(|it| it.as_str());
158 assert_eq!(doc, Some(expected.documentation));
159 return;
160 }
161 }
162 panic!("completion detail not found: {}", expected.detail)
163 }
164
165 fn check_no_completion(ra_fixture: &str) {
166 let (analysis, position) = fixture::position(ra_fixture);
167 let config = CompletionConfig::default();
168 analysis.completions(&config, position).unwrap();
169
170 let completions: Option<Vec<String>> = analysis
171 .completions(&config, position)
172 .unwrap()
173 .and_then(|completions| if completions.is_empty() { None } else { Some(completions) })
174 .map(|completions| {
175 completions.into_iter().map(|completion| format!("{:?}", completion)).collect()
176 });
177
178 // `assert_eq` instead of `assert!(completions.is_none())` to get the list of completions if test will panic.
179 assert_eq!(completions, None, "Completions were generated, but weren't expected");
180 }
181
182 #[test]
183 fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
184 check_detail_and_documentation(
185 r#"
186 //- /lib.rs
187 macro_rules! bar {
188 () => {
189 struct Bar;
190 impl Bar {
191 #[doc = "Do the foo"]
192 fn foo(&self) {}
193 }
194 }
195 }
196
197 bar!();
198
199 fn foo() {
200 let bar = Bar;
201 bar.fo<|>;
202 }
203 "#,
204 DetailAndDocumentation { detail: "fn foo(&self)", documentation: "Do the foo" },
205 );
206 }
207
208 #[test]
209 fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
210 check_detail_and_documentation(
211 r#"
212 //- /lib.rs
213 macro_rules! bar {
214 () => {
215 struct Bar;
216 impl Bar {
217 /// Do the foo
218 fn foo(&self) {}
219 }
220 }
221 }
222
223 bar!();
224
225 fn foo() {
226 let bar = Bar;
227 bar.fo<|>;
228 }
229 "#,
230 DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" },
231 );
232 }
233
234 #[test]
235 fn test_no_completions_required() {
236 // There must be no hint for 'in' keyword.
237 check_no_completion(
238 r#"
239 fn foo() {
240 for i i<|>
241 }
242 "#,
243 );
244 // After 'in' keyword hints may be spawned.
245 check_detail_and_documentation(
246 r#"
247 /// Do the foo
248 fn foo() -> &'static str { "foo" }
249
250 fn bar() {
251 for c in fo<|>
252 }
253 "#,
254 DetailAndDocumentation {
255 detail: "fn foo() -> &'static str",
256 documentation: "Do the foo",
257 },
258 );
259 }
260}
diff --git a/crates/ide/src/completion/complete_attribute.rs b/crates/ide/src/completion/complete_attribute.rs
deleted file mode 100644
index f4a9864d1..000000000
--- a/crates/ide/src/completion/complete_attribute.rs
+++ /dev/null
@@ -1,657 +0,0 @@
1//! Completion for attributes
2//!
3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes.
5
6use rustc_hash::FxHashSet;
7use syntax::{ast, AstNode, SyntaxKind};
8
9use crate::completion::{
10 completion_context::CompletionContext,
11 completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions},
12 generated_features::FEATURES,
13};
14
15pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
16 if ctx.mod_declaration_under_caret.is_some() {
17 return None;
18 }
19
20 let attribute = ctx.attribute_under_caret.as_ref()?;
21 match (attribute.path(), attribute.token_tree()) {
22 (Some(path), Some(token_tree)) if path.to_string() == "derive" => {
23 complete_derive(acc, ctx, token_tree)
24 }
25 (Some(path), Some(token_tree)) if path.to_string() == "feature" => {
26 complete_lint(acc, ctx, token_tree, FEATURES)
27 }
28 (Some(path), Some(token_tree))
29 if ["allow", "warn", "deny", "forbid"]
30 .iter()
31 .any(|lint_level| lint_level == &path.to_string()) =>
32 {
33 complete_lint(acc, ctx, token_tree, DEFAULT_LINT_COMPLETIONS)
34 }
35 (_, Some(_token_tree)) => {}
36 _ => complete_attribute_start(acc, ctx, attribute),
37 }
38 Some(())
39}
40
41fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
42 for attr_completion in ATTRIBUTES {
43 let mut item = CompletionItem::new(
44 CompletionKind::Attribute,
45 ctx.source_range(),
46 attr_completion.label,
47 )
48 .kind(CompletionItemKind::Attribute);
49
50 if let Some(lookup) = attr_completion.lookup {
51 item = item.lookup_by(lookup);
52 }
53
54 match (attr_completion.snippet, ctx.config.snippet_cap) {
55 (Some(snippet), Some(cap)) => {
56 item = item.insert_snippet(cap, snippet);
57 }
58 _ => {}
59 }
60
61 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner {
62 acc.add(item);
63 }
64 }
65}
66
67struct AttrCompletion {
68 label: &'static str,
69 lookup: Option<&'static str>,
70 snippet: Option<&'static str>,
71 prefer_inner: bool,
72}
73
74impl AttrCompletion {
75 const fn prefer_inner(self) -> AttrCompletion {
76 AttrCompletion { prefer_inner: true, ..self }
77 }
78}
79
80const fn attr(
81 label: &'static str,
82 lookup: Option<&'static str>,
83 snippet: Option<&'static str>,
84) -> AttrCompletion {
85 AttrCompletion { label, lookup, snippet, prefer_inner: false }
86}
87
88const ATTRIBUTES: &[AttrCompletion] = &[
89 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
90 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
91 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
92 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
93 attr(r#"deprecated = "…""#, Some("deprecated"), Some(r#"deprecated = "${0:reason}""#)),
94 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
95 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
96 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
97 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
98 // FIXME: resolve through macro resolution?
99 attr("global_allocator", None, None).prefer_inner(),
100 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
101 attr("inline(…)", Some("inline"), Some("inline(${0:lint})")),
102 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
103 attr("link", None, None),
104 attr("macro_export", None, None),
105 attr("macro_use", None, None),
106 attr(r#"must_use = "…""#, Some("must_use"), Some(r#"must_use = "${0:reason}""#)),
107 attr("no_mangle", None, None),
108 attr("no_std", None, None).prefer_inner(),
109 attr("non_exhaustive", None, None),
110 attr("panic_handler", None, None).prefer_inner(),
111 attr("path = \"…\"", Some("path"), Some("path =\"${0:path}\"")),
112 attr("proc_macro", None, None),
113 attr("proc_macro_attribute", None, None),
114 attr("proc_macro_derive(…)", Some("proc_macro_derive"), Some("proc_macro_derive(${0:Trait})")),
115 attr("recursion_limit = …", Some("recursion_limit"), Some("recursion_limit = ${0:128}"))
116 .prefer_inner(),
117 attr("repr(…)", Some("repr"), Some("repr(${0:C})")),
118 attr(
119 "should_panic(…)",
120 Some("should_panic"),
121 Some(r#"should_panic(expected = "${0:reason}")"#),
122 ),
123 attr(
124 r#"target_feature = "…""#,
125 Some("target_feature"),
126 Some("target_feature = \"${0:feature}\""),
127 ),
128 attr("test", None, None),
129 attr("used", None, None),
130 attr("warn(…)", Some("warn"), Some("warn(${0:lint})")),
131 attr(
132 r#"windows_subsystem = "…""#,
133 Some("windows_subsystem"),
134 Some(r#"windows_subsystem = "${0:subsystem}""#),
135 )
136 .prefer_inner(),
137];
138
139fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
140 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) {
141 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
142 .into_iter()
143 .filter(|completion| !existing_derives.contains(completion.label))
144 {
145 let mut label = derive_completion.label.to_owned();
146 for dependency in derive_completion
147 .dependencies
148 .into_iter()
149 .filter(|&&dependency| !existing_derives.contains(dependency))
150 {
151 label.push_str(", ");
152 label.push_str(dependency);
153 }
154 acc.add(
155 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label)
156 .kind(CompletionItemKind::Attribute),
157 );
158 }
159
160 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
161 acc.add(
162 CompletionItem::new(
163 CompletionKind::Attribute,
164 ctx.source_range(),
165 custom_derive_name,
166 )
167 .kind(CompletionItemKind::Attribute),
168 );
169 }
170 }
171}
172
173fn complete_lint(
174 acc: &mut Completions,
175 ctx: &CompletionContext,
176 derive_input: ast::TokenTree,
177 lints_completions: &[LintCompletion],
178) {
179 if let Ok(existing_lints) = parse_comma_sep_input(derive_input) {
180 for lint_completion in lints_completions
181 .into_iter()
182 .filter(|completion| !existing_lints.contains(completion.label))
183 {
184 acc.add(
185 CompletionItem::new(
186 CompletionKind::Attribute,
187 ctx.source_range(),
188 lint_completion.label,
189 )
190 .kind(CompletionItemKind::Attribute)
191 .detail(lint_completion.description),
192 );
193 }
194 }
195}
196
197fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
198 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
199 (Some(left_paren), Some(right_paren))
200 if left_paren.kind() == SyntaxKind::L_PAREN
201 && right_paren.kind() == SyntaxKind::R_PAREN =>
202 {
203 let mut input_derives = FxHashSet::default();
204 let mut current_derive = String::new();
205 for token in derive_input
206 .syntax()
207 .children_with_tokens()
208 .filter_map(|token| token.into_token())
209 .skip_while(|token| token != &left_paren)
210 .skip(1)
211 .take_while(|token| token != &right_paren)
212 {
213 if SyntaxKind::COMMA == token.kind() {
214 if !current_derive.is_empty() {
215 input_derives.insert(current_derive);
216 current_derive = String::new();
217 }
218 } else {
219 current_derive.push_str(token.to_string().trim());
220 }
221 }
222
223 if !current_derive.is_empty() {
224 input_derives.insert(current_derive);
225 }
226 Ok(input_derives)
227 }
228 _ => Err(()),
229 }
230}
231
232fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
233 let mut result = FxHashSet::default();
234 ctx.scope.process_all_names(&mut |name, scope_def| {
235 if let hir::ScopeDef::MacroDef(mac) = scope_def {
236 if mac.is_derive_macro() {
237 result.insert(name.to_string());
238 }
239 }
240 });
241 result
242}
243
244struct DeriveCompletion {
245 label: &'static str,
246 dependencies: &'static [&'static str],
247}
248
249/// Standard Rust derives and the information about their dependencies
250/// (the dependencies are needed so that the main derive don't break the compilation when added)
251#[rustfmt::skip]
252const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
253 DeriveCompletion { label: "Clone", dependencies: &[] },
254 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
255 DeriveCompletion { label: "Debug", dependencies: &[] },
256 DeriveCompletion { label: "Default", dependencies: &[] },
257 DeriveCompletion { label: "Hash", dependencies: &[] },
258 DeriveCompletion { label: "PartialEq", dependencies: &[] },
259 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
260 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
261 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
262];
263
264pub(super) struct LintCompletion {
265 pub(super) label: &'static str,
266 pub(super) description: &'static str,
267}
268
269#[rustfmt::skip]
270const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
271 LintCompletion { label: "absolute_paths_not_starting_with_crate", description: r#"fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name"# },
272 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
273 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
274 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
275 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
276 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
277 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
278 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
279 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
280 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
281 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
282 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
283 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
284 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
285 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
286 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
287 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
288 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
289 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
290 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
291 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
292 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
293 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
294 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
295 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
296 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
297 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
298 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
299 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
300 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
301 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
302 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
303 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
304 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
305 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
306 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
307 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
308 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
309 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
310 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
311 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
312 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
313 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
314 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
315 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
316 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
317 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
318 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
319 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
320 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
321 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
322 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
323 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
324 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
325 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
326 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
327 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
328 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
329 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
330 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
331 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
332 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
333 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
334 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
335 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
336 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
337 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
338 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
339 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
340 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
341 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
342 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
343 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
344 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
345 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
346 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
347 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
348 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
349 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
350 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
351 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
352 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
353 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
354 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
355 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
356 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
357 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
358 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
359 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
360 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
361 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
362 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
363 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
364 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
365 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
366 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
367 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
368 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
369 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
370 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
371 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
372 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
373 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
374 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
375 LintCompletion { label: "macro_expanded_macro_exports_accessed_by_absolute_paths", description: r#"macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths"# },
376 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
377 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
378 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
379 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
380 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
381 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
382 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
383 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
384 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
385 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
386];
387
388#[cfg(test)]
389mod tests {
390 use expect_test::{expect, Expect};
391
392 use crate::completion::{test_utils::completion_list, CompletionKind};
393
394 fn check(ra_fixture: &str, expect: Expect) {
395 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
396 expect.assert_eq(&actual);
397 }
398
399 #[test]
400 fn empty_derive_completion() {
401 check(
402 r#"
403#[derive(<|>)]
404struct Test {}
405 "#,
406 expect![[r#"
407 at Clone
408 at Copy, Clone
409 at Debug
410 at Default
411 at Eq, PartialEq
412 at Hash
413 at Ord, PartialOrd, Eq, PartialEq
414 at PartialEq
415 at PartialOrd, PartialEq
416 "#]],
417 );
418 }
419
420 #[test]
421 fn empty_lint_completion() {
422 check(
423 r#"#[allow(<|>)]"#,
424 expect![[r#"
425 at absolute_paths_not_starting_with_crate fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name
426 at ambiguous_associated_items ambiguous associated items
427 at anonymous_parameters detects anonymous parameters
428 at arithmetic_overflow arithmetic operation overflows
429 at array_into_iter detects calling `into_iter` on arrays
430 at asm_sub_register using only a subset of a register for inline asm inputs
431 at bare_trait_objects suggest using `dyn Trait` for trait objects
432 at bindings_with_variant_name detects pattern bindings with the same name as one of the matched variants
433 at box_pointers use of owned (Box type) heap memory
434 at cenum_impl_drop_cast a C-like enum implementing Drop is cast
435 at clashing_extern_declarations detects when an extern fn has been declared with the same name but different types
436 at coherence_leak_check distinct impls distinguished only by the leak-check code
437 at conflicting_repr_hints conflicts between `#[repr(..)]` hints that were previously accepted and used in practice
438 at confusable_idents detects visually confusable pairs between identifiers
439 at const_err constant evaluation detected erroneous expression
440 at dead_code detect unused, unexported items
441 at deprecated detects use of deprecated items
442 at deprecated_in_future detects use of items that will be deprecated in a future version
443 at elided_lifetimes_in_paths hidden lifetime parameters in types are deprecated
444 at ellipsis_inclusive_range_patterns `...` range patterns are deprecated
445 at explicit_outlives_requirements outlives requirements can be inferred
446 at exported_private_dependencies public interface leaks type from a private dependency
447 at ill_formed_attribute_input ill-formed attribute inputs that were previously accepted and used in practice
448 at illegal_floating_point_literal_pattern floating-point literals cannot be used in patterns
449 at improper_ctypes proper use of libc types in foreign modules
450 at improper_ctypes_definitions proper use of libc types in foreign item definitions
451 at incomplete_features incomplete features that may function improperly in some or all cases
452 at incomplete_include trailing content in included file
453 at indirect_structural_match pattern with const indirectly referencing non-structural-match type
454 at inline_no_sanitize detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`
455 at intra_doc_link_resolution_failure failures in resolving intra-doc link targets
456 at invalid_codeblock_attributes codeblock attribute looks a lot like a known one
457 at invalid_type_param_default type parameter default erroneously allowed in invalid location
458 at invalid_value an invalid value is being created (such as a NULL reference)
459 at irrefutable_let_patterns detects irrefutable patterns in if-let and while-let statements
460 at keyword_idents detects edition keywords being used as an identifier
461 at late_bound_lifetime_arguments detects generic lifetime arguments in path segments with late bound lifetime parameters
462 at macro_expanded_macro_exports_accessed_by_absolute_paths macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths
463 at macro_use_extern_crate the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system
464 at meta_variable_misuse possible meta-variable misuse at macro definition
465 at missing_copy_implementations detects potentially-forgotten implementations of `Copy`
466 at missing_crate_level_docs detects crates with no crate-level documentation
467 at missing_debug_implementations detects missing implementations of Debug
468 at missing_doc_code_examples detects publicly-exported items without code samples in their documentation
469 at missing_docs detects missing documentation for public members
470 at missing_fragment_specifier detects missing fragment specifiers in unused `macro_rules!` patterns
471 at mixed_script_confusables detects Unicode scripts whose mixed script confusables codepoints are solely used
472 at mutable_borrow_reservation_conflict reservation of a two-phased borrow conflicts with other shared borrows
473 at mutable_transmutes mutating transmuted &mut T from &T may cause undefined behavior
474 at no_mangle_const_items const items will not have their symbols exported
475 at no_mangle_generic_items generic items must be mangled
476 at non_ascii_idents detects non-ASCII identifiers
477 at non_camel_case_types types, variants, traits and type parameters should have camel case names
478 at non_shorthand_field_patterns using `Struct { x: x }` instead of `Struct { x }` in a pattern
479 at non_snake_case variables, methods, functions, lifetime parameters and modules should have snake case names
480 at non_upper_case_globals static constants should have uppercase identifiers
481 at order_dependent_trait_objects trait-object types were treated as different depending on marker-trait order
482 at overflowing_literals literal out of range for its type
483 at overlapping_patterns detects overlapping patterns
484 at path_statements path statements with no effect
485 at patterns_in_fns_without_body patterns in functions without body were erroneously allowed
486 at private_doc_tests detects code samples in docs of private items not documented by rustdoc
487 at private_in_public detect private items in public interfaces not caught by the old implementation
488 at proc_macro_derive_resolution_fallback detects proc macro derives using inaccessible names from parent modules
489 at pub_use_of_private_extern_crate detect public re-exports of private extern crates
490 at redundant_semicolons detects unnecessary trailing semicolons
491 at renamed_and_removed_lints lints that have been renamed or removed
492 at safe_packed_borrows safe borrows of fields of packed structs were erroneously allowed
493 at single_use_lifetimes detects lifetime parameters that are only used once
494 at soft_unstable a feature gate that doesn't break dependent crates
495 at stable_features stable features found in `#[feature]` directive
496 at trivial_bounds these bounds don't depend on an type parameters
497 at trivial_casts detects trivial casts which could be removed
498 at trivial_numeric_casts detects trivial casts of numeric types which could be removed
499 at type_alias_bounds bounds in type aliases are not enforced
500 at tyvar_behind_raw_pointer raw pointer to an inference variable
501 at unaligned_references detects unaligned references to fields of packed structs
502 at uncommon_codepoints detects uncommon Unicode codepoints in identifiers
503 at unconditional_panic operation will cause a panic at runtime
504 at unconditional_recursion functions that cannot return without calling themselves
505 at unknown_crate_types unknown crate type found in `#[crate_type]` directive
506 at unknown_lints unrecognized lint attribute
507 at unnameable_test_items detects an item that cannot be named being marked as `#[test_case]`
508 at unreachable_code detects unreachable code paths
509 at unreachable_patterns detects unreachable patterns
510 at unreachable_pub `pub` items not reachable from crate root
511 at unsafe_code usage of `unsafe` code
512 at unsafe_op_in_unsafe_fn unsafe operations in unsafe functions without an explicit unsafe block are deprecated
513 at unstable_features enabling unstable features (deprecated. do not use)
514 at unstable_name_collisions detects name collision with an existing but unstable method
515 at unused_allocation detects unnecessary allocations that can be eliminated
516 at unused_assignments detect assignments that will never be read
517 at unused_attributes detects attributes that were not used by the compiler
518 at unused_braces unnecessary braces around an expression
519 at unused_comparisons comparisons made useless by limits of the types involved
520 at unused_crate_dependencies crate dependencies that are never used
521 at unused_doc_comments detects doc comments that aren't used by rustdoc
522 at unused_extern_crates extern crates that are never used
523 at unused_features unused features found in crate-level `#[feature]` directives
524 at unused_import_braces unnecessary braces around an imported item
525 at unused_imports imports that are never used
526 at unused_labels detects labels that are never used
527 at unused_lifetimes detects lifetime parameters that are never used
528 at unused_macros detects macros that were not used
529 at unused_must_use unused result of a type flagged as `#[must_use]`
530 at unused_mut detect mut variables which don't need to be mutable
531 at unused_parens `if`, `match`, `while` and `return` do not need parentheses
532 at unused_qualifications detects unnecessarily qualified names
533 at unused_results unused result of an expression in a statement
534 at unused_unsafe unnecessary use of an `unsafe` block
535 at unused_variables detect variables which are not used in any way
536 at variant_size_differences detects enums with widely varying variant sizes
537 at warnings mass-change the level for lints which produce warnings
538 at where_clauses_object_safety checks the object safety of where clauses
539 at while_true suggest using `loop { }` instead of `while true { }`
540 "#]],
541 )
542 }
543
544 #[test]
545 fn no_completion_for_incorrect_derive() {
546 check(
547 r#"
548#[derive{<|>)]
549struct Test {}
550"#,
551 expect![[r#""#]],
552 )
553 }
554
555 #[test]
556 fn derive_with_input_completion() {
557 check(
558 r#"
559#[derive(serde::Serialize, PartialEq, <|>)]
560struct Test {}
561"#,
562 expect![[r#"
563 at Clone
564 at Copy, Clone
565 at Debug
566 at Default
567 at Eq
568 at Hash
569 at Ord, PartialOrd, Eq
570 at PartialOrd
571 "#]],
572 )
573 }
574
575 #[test]
576 fn test_attribute_completion() {
577 check(
578 r#"#[<|>]"#,
579 expect![[r#"
580 at allow(…)
581 at cfg(…)
582 at cfg_attr(…)
583 at deny(…)
584 at deprecated = "…"
585 at derive(…)
586 at doc = "…"
587 at forbid(…)
588 at ignore = "…"
589 at inline(…)
590 at link
591 at link_name = "…"
592 at macro_export
593 at macro_use
594 at must_use = "…"
595 at no_mangle
596 at non_exhaustive
597 at path = "…"
598 at proc_macro
599 at proc_macro_attribute
600 at proc_macro_derive(…)
601 at repr(…)
602 at should_panic(…)
603 at target_feature = "…"
604 at test
605 at used
606 at warn(…)
607 "#]],
608 )
609 }
610
611 #[test]
612 fn test_attribute_completion_inside_nested_attr() {
613 check(r#"#[cfg(<|>)]"#, expect![[]])
614 }
615
616 #[test]
617 fn test_inner_attribute_completion() {
618 check(
619 r"#![<|>]",
620 expect![[r#"
621 at allow(…)
622 at cfg(…)
623 at cfg_attr(…)
624 at deny(…)
625 at deprecated = "…"
626 at derive(…)
627 at doc = "…"
628 at feature(…)
629 at forbid(…)
630 at global_allocator
631 at ignore = "…"
632 at inline(…)
633 at link
634 at link_name = "…"
635 at macro_export
636 at macro_use
637 at must_use = "…"
638 at no_mangle
639 at no_std
640 at non_exhaustive
641 at panic_handler
642 at path = "…"
643 at proc_macro
644 at proc_macro_attribute
645 at proc_macro_derive(…)
646 at recursion_limit = …
647 at repr(…)
648 at should_panic(…)
649 at target_feature = "…"
650 at test
651 at used
652 at warn(…)
653 at windows_subsystem = "…"
654 "#]],
655 );
656 }
657}
diff --git a/crates/ide/src/completion/complete_dot.rs b/crates/ide/src/completion/complete_dot.rs
deleted file mode 100644
index f0f9a7f1d..000000000
--- a/crates/ide/src/completion/complete_dot.rs
+++ /dev/null
@@ -1,431 +0,0 @@
1//! Completes references after dot (fields and method calls).
2
3use hir::{HasVisibility, Type};
4use rustc_hash::FxHashSet;
5use test_utils::mark;
6
7use crate::completion::{completion_context::CompletionContext, completion_item::Completions};
8
9/// Complete dot accesses, i.e. fields or methods.
10pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
11 let dot_receiver = match &ctx.dot_receiver {
12 Some(expr) => expr,
13 _ => return,
14 };
15
16 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
17 Some(ty) => ty,
18 _ => return,
19 };
20
21 if ctx.is_call {
22 mark::hit!(test_no_struct_field_completion_for_method_call);
23 } else {
24 complete_fields(acc, ctx, &receiver_ty);
25 }
26 complete_methods(acc, ctx, &receiver_ty);
27}
28
29fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
30 for receiver in receiver.autoderef(ctx.db) {
31 for (field, ty) in receiver.fields(ctx.db) {
32 if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
33 // Skip private field. FIXME: If the definition location of the
34 // field is editable, we should show the completion
35 continue;
36 }
37 acc.add_field(ctx, field, &ty);
38 }
39 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
40 // FIXME: Handle visibility
41 acc.add_tuple_field(ctx, i, &ty);
42 }
43 }
44}
45
46fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
47 if let Some(krate) = ctx.krate {
48 let mut seen_methods = FxHashSet::default();
49 let traits_in_scope = ctx.scope.traits_in_scope();
50 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
51 if func.self_param(ctx.db).is_some()
52 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
53 && seen_methods.insert(func.name(ctx.db))
54 {
55 acc.add_function(ctx, func, None);
56 }
57 None::<()>
58 });
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use expect_test::{expect, Expect};
65 use test_utils::mark;
66
67 use crate::completion::{test_utils::completion_list, CompletionKind};
68
69 fn check(ra_fixture: &str, expect: Expect) {
70 let actual = completion_list(ra_fixture, CompletionKind::Reference);
71 expect.assert_eq(&actual);
72 }
73
74 #[test]
75 fn test_struct_field_and_method_completion() {
76 check(
77 r#"
78struct S { foo: u32 }
79impl S {
80 fn bar(&self) {}
81}
82fn foo(s: S) { s.<|> }
83"#,
84 expect![[r#"
85 me bar() fn bar(&self)
86 fd foo u32
87 "#]],
88 );
89 }
90
91 #[test]
92 fn test_struct_field_completion_self() {
93 check(
94 r#"
95struct S { the_field: (u32,) }
96impl S {
97 fn foo(self) { self.<|> }
98}
99"#,
100 expect![[r#"
101 me foo() fn foo(self)
102 fd the_field (u32,)
103 "#]],
104 )
105 }
106
107 #[test]
108 fn test_struct_field_completion_autoderef() {
109 check(
110 r#"
111struct A { the_field: (u32, i32) }
112impl A {
113 fn foo(&self) { self.<|> }
114}
115"#,
116 expect![[r#"
117 me foo() fn foo(&self)
118 fd the_field (u32, i32)
119 "#]],
120 )
121 }
122
123 #[test]
124 fn test_no_struct_field_completion_for_method_call() {
125 mark::check!(test_no_struct_field_completion_for_method_call);
126 check(
127 r#"
128struct A { the_field: u32 }
129fn foo(a: A) { a.<|>() }
130"#,
131 expect![[""]],
132 );
133 }
134
135 #[test]
136 fn test_visibility_filtering() {
137 check(
138 r#"
139mod inner {
140 pub struct A {
141 private_field: u32,
142 pub pub_field: u32,
143 pub(crate) crate_field: u32,
144 pub(super) super_field: u32,
145 }
146}
147fn foo(a: inner::A) { a.<|> }
148"#,
149 expect![[r#"
150 fd crate_field u32
151 fd pub_field u32
152 fd super_field u32
153 "#]],
154 );
155
156 check(
157 r#"
158struct A {}
159mod m {
160 impl super::A {
161 fn private_method(&self) {}
162 pub(super) fn the_method(&self) {}
163 }
164}
165fn foo(a: A) { a.<|> }
166"#,
167 expect![[r#"
168 me the_method() pub(super) fn the_method(&self)
169 "#]],
170 );
171 }
172
173 #[test]
174 fn test_union_field_completion() {
175 check(
176 r#"
177union U { field: u8, other: u16 }
178fn foo(u: U) { u.<|> }
179"#,
180 expect![[r#"
181 fd field u8
182 fd other u16
183 "#]],
184 );
185 }
186
187 #[test]
188 fn test_method_completion_only_fitting_impls() {
189 check(
190 r#"
191struct A<T> {}
192impl A<u32> {
193 fn the_method(&self) {}
194}
195impl A<i32> {
196 fn the_other_method(&self) {}
197}
198fn foo(a: A<u32>) { a.<|> }
199"#,
200 expect![[r#"
201 me the_method() fn the_method(&self)
202 "#]],
203 )
204 }
205
206 #[test]
207 fn test_trait_method_completion() {
208 check(
209 r#"
210struct A {}
211trait Trait { fn the_method(&self); }
212impl Trait for A {}
213fn foo(a: A) { a.<|> }
214"#,
215 expect![[r#"
216 me the_method() fn the_method(&self)
217 "#]],
218 );
219 }
220
221 #[test]
222 fn test_trait_method_completion_deduplicated() {
223 check(
224 r"
225struct A {}
226trait Trait { fn the_method(&self); }
227impl<T> Trait for T {}
228fn foo(a: &A) { a.<|> }
229",
230 expect![[r#"
231 me the_method() fn the_method(&self)
232 "#]],
233 );
234 }
235
236 #[test]
237 fn completes_trait_method_from_other_module() {
238 check(
239 r"
240struct A {}
241mod m {
242 pub trait Trait { fn the_method(&self); }
243}
244use m::Trait;
245impl Trait for A {}
246fn foo(a: A) { a.<|> }
247",
248 expect![[r#"
249 me the_method() fn the_method(&self)
250 "#]],
251 );
252 }
253
254 #[test]
255 fn test_no_non_self_method() {
256 check(
257 r#"
258struct A {}
259impl A {
260 fn the_method() {}
261}
262fn foo(a: A) {
263 a.<|>
264}
265"#,
266 expect![[""]],
267 );
268 }
269
270 #[test]
271 fn test_tuple_field_completion() {
272 check(
273 r#"
274fn foo() {
275 let b = (0, 3.14);
276 b.<|>
277}
278"#,
279 expect![[r#"
280 fd 0 i32
281 fd 1 f64
282 "#]],
283 )
284 }
285
286 #[test]
287 fn test_tuple_field_inference() {
288 check(
289 r#"
290pub struct S;
291impl S { pub fn blah(&self) {} }
292
293struct T(S);
294
295impl T {
296 fn foo(&self) {
297 // FIXME: This doesn't work without the trailing `a` as `0.` is a float
298 self.0.a<|>
299 }
300}
301"#,
302 expect![[r#"
303 me blah() pub fn blah(&self)
304 "#]],
305 );
306 }
307
308 #[test]
309 fn test_completion_works_in_consts() {
310 check(
311 r#"
312struct A { the_field: u32 }
313const X: u32 = {
314 A { the_field: 92 }.<|>
315};
316"#,
317 expect![[r#"
318 fd the_field u32
319 "#]],
320 );
321 }
322
323 #[test]
324 fn works_in_simple_macro_1() {
325 check(
326 r#"
327macro_rules! m { ($e:expr) => { $e } }
328struct A { the_field: u32 }
329fn foo(a: A) {
330 m!(a.x<|>)
331}
332"#,
333 expect![[r#"
334 fd the_field u32
335 "#]],
336 );
337 }
338
339 #[test]
340 fn works_in_simple_macro_2() {
341 // this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery
342 check(
343 r#"
344macro_rules! m { ($e:expr) => { $e } }
345struct A { the_field: u32 }
346fn foo(a: A) {
347 m!(a.<|>)
348}
349"#,
350 expect![[r#"
351 fd the_field u32
352 "#]],
353 );
354 }
355
356 #[test]
357 fn works_in_simple_macro_recursive_1() {
358 check(
359 r#"
360macro_rules! m { ($e:expr) => { $e } }
361struct A { the_field: u32 }
362fn foo(a: A) {
363 m!(m!(m!(a.x<|>)))
364}
365"#,
366 expect![[r#"
367 fd the_field u32
368 "#]],
369 );
370 }
371
372 #[test]
373 fn macro_expansion_resilient() {
374 check(
375 r#"
376macro_rules! dbg {
377 () => {};
378 ($val:expr) => {
379 match $val { tmp => { tmp } }
380 };
381 // Trailing comma with single argument is ignored
382 ($val:expr,) => { $crate::dbg!($val) };
383 ($($val:expr),+ $(,)?) => {
384 ($($crate::dbg!($val)),+,)
385 };
386}
387struct A { the_field: u32 }
388fn foo(a: A) {
389 dbg!(a.<|>)
390}
391"#,
392 expect![[r#"
393 fd the_field u32
394 "#]],
395 );
396 }
397
398 #[test]
399 fn test_method_completion_issue_3547() {
400 check(
401 r#"
402struct HashSet<T> {}
403impl<T> HashSet<T> {
404 pub fn the_method(&self) {}
405}
406fn foo() {
407 let s: HashSet<_>;
408 s.<|>
409}
410"#,
411 expect![[r#"
412 me the_method() pub fn the_method(&self)
413 "#]],
414 );
415 }
416
417 #[test]
418 fn completes_method_call_when_receiver_is_a_macro_call() {
419 check(
420 r#"
421struct S;
422impl S { fn foo(&self) {} }
423macro_rules! make_s { () => { S }; }
424fn main() { make_s!().f<|>; }
425"#,
426 expect![[r#"
427 me foo() fn foo(&self)
428 "#]],
429 )
430 }
431}
diff --git a/crates/ide/src/completion/complete_fn_param.rs b/crates/ide/src/completion/complete_fn_param.rs
deleted file mode 100644
index 9efe25461..000000000
--- a/crates/ide/src/completion/complete_fn_param.rs
+++ /dev/null
@@ -1,135 +0,0 @@
1//! See `complete_fn_param`.
2
3use rustc_hash::FxHashMap;
4use syntax::{
5 ast::{self, ModuleItemOwner},
6 match_ast, AstNode,
7};
8
9use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions};
10
11/// Complete repeated parameters, both name and type. For example, if all
12/// functions in a file have a `spam: &mut Spam` parameter, a completion with
13/// `spam: &mut Spam` insert text/label and `spam` lookup string will be
14/// suggested.
15pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) {
16 if !ctx.is_param {
17 return;
18 }
19
20 let mut params = FxHashMap::default();
21
22 let me = ctx.token.ancestors().find_map(ast::Fn::cast);
23 let mut process_fn = |func: ast::Fn| {
24 if Some(&func) == me.as_ref() {
25 return;
26 }
27 func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
28 let text = param.syntax().text().to_string();
29 params.entry(text).or_insert(param);
30 })
31 };
32
33 for node in ctx.token.parent().ancestors() {
34 match_ast! {
35 match node {
36 ast::SourceFile(it) => it.items().filter_map(|item| match item {
37 ast::Item::Fn(it) => Some(it),
38 _ => None,
39 }).for_each(&mut process_fn),
40 ast::ItemList(it) => it.items().filter_map(|item| match item {
41 ast::Item::Fn(it) => Some(it),
42 _ => None,
43 }).for_each(&mut process_fn),
44 ast::AssocItemList(it) => it.assoc_items().filter_map(|item| match item {
45 ast::AssocItem::Fn(it) => Some(it),
46 _ => None,
47 }).for_each(&mut process_fn),
48 _ => continue,
49 }
50 };
51 }
52
53 params
54 .into_iter()
55 .filter_map(|(label, param)| {
56 let lookup = param.pat()?.syntax().text().to_string();
57 Some((label, lookup))
58 })
59 .for_each(|(label, lookup)| {
60 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
61 .kind(crate::CompletionItemKind::Binding)
62 .lookup_by(lookup)
63 .add_to(acc)
64 });
65}
66
67#[cfg(test)]
68mod tests {
69 use expect_test::{expect, Expect};
70
71 use crate::completion::{test_utils::completion_list, CompletionKind};
72
73 fn check(ra_fixture: &str, expect: Expect) {
74 let actual = completion_list(ra_fixture, CompletionKind::Magic);
75 expect.assert_eq(&actual);
76 }
77
78 #[test]
79 fn test_param_completion_last_param() {
80 check(
81 r#"
82fn foo(file_id: FileId) {}
83fn bar(file_id: FileId) {}
84fn baz(file<|>) {}
85"#,
86 expect![[r#"
87 bn file_id: FileId
88 "#]],
89 );
90 }
91
92 #[test]
93 fn test_param_completion_nth_param() {
94 check(
95 r#"
96fn foo(file_id: FileId) {}
97fn baz(file<|>, x: i32) {}
98"#,
99 expect![[r#"
100 bn file_id: FileId
101 "#]],
102 );
103 }
104
105 #[test]
106 fn test_param_completion_trait_param() {
107 check(
108 r#"
109pub(crate) trait SourceRoot {
110 pub fn contains(&self, file_id: FileId) -> bool;
111 pub fn module_map(&self) -> &ModuleMap;
112 pub fn lines(&self, file_id: FileId) -> &LineIndex;
113 pub fn syntax(&self, file<|>)
114}
115"#,
116 expect![[r#"
117 bn file_id: FileId
118 "#]],
119 );
120 }
121
122 #[test]
123 fn completes_param_in_inner_function() {
124 check(
125 r#"
126fn outer(text: String) {
127 fn inner(<|>)
128}
129"#,
130 expect![[r#"
131 bn text: String
132 "#]],
133 )
134 }
135}
diff --git a/crates/ide/src/completion/complete_keyword.rs b/crates/ide/src/completion/complete_keyword.rs
deleted file mode 100644
index e59747095..000000000
--- a/crates/ide/src/completion/complete_keyword.rs
+++ /dev/null
@@ -1,568 +0,0 @@
1//! FIXME: write short doc here
2
3use syntax::{ast, SyntaxKind};
4use test_utils::mark;
5
6use crate::completion::{
7 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
8};
9
10pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
11 // complete keyword "crate" in use stmt
12 let source_range = ctx.source_range();
13
14 if ctx.use_item_syntax.is_some() {
15 if ctx.path_qual.is_none() {
16 CompletionItem::new(CompletionKind::Keyword, source_range, "crate::")
17 .kind(CompletionItemKind::Keyword)
18 .insert_text("crate::")
19 .add_to(acc);
20 }
21 CompletionItem::new(CompletionKind::Keyword, source_range, "self")
22 .kind(CompletionItemKind::Keyword)
23 .add_to(acc);
24 CompletionItem::new(CompletionKind::Keyword, source_range, "super::")
25 .kind(CompletionItemKind::Keyword)
26 .insert_text("super::")
27 .add_to(acc);
28 }
29
30 // Suggest .await syntax for types that implement Future trait
31 if let Some(receiver) = &ctx.dot_receiver {
32 if let Some(ty) = ctx.sema.type_of_expr(receiver) {
33 if ty.impls_future(ctx.db) {
34 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
35 .kind(CompletionItemKind::Keyword)
36 .detail("expr.await")
37 .insert_text("await")
38 .add_to(acc);
39 }
40 };
41 }
42}
43
44pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
45 if ctx.token.kind() == SyntaxKind::COMMENT {
46 mark::hit!(no_keyword_completion_in_comments);
47 return;
48 }
49
50 let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
51 if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
52 add_keyword(ctx, acc, "where", "where ");
53 return;
54 }
55 if ctx.unsafe_is_prev {
56 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
57 add_keyword(ctx, acc, "fn", "fn $0() {}")
58 }
59
60 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
61 add_keyword(ctx, acc, "trait", "trait $0 {}");
62 add_keyword(ctx, acc, "impl", "impl $0 {}");
63 }
64
65 return;
66 }
67 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
68 {
69 add_keyword(ctx, acc, "fn", "fn $0() {}");
70 }
71 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
72 add_keyword(ctx, acc, "use", "use ");
73 add_keyword(ctx, acc, "impl", "impl $0 {}");
74 add_keyword(ctx, acc, "trait", "trait $0 {}");
75 }
76
77 if ctx.has_item_list_or_source_file_parent {
78 add_keyword(ctx, acc, "enum", "enum $0 {}");
79 add_keyword(ctx, acc, "struct", "struct $0");
80 add_keyword(ctx, acc, "union", "union $0 {}");
81 }
82
83 if ctx.is_expr {
84 add_keyword(ctx, acc, "match", "match $0 {}");
85 add_keyword(ctx, acc, "while", "while $0 {}");
86 add_keyword(ctx, acc, "loop", "loop {$0}");
87 add_keyword(ctx, acc, "if", "if ");
88 add_keyword(ctx, acc, "if let", "if let ");
89 }
90
91 if ctx.if_is_prev || ctx.block_expr_parent {
92 add_keyword(ctx, acc, "let", "let ");
93 }
94
95 if ctx.after_if {
96 add_keyword(ctx, acc, "else", "else {$0}");
97 add_keyword(ctx, acc, "else if", "else if $0 {}");
98 }
99 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
100 add_keyword(ctx, acc, "mod", "mod $0 {}");
101 }
102 if ctx.bind_pat_parent || ctx.ref_pat_parent {
103 add_keyword(ctx, acc, "mut", "mut ");
104 }
105 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
106 {
107 add_keyword(ctx, acc, "const", "const ");
108 add_keyword(ctx, acc, "type", "type ");
109 }
110 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
111 add_keyword(ctx, acc, "static", "static ");
112 };
113 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
114 add_keyword(ctx, acc, "extern", "extern ");
115 }
116 if ctx.has_item_list_or_source_file_parent
117 || has_trait_or_impl_parent
118 || ctx.block_expr_parent
119 || ctx.is_match_arm
120 {
121 add_keyword(ctx, acc, "unsafe", "unsafe ");
122 }
123 if ctx.in_loop_body {
124 if ctx.can_be_stmt {
125 add_keyword(ctx, acc, "continue", "continue;");
126 add_keyword(ctx, acc, "break", "break;");
127 } else {
128 add_keyword(ctx, acc, "continue", "continue");
129 add_keyword(ctx, acc, "break", "break");
130 }
131 }
132 if ctx.has_item_list_or_source_file_parent || ctx.has_impl_parent | ctx.has_field_list_parent {
133 add_keyword(ctx, acc, "pub(crate)", "pub(crate) ");
134 add_keyword(ctx, acc, "pub", "pub ");
135 }
136
137 if !ctx.is_trivial_path {
138 return;
139 }
140 let fn_def = match &ctx.function_syntax {
141 Some(it) => it,
142 None => return,
143 };
144 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
145}
146
147fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
148 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
149 .kind(CompletionItemKind::Keyword);
150
151 match ctx.config.snippet_cap {
152 Some(cap) => res.insert_snippet(cap, snippet),
153 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
154 }
155 .build()
156}
157
158fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
159 acc.add(keyword(ctx, kw, snippet));
160}
161
162fn complete_return(
163 ctx: &CompletionContext,
164 fn_def: &ast::Fn,
165 can_be_stmt: bool,
166) -> Option<CompletionItem> {
167 let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
168 (true, true) => "return $0;",
169 (true, false) => "return;",
170 (false, true) => "return $0",
171 (false, false) => "return",
172 };
173 Some(keyword(ctx, "return", snip))
174}
175
176#[cfg(test)]
177mod tests {
178 use expect_test::{expect, Expect};
179
180 use crate::completion::{
181 test_utils::{check_edit, completion_list},
182 CompletionKind,
183 };
184 use test_utils::mark;
185
186 fn check(ra_fixture: &str, expect: Expect) {
187 let actual = completion_list(ra_fixture, CompletionKind::Keyword);
188 expect.assert_eq(&actual)
189 }
190
191 #[test]
192 fn test_keywords_in_use_stmt() {
193 check(
194 r"use <|>",
195 expect![[r#"
196 kw crate::
197 kw self
198 kw super::
199 "#]],
200 );
201
202 check(
203 r"use a::<|>",
204 expect![[r#"
205 kw self
206 kw super::
207 "#]],
208 );
209
210 check(
211 r"use a::{b, <|>}",
212 expect![[r#"
213 kw self
214 kw super::
215 "#]],
216 );
217 }
218
219 #[test]
220 fn test_keywords_at_source_file_level() {
221 check(
222 r"m<|>",
223 expect![[r#"
224 kw const
225 kw enum
226 kw extern
227 kw fn
228 kw impl
229 kw mod
230 kw pub
231 kw pub(crate)
232 kw static
233 kw struct
234 kw trait
235 kw type
236 kw union
237 kw unsafe
238 kw use
239 "#]],
240 );
241 }
242
243 #[test]
244 fn test_keywords_in_function() {
245 check(
246 r"fn quux() { <|> }",
247 expect![[r#"
248 kw const
249 kw extern
250 kw fn
251 kw if
252 kw if let
253 kw impl
254 kw let
255 kw loop
256 kw match
257 kw mod
258 kw return
259 kw static
260 kw trait
261 kw type
262 kw unsafe
263 kw use
264 kw while
265 "#]],
266 );
267 }
268
269 #[test]
270 fn test_keywords_inside_block() {
271 check(
272 r"fn quux() { if true { <|> } }",
273 expect![[r#"
274 kw const
275 kw extern
276 kw fn
277 kw if
278 kw if let
279 kw impl
280 kw let
281 kw loop
282 kw match
283 kw mod
284 kw return
285 kw static
286 kw trait
287 kw type
288 kw unsafe
289 kw use
290 kw while
291 "#]],
292 );
293 }
294
295 #[test]
296 fn test_keywords_after_if() {
297 check(
298 r#"fn quux() { if true { () } <|> }"#,
299 expect![[r#"
300 kw const
301 kw else
302 kw else if
303 kw extern
304 kw fn
305 kw if
306 kw if let
307 kw impl
308 kw let
309 kw loop
310 kw match
311 kw mod
312 kw return
313 kw static
314 kw trait
315 kw type
316 kw unsafe
317 kw use
318 kw while
319 "#]],
320 );
321 check_edit(
322 "else",
323 r#"fn quux() { if true { () } <|> }"#,
324 r#"fn quux() { if true { () } else {$0} }"#,
325 );
326 }
327
328 #[test]
329 fn test_keywords_in_match_arm() {
330 check(
331 r#"
332fn quux() -> i32 {
333 match () { () => <|> }
334}
335"#,
336 expect![[r#"
337 kw if
338 kw if let
339 kw loop
340 kw match
341 kw return
342 kw unsafe
343 kw while
344 "#]],
345 );
346 }
347
348 #[test]
349 fn test_keywords_in_trait_def() {
350 check(
351 r"trait My { <|> }",
352 expect![[r#"
353 kw const
354 kw fn
355 kw type
356 kw unsafe
357 "#]],
358 );
359 }
360
361 #[test]
362 fn test_keywords_in_impl_def() {
363 check(
364 r"impl My { <|> }",
365 expect![[r#"
366 kw const
367 kw fn
368 kw pub
369 kw pub(crate)
370 kw type
371 kw unsafe
372 "#]],
373 );
374 }
375
376 #[test]
377 fn test_keywords_in_loop() {
378 check(
379 r"fn my() { loop { <|> } }",
380 expect![[r#"
381 kw break
382 kw const
383 kw continue
384 kw extern
385 kw fn
386 kw if
387 kw if let
388 kw impl
389 kw let
390 kw loop
391 kw match
392 kw mod
393 kw return
394 kw static
395 kw trait
396 kw type
397 kw unsafe
398 kw use
399 kw while
400 "#]],
401 );
402 }
403
404 #[test]
405 fn test_keywords_after_unsafe_in_item_list() {
406 check(
407 r"unsafe <|>",
408 expect![[r#"
409 kw fn
410 kw impl
411 kw trait
412 "#]],
413 );
414 }
415
416 #[test]
417 fn test_keywords_after_unsafe_in_block_expr() {
418 check(
419 r"fn my_fn() { unsafe <|> }",
420 expect![[r#"
421 kw fn
422 kw impl
423 kw trait
424 "#]],
425 );
426 }
427
428 #[test]
429 fn test_mut_in_ref_and_in_fn_parameters_list() {
430 check(
431 r"fn my_fn(&<|>) {}",
432 expect![[r#"
433 kw mut
434 "#]],
435 );
436 check(
437 r"fn my_fn(<|>) {}",
438 expect![[r#"
439 kw mut
440 "#]],
441 );
442 check(
443 r"fn my_fn() { let &<|> }",
444 expect![[r#"
445 kw mut
446 "#]],
447 );
448 }
449
450 #[test]
451 fn test_where_keyword() {
452 check(
453 r"trait A <|>",
454 expect![[r#"
455 kw where
456 "#]],
457 );
458 check(
459 r"impl A <|>",
460 expect![[r#"
461 kw where
462 "#]],
463 );
464 }
465
466 #[test]
467 fn no_keyword_completion_in_comments() {
468 mark::check!(no_keyword_completion_in_comments);
469 check(
470 r#"
471fn test() {
472 let x = 2; // A comment<|>
473}
474"#,
475 expect![[""]],
476 );
477 check(
478 r#"
479/*
480Some multi-line comment<|>
481*/
482"#,
483 expect![[""]],
484 );
485 check(
486 r#"
487/// Some doc comment
488/// let test<|> = 1
489"#,
490 expect![[""]],
491 );
492 }
493
494 #[test]
495 fn test_completion_await_impls_future() {
496 check(
497 r#"
498//- /main.rs crate:main deps:std
499use std::future::*;
500struct A {}
501impl Future for A {}
502fn foo(a: A) { a.<|> }
503
504//- /std/lib.rs crate:std
505pub mod future {
506 #[lang = "future_trait"]
507 pub trait Future {}
508}
509"#,
510 expect![[r#"
511 kw await expr.await
512 "#]],
513 );
514
515 check(
516 r#"
517//- /main.rs crate:main deps:std
518use std::future::*;
519fn foo() {
520 let a = async {};
521 a.<|>
522}
523
524//- /std/lib.rs crate:std
525pub mod future {
526 #[lang = "future_trait"]
527 pub trait Future {
528 type Output;
529 }
530}
531"#,
532 expect![[r#"
533 kw await expr.await
534 "#]],
535 )
536 }
537
538 #[test]
539 fn after_let() {
540 check(
541 r#"fn main() { let _ = <|> }"#,
542 expect![[r#"
543 kw if
544 kw if let
545 kw loop
546 kw match
547 kw return
548 kw while
549 "#]],
550 )
551 }
552
553 #[test]
554 fn before_field() {
555 check(
556 r#"
557struct Foo {
558 <|>
559 pub f: i32,
560}
561"#,
562 expect![[r#"
563 kw pub
564 kw pub(crate)
565 "#]],
566 )
567 }
568}
diff --git a/crates/ide/src/completion/complete_macro_in_item_position.rs b/crates/ide/src/completion/complete_macro_in_item_position.rs
deleted file mode 100644
index fc8625d8e..000000000
--- a/crates/ide/src/completion/complete_macro_in_item_position.rs
+++ /dev/null
@@ -1,41 +0,0 @@
1//! FIXME: write short doc here
2
3use crate::completion::{CompletionContext, Completions};
4
5pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
6 // Show only macros in top level.
7 if ctx.is_new_item {
8 ctx.scope.process_all_names(&mut |name, res| {
9 if let hir::ScopeDef::MacroDef(mac) = res {
10 acc.add_macro(ctx, Some(name.to_string()), mac);
11 }
12 })
13 }
14}
15
16#[cfg(test)]
17mod tests {
18 use expect_test::{expect, Expect};
19
20 use crate::completion::{test_utils::completion_list, CompletionKind};
21
22 fn check(ra_fixture: &str, expect: Expect) {
23 let actual = completion_list(ra_fixture, CompletionKind::Reference);
24 expect.assert_eq(&actual)
25 }
26
27 #[test]
28 fn completes_macros_as_item() {
29 check(
30 r#"
31macro_rules! foo { () => {} }
32fn foo() {}
33
34<|>
35"#,
36 expect![[r#"
37 ma foo!(…) macro_rules! foo
38 "#]],
39 )
40 }
41}
diff --git a/crates/ide/src/completion/complete_mod.rs b/crates/ide/src/completion/complete_mod.rs
deleted file mode 100644
index c7a99bdc3..000000000
--- a/crates/ide/src/completion/complete_mod.rs
+++ /dev/null
@@ -1,324 +0,0 @@
1//! Completes mod declarations.
2
3use base_db::{SourceDatabaseExt, VfsPath};
4use hir::{Module, ModuleSource};
5use ide_db::RootDatabase;
6use rustc_hash::FxHashSet;
7
8use crate::{CompletionItem, CompletionItemKind};
9
10use super::{
11 completion_context::CompletionContext, completion_item::CompletionKind,
12 completion_item::Completions,
13};
14
15/// Complete mod declaration, i.e. `mod <|> ;`
16pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
17 let mod_under_caret = match &ctx.mod_declaration_under_caret {
18 Some(mod_under_caret) if mod_under_caret.item_list().is_some() => return None,
19 Some(mod_under_caret) => mod_under_caret,
20 None => return None,
21 };
22
23 let _p = profile::span("completion::complete_mod");
24
25 let current_module = ctx.scope.module()?;
26
27 let module_definition_file =
28 current_module.definition_source(ctx.db).file_id.original_file(ctx.db);
29 let source_root = ctx.db.source_root(ctx.db.file_source_root(module_definition_file));
30 let directory_to_look_for_submodules = directory_to_look_for_submodules(
31 current_module,
32 ctx.db,
33 source_root.path_for_file(&module_definition_file)?,
34 )?;
35
36 let existing_mod_declarations = current_module
37 .children(ctx.db)
38 .filter_map(|module| Some(module.name(ctx.db)?.to_string()))
39 .collect::<FxHashSet<_>>();
40
41 let module_declaration_file =
42 current_module.declaration_source(ctx.db).map(|module_declaration_source_file| {
43 module_declaration_source_file.file_id.original_file(ctx.db)
44 });
45
46 source_root
47 .iter()
48 .filter(|submodule_candidate_file| submodule_candidate_file != &module_definition_file)
49 .filter(|submodule_candidate_file| {
50 Some(submodule_candidate_file) != module_declaration_file.as_ref()
51 })
52 .filter_map(|submodule_file| {
53 let submodule_path = source_root.path_for_file(&submodule_file)?;
54 let directory_with_submodule = submodule_path.parent()?;
55 match submodule_path.name_and_extension()? {
56 ("lib", Some("rs")) | ("main", Some("rs")) => None,
57 ("mod", Some("rs")) => {
58 if directory_with_submodule.parent()? == directory_to_look_for_submodules {
59 match directory_with_submodule.name_and_extension()? {
60 (directory_name, None) => Some(directory_name.to_owned()),
61 _ => None,
62 }
63 } else {
64 None
65 }
66 }
67 (file_name, Some("rs"))
68 if directory_with_submodule == directory_to_look_for_submodules =>
69 {
70 Some(file_name.to_owned())
71 }
72 _ => None,
73 }
74 })
75 .filter(|name| !existing_mod_declarations.contains(name))
76 .for_each(|submodule_name| {
77 let mut label = submodule_name;
78 if mod_under_caret.semicolon_token().is_none() {
79 label.push(';')
80 }
81 acc.add(
82 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label)
83 .kind(CompletionItemKind::Module),
84 )
85 });
86
87 Some(())
88}
89
90fn directory_to_look_for_submodules(
91 module: Module,
92 db: &RootDatabase,
93 module_file_path: &VfsPath,
94) -> Option<VfsPath> {
95 let directory_with_module_path = module_file_path.parent()?;
96 let base_directory = match module_file_path.name_and_extension()? {
97 ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => {
98 Some(directory_with_module_path)
99 }
100 (regular_rust_file_name, Some("rs")) => {
101 if matches!(
102 (
103 directory_with_module_path
104 .parent()
105 .as_ref()
106 .and_then(|path| path.name_and_extension()),
107 directory_with_module_path.name_and_extension(),
108 ),
109 (Some(("src", None)), Some(("bin", None)))
110 ) {
111 // files in /src/bin/ can import each other directly
112 Some(directory_with_module_path)
113 } else {
114 directory_with_module_path.join(regular_rust_file_name)
115 }
116 }
117 _ => None,
118 }?;
119
120 let mut resulting_path = base_directory;
121 for module in module_chain_to_containing_module_file(module, db) {
122 if let Some(name) = module.name(db) {
123 resulting_path = resulting_path.join(&name.to_string())?;
124 }
125 }
126
127 Some(resulting_path)
128}
129
130fn module_chain_to_containing_module_file(
131 current_module: Module,
132 db: &RootDatabase,
133) -> Vec<Module> {
134 let mut path = Vec::new();
135
136 let mut current_module = Some(current_module);
137 while let Some(ModuleSource::Module(_)) =
138 current_module.map(|module| module.definition_source(db).value)
139 {
140 if let Some(module) = current_module {
141 path.insert(0, module);
142 current_module = module.parent(db);
143 } else {
144 current_module = None;
145 }
146 }
147
148 path
149}
150
151#[cfg(test)]
152mod tests {
153 use crate::completion::{test_utils::completion_list, CompletionKind};
154 use expect_test::{expect, Expect};
155
156 fn check(ra_fixture: &str, expect: Expect) {
157 let actual = completion_list(ra_fixture, CompletionKind::Magic);
158 expect.assert_eq(&actual);
159 }
160
161 #[test]
162 fn lib_module_completion() {
163 check(
164 r#"
165 //- /lib.rs
166 mod <|>
167 //- /foo.rs
168 fn foo() {}
169 //- /foo/ignored_foo.rs
170 fn ignored_foo() {}
171 //- /bar/mod.rs
172 fn bar() {}
173 //- /bar/ignored_bar.rs
174 fn ignored_bar() {}
175 "#,
176 expect![[r#"
177 md bar;
178 md foo;
179 "#]],
180 );
181 }
182
183 #[test]
184 fn no_module_completion_with_module_body() {
185 check(
186 r#"
187 //- /lib.rs
188 mod <|> {
189
190 }
191 //- /foo.rs
192 fn foo() {}
193 "#,
194 expect![[r#""#]],
195 );
196 }
197
198 #[test]
199 fn main_module_completion() {
200 check(
201 r#"
202 //- /main.rs
203 mod <|>
204 //- /foo.rs
205 fn foo() {}
206 //- /foo/ignored_foo.rs
207 fn ignored_foo() {}
208 //- /bar/mod.rs
209 fn bar() {}
210 //- /bar/ignored_bar.rs
211 fn ignored_bar() {}
212 "#,
213 expect![[r#"
214 md bar;
215 md foo;
216 "#]],
217 );
218 }
219
220 #[test]
221 fn main_test_module_completion() {
222 check(
223 r#"
224 //- /main.rs
225 mod tests {
226 mod <|>;
227 }
228 //- /tests/foo.rs
229 fn foo() {}
230 "#,
231 expect![[r#"
232 md foo
233 "#]],
234 );
235 }
236
237 #[test]
238 fn directly_nested_module_completion() {
239 check(
240 r#"
241 //- /lib.rs
242 mod foo;
243 //- /foo.rs
244 mod <|>;
245 //- /foo/bar.rs
246 fn bar() {}
247 //- /foo/bar/ignored_bar.rs
248 fn ignored_bar() {}
249 //- /foo/baz/mod.rs
250 fn baz() {}
251 //- /foo/moar/ignored_moar.rs
252 fn ignored_moar() {}
253 "#,
254 expect![[r#"
255 md bar
256 md baz
257 "#]],
258 );
259 }
260
261 #[test]
262 fn nested_in_source_module_completion() {
263 check(
264 r#"
265 //- /lib.rs
266 mod foo;
267 //- /foo.rs
268 mod bar {
269 mod <|>
270 }
271 //- /foo/bar/baz.rs
272 fn baz() {}
273 "#,
274 expect![[r#"
275 md baz;
276 "#]],
277 );
278 }
279
280 // FIXME binary modules are not supported in tests properly
281 // Binary modules are a bit special, they allow importing the modules from `/src/bin`
282 // and that's why are good to test two things:
283 // * no cycles are allowed in mod declarations
284 // * no modules from the parent directory are proposed
285 // Unfortunately, binary modules support is in cargo not rustc,
286 // hence the test does not work now
287 //
288 // #[test]
289 // fn regular_bin_module_completion() {
290 // check(
291 // r#"
292 // //- /src/bin.rs
293 // fn main() {}
294 // //- /src/bin/foo.rs
295 // mod <|>
296 // //- /src/bin/bar.rs
297 // fn bar() {}
298 // //- /src/bin/bar/bar_ignored.rs
299 // fn bar_ignored() {}
300 // "#,
301 // expect![[r#"
302 // md bar;
303 // "#]],foo
304 // );
305 // }
306
307 #[test]
308 fn already_declared_bin_module_completion_omitted() {
309 check(
310 r#"
311 //- /src/bin.rs crate:main
312 fn main() {}
313 //- /src/bin/foo.rs
314 mod <|>
315 //- /src/bin/bar.rs
316 mod foo;
317 fn bar() {}
318 //- /src/bin/bar/bar_ignored.rs
319 fn bar_ignored() {}
320 "#,
321 expect![[r#""#]],
322 );
323 }
324}
diff --git a/crates/ide/src/completion/complete_pattern.rs b/crates/ide/src/completion/complete_pattern.rs
deleted file mode 100644
index 5a13574d4..000000000
--- a/crates/ide/src/completion/complete_pattern.rs
+++ /dev/null
@@ -1,88 +0,0 @@
1//! FIXME: write short doc here
2
3use crate::completion::{CompletionContext, Completions};
4
5/// Completes constats and paths in patterns.
6pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
7 if !ctx.is_pat_binding_or_const {
8 return;
9 }
10 if ctx.record_pat_syntax.is_some() {
11 return;
12 }
13
14 // FIXME: ideally, we should look at the type we are matching against and
15 // suggest variants + auto-imports
16 ctx.scope.process_all_names(&mut |name, res| {
17 match &res {
18 hir::ScopeDef::ModuleDef(def) => match def {
19 hir::ModuleDef::Adt(hir::Adt::Enum(..))
20 | hir::ModuleDef::Adt(hir::Adt::Struct(..))
21 | hir::ModuleDef::EnumVariant(..)
22 | hir::ModuleDef::Const(..)
23 | hir::ModuleDef::Module(..) => (),
24 _ => return,
25 },
26 hir::ScopeDef::MacroDef(_) => (),
27 _ => return,
28 };
29
30 acc.add_resolution(ctx, name.to_string(), &res)
31 });
32}
33
34#[cfg(test)]
35mod tests {
36 use expect_test::{expect, Expect};
37
38 use crate::completion::{test_utils::completion_list, CompletionKind};
39
40 fn check(ra_fixture: &str, expect: Expect) {
41 let actual = completion_list(ra_fixture, CompletionKind::Reference);
42 expect.assert_eq(&actual)
43 }
44
45 #[test]
46 fn completes_enum_variants_and_modules() {
47 check(
48 r#"
49enum E { X }
50use self::E::X;
51const Z: E = E::X;
52mod m {}
53
54static FOO: E = E::X;
55struct Bar { f: u32 }
56
57fn foo() {
58 match E::X { <|> }
59}
60"#,
61 expect![[r#"
62 st Bar
63 en E
64 ev X ()
65 ct Z
66 md m
67 "#]],
68 );
69 }
70
71 #[test]
72 fn completes_in_simple_macro_call() {
73 check(
74 r#"
75macro_rules! m { ($e:expr) => { $e } }
76enum E { X }
77
78fn foo() {
79 m!(match E::X { <|> })
80}
81"#,
82 expect![[r#"
83 en E
84 ma m!(…) macro_rules! m
85 "#]],
86 );
87 }
88}
diff --git a/crates/ide/src/completion/complete_postfix.rs b/crates/ide/src/completion/complete_postfix.rs
deleted file mode 100644
index db5319618..000000000
--- a/crates/ide/src/completion/complete_postfix.rs
+++ /dev/null
@@ -1,454 +0,0 @@
1//! FIXME: write short doc here
2
3mod format_like;
4
5use assists::utils::TryEnum;
6use syntax::{
7 ast::{self, AstNode, AstToken},
8 TextRange, TextSize,
9};
10use text_edit::TextEdit;
11
12use self::format_like::add_format_like_completions;
13use crate::{
14 completion::{
15 completion_config::SnippetCap,
16 completion_context::CompletionContext,
17 completion_item::{Builder, CompletionKind, Completions},
18 },
19 CompletionItem, CompletionItemKind,
20};
21
22pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
23 if !ctx.config.enable_postfix_completions {
24 return;
25 }
26
27 let dot_receiver = match &ctx.dot_receiver {
28 Some(it) => it,
29 None => return,
30 };
31
32 let receiver_text =
33 get_receiver_text(dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
34
35 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
36 Some(it) => it,
37 None => return,
38 };
39
40 let cap = match ctx.config.snippet_cap {
41 Some(it) => it,
42 None => return,
43 };
44 let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty);
45 if let Some(try_enum) = &try_enum {
46 match try_enum {
47 TryEnum::Result => {
48 postfix_snippet(
49 ctx,
50 cap,
51 &dot_receiver,
52 "ifl",
53 "if let Ok {}",
54 &format!("if let Ok($1) = {} {{\n $0\n}}", receiver_text),
55 )
56 .add_to(acc);
57
58 postfix_snippet(
59 ctx,
60 cap,
61 &dot_receiver,
62 "while",
63 "while let Ok {}",
64 &format!("while let Ok($1) = {} {{\n $0\n}}", receiver_text),
65 )
66 .add_to(acc);
67 }
68 TryEnum::Option => {
69 postfix_snippet(
70 ctx,
71 cap,
72 &dot_receiver,
73 "ifl",
74 "if let Some {}",
75 &format!("if let Some($1) = {} {{\n $0\n}}", receiver_text),
76 )
77 .add_to(acc);
78
79 postfix_snippet(
80 ctx,
81 cap,
82 &dot_receiver,
83 "while",
84 "while let Some {}",
85 &format!("while let Some($1) = {} {{\n $0\n}}", receiver_text),
86 )
87 .add_to(acc);
88 }
89 }
90 } else if receiver_ty.is_bool() || receiver_ty.is_unknown() {
91 postfix_snippet(
92 ctx,
93 cap,
94 &dot_receiver,
95 "if",
96 "if expr {}",
97 &format!("if {} {{\n $0\n}}", receiver_text),
98 )
99 .add_to(acc);
100 postfix_snippet(
101 ctx,
102 cap,
103 &dot_receiver,
104 "while",
105 "while expr {}",
106 &format!("while {} {{\n $0\n}}", receiver_text),
107 )
108 .add_to(acc);
109 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
110 .add_to(acc);
111 }
112
113 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
114 .add_to(acc);
115 postfix_snippet(
116 ctx,
117 cap,
118 &dot_receiver,
119 "refm",
120 "&mut expr",
121 &format!("&mut {}", receiver_text),
122 )
123 .add_to(acc);
124
125 // The rest of the postfix completions create an expression that moves an argument,
126 // so it's better to consider references now to avoid breaking the compilation
127 let dot_receiver = include_references(dot_receiver);
128 let receiver_text =
129 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
130
131 match try_enum {
132 Some(try_enum) => match try_enum {
133 TryEnum::Result => {
134 postfix_snippet(
135 ctx,
136 cap,
137 &dot_receiver,
138 "match",
139 "match expr {}",
140 &format!("match {} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}", receiver_text),
141 )
142 .add_to(acc);
143 }
144 TryEnum::Option => {
145 postfix_snippet(
146 ctx,
147 cap,
148 &dot_receiver,
149 "match",
150 "match expr {}",
151 &format!(
152 "match {} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}",
153 receiver_text
154 ),
155 )
156 .add_to(acc);
157 }
158 },
159 None => {
160 postfix_snippet(
161 ctx,
162 cap,
163 &dot_receiver,
164 "match",
165 "match expr {}",
166 &format!("match {} {{\n ${{1:_}} => {{$0}},\n}}", receiver_text),
167 )
168 .add_to(acc);
169 }
170 }
171
172 postfix_snippet(
173 ctx,
174 cap,
175 &dot_receiver,
176 "box",
177 "Box::new(expr)",
178 &format!("Box::new({})", receiver_text),
179 )
180 .add_to(acc);
181
182 postfix_snippet(ctx, cap, &dot_receiver, "ok", "Ok(expr)", &format!("Ok({})", receiver_text))
183 .add_to(acc);
184
185 postfix_snippet(
186 ctx,
187 cap,
188 &dot_receiver,
189 "dbg",
190 "dbg!(expr)",
191 &format!("dbg!({})", receiver_text),
192 )
193 .add_to(acc);
194
195 postfix_snippet(
196 ctx,
197 cap,
198 &dot_receiver,
199 "dbgr",
200 "dbg!(&expr)",
201 &format!("dbg!(&{})", receiver_text),
202 )
203 .add_to(acc);
204
205 postfix_snippet(
206 ctx,
207 cap,
208 &dot_receiver,
209 "call",
210 "function(expr)",
211 &format!("${{1}}({})", receiver_text),
212 )
213 .add_to(acc);
214
215 if let ast::Expr::Literal(literal) = dot_receiver.clone() {
216 if let Some(literal_text) = ast::String::cast(literal.token()) {
217 add_format_like_completions(acc, ctx, &dot_receiver, cap, &literal_text);
218 }
219 }
220}
221
222fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
223 if receiver_is_ambiguous_float_literal {
224 let text = receiver.syntax().text();
225 let without_dot = ..text.len() - TextSize::of('.');
226 text.slice(without_dot).to_string()
227 } else {
228 receiver.to_string()
229 }
230}
231
232fn include_references(initial_element: &ast::Expr) -> ast::Expr {
233 let mut resulting_element = initial_element.clone();
234 while let Some(parent_ref_element) =
235 resulting_element.syntax().parent().and_then(ast::RefExpr::cast)
236 {
237 resulting_element = ast::Expr::from(parent_ref_element);
238 }
239 resulting_element
240}
241
242fn postfix_snippet(
243 ctx: &CompletionContext,
244 cap: SnippetCap,
245 receiver: &ast::Expr,
246 label: &str,
247 detail: &str,
248 snippet: &str,
249) -> Builder {
250 let edit = {
251 let receiver_syntax = receiver.syntax();
252 let receiver_range = ctx.sema.original_range(receiver_syntax).range;
253 let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
254 TextEdit::replace(delete_range, snippet.to_string())
255 };
256 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
257 .detail(detail)
258 .kind(CompletionItemKind::Snippet)
259 .snippet_edit(cap, edit)
260}
261
262#[cfg(test)]
263mod tests {
264 use expect_test::{expect, Expect};
265
266 use crate::completion::{
267 test_utils::{check_edit, completion_list},
268 CompletionKind,
269 };
270
271 fn check(ra_fixture: &str, expect: Expect) {
272 let actual = completion_list(ra_fixture, CompletionKind::Postfix);
273 expect.assert_eq(&actual)
274 }
275
276 #[test]
277 fn postfix_completion_works_for_trivial_path_expression() {
278 check(
279 r#"
280fn main() {
281 let bar = true;
282 bar.<|>
283}
284"#,
285 expect![[r#"
286 sn box Box::new(expr)
287 sn call function(expr)
288 sn dbg dbg!(expr)
289 sn dbgr dbg!(&expr)
290 sn if if expr {}
291 sn match match expr {}
292 sn not !expr
293 sn ok Ok(expr)
294 sn ref &expr
295 sn refm &mut expr
296 sn while while expr {}
297 "#]],
298 );
299 }
300
301 #[test]
302 fn postfix_type_filtering() {
303 check(
304 r#"
305fn main() {
306 let bar: u8 = 12;
307 bar.<|>
308}
309"#,
310 expect![[r#"
311 sn box Box::new(expr)
312 sn call function(expr)
313 sn dbg dbg!(expr)
314 sn dbgr dbg!(&expr)
315 sn match match expr {}
316 sn ok Ok(expr)
317 sn ref &expr
318 sn refm &mut expr
319 "#]],
320 )
321 }
322
323 #[test]
324 fn option_iflet() {
325 check_edit(
326 "ifl",
327 r#"
328enum Option<T> { Some(T), None }
329
330fn main() {
331 let bar = Option::Some(true);
332 bar.<|>
333}
334"#,
335 r#"
336enum Option<T> { Some(T), None }
337
338fn main() {
339 let bar = Option::Some(true);
340 if let Some($1) = bar {
341 $0
342}
343}
344"#,
345 );
346 }
347
348 #[test]
349 fn result_match() {
350 check_edit(
351 "match",
352 r#"
353enum Result<T, E> { Ok(T), Err(E) }
354
355fn main() {
356 let bar = Result::Ok(true);
357 bar.<|>
358}
359"#,
360 r#"
361enum Result<T, E> { Ok(T), Err(E) }
362
363fn main() {
364 let bar = Result::Ok(true);
365 match bar {
366 Ok(${1:_}) => {$2},
367 Err(${3:_}) => {$0},
368}
369}
370"#,
371 );
372 }
373
374 #[test]
375 fn postfix_completion_works_for_ambiguous_float_literal() {
376 check_edit("refm", r#"fn main() { 42.<|> }"#, r#"fn main() { &mut 42 }"#)
377 }
378
379 #[test]
380 fn works_in_simple_macro() {
381 check_edit(
382 "dbg",
383 r#"
384macro_rules! m { ($e:expr) => { $e } }
385fn main() {
386 let bar: u8 = 12;
387 m!(bar.d<|>)
388}
389"#,
390 r#"
391macro_rules! m { ($e:expr) => { $e } }
392fn main() {
393 let bar: u8 = 12;
394 m!(dbg!(bar))
395}
396"#,
397 );
398 }
399
400 #[test]
401 fn postfix_completion_for_references() {
402 check_edit("dbg", r#"fn main() { &&42.<|> }"#, r#"fn main() { dbg!(&&42) }"#);
403 check_edit("refm", r#"fn main() { &&42.<|> }"#, r#"fn main() { &&&mut 42 }"#);
404 }
405
406 #[test]
407 fn postfix_completion_for_format_like_strings() {
408 check_edit(
409 "fmt",
410 r#"fn main() { "{some_var:?}".<|> }"#,
411 r#"fn main() { format!("{:?}", some_var) }"#,
412 );
413 check_edit(
414 "panic",
415 r#"fn main() { "Panic with {a}".<|> }"#,
416 r#"fn main() { panic!("Panic with {}", a) }"#,
417 );
418 check_edit(
419 "println",
420 r#"fn main() { "{ 2+2 } { SomeStruct { val: 1, other: 32 } :?}".<|> }"#,
421 r#"fn main() { println!("{} {:?}", 2+2, SomeStruct { val: 1, other: 32 }) }"#,
422 );
423 check_edit(
424 "loge",
425 r#"fn main() { "{2+2}".<|> }"#,
426 r#"fn main() { log::error!("{}", 2+2) }"#,
427 );
428 check_edit(
429 "logt",
430 r#"fn main() { "{2+2}".<|> }"#,
431 r#"fn main() { log::trace!("{}", 2+2) }"#,
432 );
433 check_edit(
434 "logd",
435 r#"fn main() { "{2+2}".<|> }"#,
436 r#"fn main() { log::debug!("{}", 2+2) }"#,
437 );
438 check_edit(
439 "logi",
440 r#"fn main() { "{2+2}".<|> }"#,
441 r#"fn main() { log::info!("{}", 2+2) }"#,
442 );
443 check_edit(
444 "logw",
445 r#"fn main() { "{2+2}".<|> }"#,
446 r#"fn main() { log::warn!("{}", 2+2) }"#,
447 );
448 check_edit(
449 "loge",
450 r#"fn main() { "{2+2}".<|> }"#,
451 r#"fn main() { log::error!("{}", 2+2) }"#,
452 );
453 }
454}
diff --git a/crates/ide/src/completion/complete_postfix/format_like.rs b/crates/ide/src/completion/complete_postfix/format_like.rs
deleted file mode 100644
index 50d1e5c81..000000000
--- a/crates/ide/src/completion/complete_postfix/format_like.rs
+++ /dev/null
@@ -1,279 +0,0 @@
1// Feature: Format String Completion.
2//
3// `"Result {result} is {2 + 2}"` is expanded to the `"Result {} is {}", result, 2 + 2`.
4//
5// The following postfix snippets are available:
6//
7// - `format` -> `format!(...)`
8// - `panic` -> `panic!(...)`
9// - `println` -> `println!(...)`
10// - `log`:
11// + `logd` -> `log::debug!(...)`
12// + `logt` -> `log::trace!(...)`
13// + `logi` -> `log::info!(...)`
14// + `logw` -> `log::warn!(...)`
15// + `loge` -> `log::error!(...)`
16
17use crate::completion::{
18 complete_postfix::postfix_snippet, completion_config::SnippetCap,
19 completion_context::CompletionContext, completion_item::Completions,
20};
21use syntax::ast::{self, AstToken};
22
23/// Mapping ("postfix completion item" => "macro to use")
24static KINDS: &[(&str, &str)] = &[
25 ("fmt", "format!"),
26 ("panic", "panic!"),
27 ("println", "println!"),
28 ("eprintln", "eprintln!"),
29 ("logd", "log::debug!"),
30 ("logt", "log::trace!"),
31 ("logi", "log::info!"),
32 ("logw", "log::warn!"),
33 ("loge", "log::error!"),
34];
35
36pub(super) fn add_format_like_completions(
37 acc: &mut Completions,
38 ctx: &CompletionContext,
39 dot_receiver: &ast::Expr,
40 cap: SnippetCap,
41 receiver_text: &ast::String,
42) {
43 let input = match string_literal_contents(receiver_text) {
44 // It's not a string literal, do not parse input.
45 Some(input) => input,
46 None => return,
47 };
48
49 let mut parser = FormatStrParser::new(input);
50
51 if parser.parse().is_ok() {
52 for (label, macro_name) in KINDS {
53 let snippet = parser.into_suggestion(macro_name);
54
55 postfix_snippet(ctx, cap, &dot_receiver, label, macro_name, &snippet).add_to(acc);
56 }
57 }
58}
59
60/// Checks whether provided item is a string literal.
61fn string_literal_contents(item: &ast::String) -> Option<String> {
62 let item = item.text();
63 if item.len() >= 2 && item.starts_with("\"") && item.ends_with("\"") {
64 return Some(item[1..item.len() - 1].to_owned());
65 }
66
67 None
68}
69
70/// Parser for a format-like string. It is more allowing in terms of string contents,
71/// as we expect variable placeholders to be filled with expressions.
72#[derive(Debug)]
73pub struct FormatStrParser {
74 input: String,
75 output: String,
76 extracted_expressions: Vec<String>,
77 state: State,
78 parsed: bool,
79}
80
81#[derive(Debug, Clone, Copy, PartialEq)]
82enum State {
83 NotExpr,
84 MaybeExpr,
85 Expr,
86 MaybeIncorrect,
87 FormatOpts,
88}
89
90impl FormatStrParser {
91 pub fn new(input: String) -> Self {
92 Self {
93 input: input.into(),
94 output: String::new(),
95 extracted_expressions: Vec::new(),
96 state: State::NotExpr,
97 parsed: false,
98 }
99 }
100
101 pub fn parse(&mut self) -> Result<(), ()> {
102 let mut current_expr = String::new();
103
104 let mut placeholder_id = 1;
105
106 // Count of open braces inside of an expression.
107 // We assume that user knows what they're doing, thus we treat it like a correct pattern, e.g.
108 // "{MyStruct { val_a: 0, val_b: 1 }}".
109 let mut inexpr_open_count = 0;
110
111 for chr in self.input.chars() {
112 match (self.state, chr) {
113 (State::NotExpr, '{') => {
114 self.output.push(chr);
115 self.state = State::MaybeExpr;
116 }
117 (State::NotExpr, '}') => {
118 self.output.push(chr);
119 self.state = State::MaybeIncorrect;
120 }
121 (State::NotExpr, _) => {
122 self.output.push(chr);
123 }
124 (State::MaybeIncorrect, '}') => {
125 // It's okay, we met "}}".
126 self.output.push(chr);
127 self.state = State::NotExpr;
128 }
129 (State::MaybeIncorrect, _) => {
130 // Error in the string.
131 return Err(());
132 }
133 (State::MaybeExpr, '{') => {
134 self.output.push(chr);
135 self.state = State::NotExpr;
136 }
137 (State::MaybeExpr, '}') => {
138 // This is an empty sequence '{}'. Replace it with placeholder.
139 self.output.push(chr);
140 self.extracted_expressions.push(format!("${}", placeholder_id));
141 placeholder_id += 1;
142 self.state = State::NotExpr;
143 }
144 (State::MaybeExpr, _) => {
145 current_expr.push(chr);
146 self.state = State::Expr;
147 }
148 (State::Expr, '}') => {
149 if inexpr_open_count == 0 {
150 self.output.push(chr);
151 self.extracted_expressions.push(current_expr.trim().into());
152 current_expr = String::new();
153 self.state = State::NotExpr;
154 } else {
155 // We're closing one brace met before inside of the expression.
156 current_expr.push(chr);
157 inexpr_open_count -= 1;
158 }
159 }
160 (State::Expr, ':') => {
161 if inexpr_open_count == 0 {
162 // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}"
163 self.output.push(chr);
164 self.extracted_expressions.push(current_expr.trim().into());
165 current_expr = String::new();
166 self.state = State::FormatOpts;
167 } else {
168 // We're inside of braced expression, assume that it's a struct field name/value delimeter.
169 current_expr.push(chr);
170 }
171 }
172 (State::Expr, '{') => {
173 current_expr.push(chr);
174 inexpr_open_count += 1;
175 }
176 (State::Expr, _) => {
177 current_expr.push(chr);
178 }
179 (State::FormatOpts, '}') => {
180 self.output.push(chr);
181 self.state = State::NotExpr;
182 }
183 (State::FormatOpts, _) => {
184 self.output.push(chr);
185 }
186 }
187 }
188
189 if self.state != State::NotExpr {
190 return Err(());
191 }
192
193 self.parsed = true;
194 Ok(())
195 }
196
197 pub fn into_suggestion(&self, macro_name: &str) -> String {
198 assert!(self.parsed, "Attempt to get a suggestion from not parsed expression");
199
200 let expressions_as_string = self.extracted_expressions.join(", ");
201 format!(r#"{}("{}", {})"#, macro_name, self.output, expressions_as_string)
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208 use expect_test::{expect, Expect};
209
210 fn check(input: &str, expect: &Expect) {
211 let mut parser = FormatStrParser::new((*input).to_owned());
212 let outcome_repr = if parser.parse().is_ok() {
213 // Parsing should be OK, expected repr is "string; expr_1, expr_2".
214 if parser.extracted_expressions.is_empty() {
215 parser.output
216 } else {
217 format!("{}; {}", parser.output, parser.extracted_expressions.join(", "))
218 }
219 } else {
220 // Parsing should fail, expected repr is "-".
221 "-".to_owned()
222 };
223
224 expect.assert_eq(&outcome_repr);
225 }
226
227 #[test]
228 fn format_str_parser() {
229 let test_vector = &[
230 ("no expressions", expect![["no expressions"]]),
231 ("{expr} is {2 + 2}", expect![["{} is {}; expr, 2 + 2"]]),
232 ("{expr:?}", expect![["{:?}; expr"]]),
233 ("{malformed", expect![["-"]]),
234 ("malformed}", expect![["-"]]),
235 ("{{correct", expect![["{{correct"]]),
236 ("correct}}", expect![["correct}}"]]),
237 ("{correct}}}", expect![["{}}}; correct"]]),
238 ("{correct}}}}}", expect![["{}}}}}; correct"]]),
239 ("{incorrect}}", expect![["-"]]),
240 ("placeholders {} {}", expect![["placeholders {} {}; $1, $2"]]),
241 ("mixed {} {2 + 2} {}", expect![["mixed {} {} {}; $1, 2 + 2, $2"]]),
242 (
243 "{SomeStruct { val_a: 0, val_b: 1 }}",
244 expect![["{}; SomeStruct { val_a: 0, val_b: 1 }"]],
245 ),
246 ("{expr:?} is {2.32f64:.5}", expect![["{:?} is {:.5}; expr, 2.32f64"]]),
247 (
248 "{SomeStruct { val_a: 0, val_b: 1 }:?}",
249 expect![["{:?}; SomeStruct { val_a: 0, val_b: 1 }"]],
250 ),
251 ("{ 2 + 2 }", expect![["{}; 2 + 2"]]),
252 ];
253
254 for (input, output) in test_vector {
255 check(input, output)
256 }
257 }
258
259 #[test]
260 fn test_into_suggestion() {
261 let test_vector = &[
262 ("println!", "{}", r#"println!("{}", $1)"#),
263 ("eprintln!", "{}", r#"eprintln!("{}", $1)"#),
264 (
265 "log::info!",
266 "{} {expr} {} {2 + 2}",
267 r#"log::info!("{} {} {} {}", $1, expr, $2, 2 + 2)"#,
268 ),
269 ("format!", "{expr:?}", r#"format!("{:?}", expr)"#),
270 ];
271
272 for (kind, input, output) in test_vector {
273 let mut parser = FormatStrParser::new((*input).to_owned());
274 parser.parse().expect("Parsing must succeed");
275
276 assert_eq!(&parser.into_suggestion(*kind), output);
277 }
278 }
279}
diff --git a/crates/ide/src/completion/complete_qualified_path.rs b/crates/ide/src/completion/complete_qualified_path.rs
deleted file mode 100644
index 2fafedd47..000000000
--- a/crates/ide/src/completion/complete_qualified_path.rs
+++ /dev/null
@@ -1,755 +0,0 @@
1//! Completion of paths, i.e. `some::prefix::<|>`.
2
3use hir::{Adt, HasVisibility, PathResolution, ScopeDef};
4use rustc_hash::FxHashSet;
5use syntax::AstNode;
6use test_utils::mark;
7
8use crate::completion::{CompletionContext, Completions};
9
10pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
11 let path = match &ctx.path_qual {
12 Some(path) => path.clone(),
13 None => return,
14 };
15
16 if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() {
17 return;
18 }
19
20 let context_module = ctx.scope.module();
21
22 let resolution = match ctx.sema.resolve_path(&path) {
23 Some(res) => res,
24 None => return,
25 };
26
27 // Add associated types on type parameters and `Self`.
28 resolution.assoc_type_shorthand_candidates(ctx.db, |alias| {
29 acc.add_type_alias(ctx, alias);
30 None::<()>
31 });
32
33 match resolution {
34 PathResolution::Def(hir::ModuleDef::Module(module)) => {
35 let module_scope = module.scope(ctx.db, context_module);
36 for (name, def) in module_scope {
37 if ctx.use_item_syntax.is_some() {
38 if let ScopeDef::Unknown = def {
39 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() {
40 if name_ref.syntax().text() == name.to_string().as_str() {
41 // for `use self::foo<|>`, don't suggest `foo` as a completion
42 mark::hit!(dont_complete_current_use);
43 continue;
44 }
45 }
46 }
47 }
48
49 acc.add_resolution(ctx, name.to_string(), &def);
50 }
51 }
52 PathResolution::Def(def @ hir::ModuleDef::Adt(_))
53 | PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) => {
54 if let hir::ModuleDef::Adt(Adt::Enum(e)) = def {
55 for variant in e.variants(ctx.db) {
56 acc.add_enum_variant(ctx, variant, None);
57 }
58 }
59 let ty = match def {
60 hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
61 hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
62 _ => unreachable!(),
63 };
64
65 // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType.
66 // (where AssocType is defined on a trait, not an inherent impl)
67
68 let krate = ctx.krate;
69 if let Some(krate) = krate {
70 let traits_in_scope = ctx.scope.traits_in_scope();
71 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
72 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
73 return None;
74 }
75 match item {
76 hir::AssocItem::Function(func) => {
77 acc.add_function(ctx, func, None);
78 }
79 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
80 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
81 }
82 None::<()>
83 });
84
85 // Iterate assoc types separately
86 ty.iterate_assoc_items(ctx.db, krate, |item| {
87 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
88 return None;
89 }
90 match item {
91 hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {}
92 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
93 }
94 None::<()>
95 });
96 }
97 }
98 PathResolution::Def(hir::ModuleDef::Trait(t)) => {
99 // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
100 for item in t.items(ctx.db) {
101 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
102 continue;
103 }
104 match item {
105 hir::AssocItem::Function(func) => {
106 acc.add_function(ctx, func, None);
107 }
108 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
109 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
110 }
111 }
112 }
113 PathResolution::TypeParam(_) | PathResolution::SelfType(_) => {
114 if let Some(krate) = ctx.krate {
115 let ty = match resolution {
116 PathResolution::TypeParam(param) => param.ty(ctx.db),
117 PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db),
118 _ => return,
119 };
120
121 let traits_in_scope = ctx.scope.traits_in_scope();
122 let mut seen = FxHashSet::default();
123 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
124 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
125 return None;
126 }
127
128 // We might iterate candidates of a trait multiple times here, so deduplicate
129 // them.
130 if seen.insert(item) {
131 match item {
132 hir::AssocItem::Function(func) => {
133 acc.add_function(ctx, func, None);
134 }
135 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
136 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
137 }
138 }
139 None::<()>
140 });
141 }
142 }
143 _ => {}
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use expect_test::{expect, Expect};
150 use test_utils::mark;
151
152 use crate::completion::{
153 test_utils::{check_edit, completion_list},
154 CompletionKind,
155 };
156
157 fn check(ra_fixture: &str, expect: Expect) {
158 let actual = completion_list(ra_fixture, CompletionKind::Reference);
159 expect.assert_eq(&actual);
160 }
161
162 fn check_builtin(ra_fixture: &str, expect: Expect) {
163 let actual = completion_list(ra_fixture, CompletionKind::BuiltinType);
164 expect.assert_eq(&actual);
165 }
166
167 #[test]
168 fn dont_complete_current_use() {
169 mark::check!(dont_complete_current_use);
170 check(r#"use self::foo<|>;"#, expect![[""]]);
171 }
172
173 #[test]
174 fn dont_complete_current_use_in_braces_with_glob() {
175 check(
176 r#"
177mod foo { pub struct S; }
178use self::{foo::*, bar<|>};
179"#,
180 expect![[r#"
181 st S
182 md foo
183 "#]],
184 );
185 }
186
187 #[test]
188 fn dont_complete_primitive_in_use() {
189 check_builtin(r#"use self::<|>;"#, expect![[""]]);
190 }
191
192 #[test]
193 fn dont_complete_primitive_in_module_scope() {
194 check_builtin(r#"fn foo() { self::<|> }"#, expect![[""]]);
195 }
196
197 #[test]
198 fn completes_primitives() {
199 check_builtin(
200 r#"fn main() { let _: <|> = 92; }"#,
201 expect![[r#"
202 bt bool
203 bt char
204 bt f32
205 bt f64
206 bt i128
207 bt i16
208 bt i32
209 bt i64
210 bt i8
211 bt isize
212 bt str
213 bt u128
214 bt u16
215 bt u32
216 bt u64
217 bt u8
218 bt usize
219 "#]],
220 );
221 }
222
223 #[test]
224 fn completes_mod_with_same_name_as_function() {
225 check(
226 r#"
227use self::my::<|>;
228
229mod my { pub struct Bar; }
230fn my() {}
231"#,
232 expect![[r#"
233 st Bar
234 "#]],
235 );
236 }
237
238 #[test]
239 fn filters_visibility() {
240 check(
241 r#"
242use self::my::<|>;
243
244mod my {
245 struct Bar;
246 pub struct Foo;
247 pub use Bar as PublicBar;
248}
249"#,
250 expect![[r#"
251 st Foo
252 st PublicBar
253 "#]],
254 );
255 }
256
257 #[test]
258 fn completes_use_item_starting_with_self() {
259 check(
260 r#"
261use self::m::<|>;
262
263mod m { pub struct Bar; }
264"#,
265 expect![[r#"
266 st Bar
267 "#]],
268 );
269 }
270
271 #[test]
272 fn completes_use_item_starting_with_crate() {
273 check(
274 r#"
275//- /lib.rs
276mod foo;
277struct Spam;
278//- /foo.rs
279use crate::Sp<|>
280"#,
281 expect![[r#"
282 st Spam
283 md foo
284 "#]],
285 );
286 }
287
288 #[test]
289 fn completes_nested_use_tree() {
290 check(
291 r#"
292//- /lib.rs
293mod foo;
294struct Spam;
295//- /foo.rs
296use crate::{Sp<|>};
297"#,
298 expect![[r#"
299 st Spam
300 md foo
301 "#]],
302 );
303 }
304
305 #[test]
306 fn completes_deeply_nested_use_tree() {
307 check(
308 r#"
309//- /lib.rs
310mod foo;
311pub mod bar {
312 pub mod baz {
313 pub struct Spam;
314 }
315}
316//- /foo.rs
317use crate::{bar::{baz::Sp<|>}};
318"#,
319 expect![[r#"
320 st Spam
321 "#]],
322 );
323 }
324
325 #[test]
326 fn completes_enum_variant() {
327 check(
328 r#"
329enum E { Foo, Bar(i32) }
330fn foo() { let _ = E::<|> }
331"#,
332 expect![[r#"
333 ev Bar(…) (i32)
334 ev Foo ()
335 "#]],
336 );
337 }
338
339 #[test]
340 fn completes_struct_associated_items() {
341 check(
342 r#"
343//- /lib.rs
344struct S;
345
346impl S {
347 fn a() {}
348 fn b(&self) {}
349 const C: i32 = 42;
350 type T = i32;
351}
352
353fn foo() { let _ = S::<|> }
354"#,
355 expect![[r#"
356 ct C const C: i32 = 42;
357 ta T type T = i32;
358 fn a() fn a()
359 me b() fn b(&self)
360 "#]],
361 );
362 }
363
364 #[test]
365 fn associated_item_visibility() {
366 check(
367 r#"
368struct S;
369
370mod m {
371 impl super::S {
372 pub(super) fn public_method() { }
373 fn private_method() { }
374 pub(super) type PublicType = u32;
375 type PrivateType = u32;
376 pub(super) const PUBLIC_CONST: u32 = 1;
377 const PRIVATE_CONST: u32 = 1;
378 }
379}
380
381fn foo() { let _ = S::<|> }
382"#,
383 expect![[r#"
384 ct PUBLIC_CONST pub(super) const PUBLIC_CONST: u32 = 1;
385 ta PublicType pub(super) type PublicType = u32;
386 fn public_method() pub(super) fn public_method()
387 "#]],
388 );
389 }
390
391 #[test]
392 fn completes_enum_associated_method() {
393 check(
394 r#"
395enum E {};
396impl E { fn m() { } }
397
398fn foo() { let _ = E::<|> }
399 "#,
400 expect![[r#"
401 fn m() fn m()
402 "#]],
403 );
404 }
405
406 #[test]
407 fn completes_union_associated_method() {
408 check(
409 r#"
410union U {};
411impl U { fn m() { } }
412
413fn foo() { let _ = U::<|> }
414"#,
415 expect![[r#"
416 fn m() fn m()
417 "#]],
418 );
419 }
420
421 #[test]
422 fn completes_use_paths_across_crates() {
423 check(
424 r#"
425//- /main.rs crate:main deps:foo
426use foo::<|>;
427
428//- /foo/lib.rs crate:foo
429pub mod bar { pub struct S; }
430"#,
431 expect![[r#"
432 md bar
433 "#]],
434 );
435 }
436
437 #[test]
438 fn completes_trait_associated_method_1() {
439 check(
440 r#"
441trait Trait { fn m(); }
442
443fn foo() { let _ = Trait::<|> }
444"#,
445 expect![[r#"
446 fn m() fn m()
447 "#]],
448 );
449 }
450
451 #[test]
452 fn completes_trait_associated_method_2() {
453 check(
454 r#"
455trait Trait { fn m(); }
456
457struct S;
458impl Trait for S {}
459
460fn foo() { let _ = S::<|> }
461"#,
462 expect![[r#"
463 fn m() fn m()
464 "#]],
465 );
466 }
467
468 #[test]
469 fn completes_trait_associated_method_3() {
470 check(
471 r#"
472trait Trait { fn m(); }
473
474struct S;
475impl Trait for S {}
476
477fn foo() { let _ = <S as Trait>::<|> }
478"#,
479 expect![[r#"
480 fn m() fn m()
481 "#]],
482 );
483 }
484
485 #[test]
486 fn completes_ty_param_assoc_ty() {
487 check(
488 r#"
489trait Super {
490 type Ty;
491 const CONST: u8;
492 fn func() {}
493 fn method(&self) {}
494}
495
496trait Sub: Super {
497 type SubTy;
498 const C2: ();
499 fn subfunc() {}
500 fn submethod(&self) {}
501}
502
503fn foo<T: Sub>() { T::<|> }
504"#,
505 expect![[r#"
506 ct C2 const C2: ();
507 ct CONST const CONST: u8;
508 ta SubTy type SubTy;
509 ta Ty type Ty;
510 fn func() fn func()
511 me method() fn method(&self)
512 fn subfunc() fn subfunc()
513 me submethod() fn submethod(&self)
514 "#]],
515 );
516 }
517
518 #[test]
519 fn completes_self_param_assoc_ty() {
520 check(
521 r#"
522trait Super {
523 type Ty;
524 const CONST: u8 = 0;
525 fn func() {}
526 fn method(&self) {}
527}
528
529trait Sub: Super {
530 type SubTy;
531 const C2: () = ();
532 fn subfunc() {}
533 fn submethod(&self) {}
534}
535
536struct Wrap<T>(T);
537impl<T> Super for Wrap<T> {}
538impl<T> Sub for Wrap<T> {
539 fn subfunc() {
540 // Should be able to assume `Self: Sub + Super`
541 Self::<|>
542 }
543}
544"#,
545 expect![[r#"
546 ct C2 const C2: () = ();
547 ct CONST const CONST: u8 = 0;
548 ta SubTy type SubTy;
549 ta Ty type Ty;
550 fn func() fn func()
551 me method() fn method(&self)
552 fn subfunc() fn subfunc()
553 me submethod() fn submethod(&self)
554 "#]],
555 );
556 }
557
558 #[test]
559 fn completes_type_alias() {
560 check(
561 r#"
562struct S;
563impl S { fn foo() {} }
564type T = S;
565impl T { fn bar() {} }
566
567fn main() { T::<|>; }
568"#,
569 expect![[r#"
570 fn bar() fn bar()
571 fn foo() fn foo()
572 "#]],
573 );
574 }
575
576 #[test]
577 fn completes_qualified_macros() {
578 check(
579 r#"
580#[macro_export]
581macro_rules! foo { () => {} }
582
583fn main() { let _ = crate::<|> }
584 "#,
585 expect![[r##"
586 ma foo!(…) #[macro_export]
587 macro_rules! foo
588 fn main() fn main()
589 "##]],
590 );
591 }
592
593 #[test]
594 fn test_super_super_completion() {
595 check(
596 r#"
597mod a {
598 const A: usize = 0;
599 mod b {
600 const B: usize = 0;
601 mod c { use super::super::<|> }
602 }
603}
604"#,
605 expect![[r#"
606 ct A
607 md b
608 "#]],
609 );
610 }
611
612 #[test]
613 fn completes_reexported_items_under_correct_name() {
614 check(
615 r#"
616fn foo() { self::m::<|> }
617
618mod m {
619 pub use super::p::wrong_fn as right_fn;
620 pub use super::p::WRONG_CONST as RIGHT_CONST;
621 pub use super::p::WrongType as RightType;
622}
623mod p {
624 fn wrong_fn() {}
625 const WRONG_CONST: u32 = 1;
626 struct WrongType {};
627}
628"#,
629 expect![[r#"
630 ct RIGHT_CONST
631 st RightType
632 fn right_fn() fn wrong_fn()
633 "#]],
634 );
635
636 check_edit(
637 "RightType",
638 r#"
639fn foo() { self::m::<|> }
640
641mod m {
642 pub use super::p::wrong_fn as right_fn;
643 pub use super::p::WRONG_CONST as RIGHT_CONST;
644 pub use super::p::WrongType as RightType;
645}
646mod p {
647 fn wrong_fn() {}
648 const WRONG_CONST: u32 = 1;
649 struct WrongType {};
650}
651"#,
652 r#"
653fn foo() { self::m::RightType }
654
655mod m {
656 pub use super::p::wrong_fn as right_fn;
657 pub use super::p::WRONG_CONST as RIGHT_CONST;
658 pub use super::p::WrongType as RightType;
659}
660mod p {
661 fn wrong_fn() {}
662 const WRONG_CONST: u32 = 1;
663 struct WrongType {};
664}
665"#,
666 );
667 }
668
669 #[test]
670 fn completes_in_simple_macro_call() {
671 check(
672 r#"
673macro_rules! m { ($e:expr) => { $e } }
674fn main() { m!(self::f<|>); }
675fn foo() {}
676"#,
677 expect![[r#"
678 fn foo() fn foo()
679 fn main() fn main()
680 "#]],
681 );
682 }
683
684 #[test]
685 fn function_mod_share_name() {
686 check(
687 r#"
688fn foo() { self::m::<|> }
689
690mod m {
691 pub mod z {}
692 pub fn z() {}
693}
694"#,
695 expect![[r#"
696 md z
697 fn z() pub fn z()
698 "#]],
699 );
700 }
701
702 #[test]
703 fn completes_hashmap_new() {
704 check(
705 r#"
706struct RandomState;
707struct HashMap<K, V, S = RandomState> {}
708
709impl<K, V> HashMap<K, V, RandomState> {
710 pub fn new() -> HashMap<K, V, RandomState> { }
711}
712fn foo() {
713 HashMap::<|>
714}
715"#,
716 expect![[r#"
717 fn new() pub fn new() -> HashMap<K, V, RandomState>
718 "#]],
719 );
720 }
721
722 #[test]
723 fn dont_complete_attr() {
724 check(
725 r#"
726mod foo { pub struct Foo; }
727#[foo::<|>]
728fn f() {}
729"#,
730 expect![[""]],
731 );
732 }
733
734 #[test]
735 fn completes_function() {
736 check(
737 r#"
738fn foo(
739 a: i32,
740 b: i32
741) {
742
743}
744
745fn main() {
746 fo<|>
747}
748"#,
749 expect![[r#"
750 fn foo(…) fn foo(a: i32, b: i32)
751 fn main() fn main()
752 "#]],
753 );
754 }
755}
diff --git a/crates/ide/src/completion/complete_record.rs b/crates/ide/src/completion/complete_record.rs
deleted file mode 100644
index ceb8d16c1..000000000
--- a/crates/ide/src/completion/complete_record.rs
+++ /dev/null
@@ -1,226 +0,0 @@
1//! Complete fields in record literals and patterns.
2use crate::completion::{CompletionContext, Completions};
3
4pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
5 let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) {
6 (None, None) => return None,
7 (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"),
8 (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat),
9 (_, Some(record_lit)) => ctx.sema.record_literal_missing_fields(record_lit),
10 };
11
12 for (field, ty) in missing_fields {
13 acc.add_field(ctx, field, &ty)
14 }
15
16 Some(())
17}
18
19#[cfg(test)]
20mod tests {
21 use expect_test::{expect, Expect};
22
23 use crate::completion::{test_utils::completion_list, CompletionKind};
24
25 fn check(ra_fixture: &str, expect: Expect) {
26 let actual = completion_list(ra_fixture, CompletionKind::Reference);
27 expect.assert_eq(&actual);
28 }
29
30 #[test]
31 fn test_record_pattern_field() {
32 check(
33 r#"
34struct S { foo: u32 }
35
36fn process(f: S) {
37 match f {
38 S { f<|>: 92 } => (),
39 }
40}
41"#,
42 expect![[r#"
43 fd foo u32
44 "#]],
45 );
46 }
47
48 #[test]
49 fn test_record_pattern_enum_variant() {
50 check(
51 r#"
52enum E { S { foo: u32, bar: () } }
53
54fn process(e: E) {
55 match e {
56 E::S { <|> } => (),
57 }
58}
59"#,
60 expect![[r#"
61 fd bar ()
62 fd foo u32
63 "#]],
64 );
65 }
66
67 #[test]
68 fn test_record_pattern_field_in_simple_macro() {
69 check(
70 r"
71macro_rules! m { ($e:expr) => { $e } }
72struct S { foo: u32 }
73
74fn process(f: S) {
75 m!(match f {
76 S { f<|>: 92 } => (),
77 })
78}
79",
80 expect![[r#"
81 fd foo u32
82 "#]],
83 );
84 }
85
86 #[test]
87 fn only_missing_fields_are_completed_in_destruct_pats() {
88 check(
89 r#"
90struct S {
91 foo1: u32, foo2: u32,
92 bar: u32, baz: u32,
93}
94
95fn main() {
96 let s = S {
97 foo1: 1, foo2: 2,
98 bar: 3, baz: 4,
99 };
100 if let S { foo1, foo2: a, <|> } = s {}
101}
102"#,
103 expect![[r#"
104 fd bar u32
105 fd baz u32
106 "#]],
107 );
108 }
109
110 #[test]
111 fn test_record_literal_field() {
112 check(
113 r#"
114struct A { the_field: u32 }
115fn foo() {
116 A { the<|> }
117}
118"#,
119 expect![[r#"
120 fd the_field u32
121 "#]],
122 );
123 }
124
125 #[test]
126 fn test_record_literal_enum_variant() {
127 check(
128 r#"
129enum E { A { a: u32 } }
130fn foo() {
131 let _ = E::A { <|> }
132}
133"#,
134 expect![[r#"
135 fd a u32
136 "#]],
137 );
138 }
139
140 #[test]
141 fn test_record_literal_two_structs() {
142 check(
143 r#"
144struct A { a: u32 }
145struct B { b: u32 }
146
147fn foo() {
148 let _: A = B { <|> }
149}
150"#,
151 expect![[r#"
152 fd b u32
153 "#]],
154 );
155 }
156
157 #[test]
158 fn test_record_literal_generic_struct() {
159 check(
160 r#"
161struct A<T> { a: T }
162
163fn foo() {
164 let _: A<u32> = A { <|> }
165}
166"#,
167 expect![[r#"
168 fd a u32
169 "#]],
170 );
171 }
172
173 #[test]
174 fn test_record_literal_field_in_simple_macro() {
175 check(
176 r#"
177macro_rules! m { ($e:expr) => { $e } }
178struct A { the_field: u32 }
179fn foo() {
180 m!(A { the<|> })
181}
182"#,
183 expect![[r#"
184 fd the_field u32
185 "#]],
186 );
187 }
188
189 #[test]
190 fn only_missing_fields_are_completed() {
191 check(
192 r#"
193struct S {
194 foo1: u32, foo2: u32,
195 bar: u32, baz: u32,
196}
197
198fn main() {
199 let foo1 = 1;
200 let s = S { foo1, foo2: 5, <|> }
201}
202"#,
203 expect![[r#"
204 fd bar u32
205 fd baz u32
206 "#]],
207 );
208 }
209
210 #[test]
211 fn completes_functional_update() {
212 check(
213 r#"
214struct S { foo1: u32, foo2: u32 }
215
216fn main() {
217 let foo1 = 1;
218 let s = S { foo1, <|> .. loop {} }
219}
220"#,
221 expect![[r#"
222 fd foo2 u32
223 "#]],
224 );
225 }
226}
diff --git a/crates/ide/src/completion/complete_snippet.rs b/crates/ide/src/completion/complete_snippet.rs
deleted file mode 100644
index 4837d2910..000000000
--- a/crates/ide/src/completion/complete_snippet.rs
+++ /dev/null
@@ -1,114 +0,0 @@
1//! FIXME: write short doc here
2
3use crate::completion::{
4 completion_config::SnippetCap, completion_item::Builder, CompletionContext, CompletionItem,
5 CompletionItemKind, CompletionKind, Completions,
6};
7
8fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
9 CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label)
10 .insert_snippet(cap, snippet)
11 .kind(CompletionItemKind::Snippet)
12}
13
14pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
15 if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) {
16 return;
17 }
18 let cap = match ctx.config.snippet_cap {
19 Some(it) => it,
20 None => return,
21 };
22
23 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
24 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
25}
26
27pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
28 if !ctx.is_new_item {
29 return;
30 }
31 let cap = match ctx.config.snippet_cap {
32 Some(it) => it,
33 None => return,
34 };
35
36 snippet(
37 ctx,
38 cap,
39 "tmod (Test module)",
40 "\
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn ${1:test_name}() {
47 $0
48 }
49}",
50 )
51 .lookup_by("tmod")
52 .add_to(acc);
53
54 snippet(
55 ctx,
56 cap,
57 "tfn (Test function)",
58 "\
59#[test]
60fn ${1:feature}() {
61 $0
62}",
63 )
64 .lookup_by("tfn")
65 .add_to(acc);
66
67 snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc);
68}
69
70#[cfg(test)]
71mod tests {
72 use expect_test::{expect, Expect};
73
74 use crate::completion::{test_utils::completion_list, CompletionKind};
75
76 fn check(ra_fixture: &str, expect: Expect) {
77 let actual = completion_list(ra_fixture, CompletionKind::Snippet);
78 expect.assert_eq(&actual)
79 }
80
81 #[test]
82 fn completes_snippets_in_expressions() {
83 check(
84 r#"fn foo(x: i32) { <|> }"#,
85 expect![[r#"
86 sn pd
87 sn ppd
88 "#]],
89 );
90 }
91
92 #[test]
93 fn should_not_complete_snippets_in_path() {
94 check(r#"fn foo(x: i32) { ::foo<|> }"#, expect![[""]]);
95 check(r#"fn foo(x: i32) { ::<|> }"#, expect![[""]]);
96 }
97
98 #[test]
99 fn completes_snippets_in_items() {
100 check(
101 r#"
102#[cfg(test)]
103mod tests {
104 <|>
105}
106"#,
107 expect![[r#"
108 sn macro_rules
109 sn tfn (Test function)
110 sn tmod (Test module)
111 "#]],
112 )
113 }
114}
diff --git a/crates/ide/src/completion/complete_trait_impl.rs b/crates/ide/src/completion/complete_trait_impl.rs
deleted file mode 100644
index ff115df92..000000000
--- a/crates/ide/src/completion/complete_trait_impl.rs
+++ /dev/null
@@ -1,733 +0,0 @@
1//! Completion for associated items in a trait implementation.
2//!
3//! This module adds the completion items related to implementing associated
4//! items within a `impl Trait for Struct` block. The current context node
5//! must be within either a `FN`, `TYPE_ALIAS`, or `CONST` node
6//! and an direct child of an `IMPL`.
7//!
8//! # Examples
9//!
10//! Considering the following trait `impl`:
11//!
12//! ```ignore
13//! trait SomeTrait {
14//! fn foo();
15//! }
16//!
17//! impl SomeTrait for () {
18//! fn f<|>
19//! }
20//! ```
21//!
22//! may result in the completion of the following method:
23//!
24//! ```ignore
25//! # trait SomeTrait {
26//! # fn foo();
27//! # }
28//!
29//! impl SomeTrait for () {
30//! fn foo() {}<|>
31//! }
32//! ```
33
34use assists::utils::get_missing_assoc_items;
35use hir::{self, HasAttrs, HasSource};
36use syntax::{
37 ast::{self, edit, Impl},
38 AstNode, SyntaxKind, SyntaxNode, TextRange, T,
39};
40use text_edit::TextEdit;
41
42use crate::{
43 completion::{
44 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
45 },
46 display::function_declaration,
47};
48
49#[derive(Debug, PartialEq, Eq)]
50enum ImplCompletionKind {
51 All,
52 Fn,
53 TypeAlias,
54 Const,
55}
56
57pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
58 if let Some((kind, trigger, impl_def)) = completion_match(ctx) {
59 get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item {
60 hir::AssocItem::Function(fn_item)
61 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn =>
62 {
63 add_function_impl(&trigger, acc, ctx, fn_item)
64 }
65 hir::AssocItem::TypeAlias(type_item)
66 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::TypeAlias =>
67 {
68 add_type_alias_impl(&trigger, acc, ctx, type_item)
69 }
70 hir::AssocItem::Const(const_item)
71 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Const =>
72 {
73 add_const_impl(&trigger, acc, ctx, const_item)
74 }
75 _ => {}
76 });
77 }
78}
79
80fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, SyntaxNode, Impl)> {
81 let mut token = ctx.token.clone();
82 // For keywork without name like `impl .. { fn <|> }`, the current position is inside
83 // the whitespace token, which is outside `FN` syntax node.
84 // We need to follow the previous token in this case.
85 if token.kind() == SyntaxKind::WHITESPACE {
86 token = token.prev_token()?;
87 }
88
89 let impl_item_offset = match token.kind() {
90 // `impl .. { const <|> }`
91 // ERROR 0
92 // CONST_KW <- *
93 SyntaxKind::CONST_KW => 0,
94 // `impl .. { fn/type <|> }`
95 // FN/TYPE_ALIAS 0
96 // FN_KW <- *
97 SyntaxKind::FN_KW | SyntaxKind::TYPE_KW => 0,
98 // `impl .. { fn/type/const foo<|> }`
99 // FN/TYPE_ALIAS/CONST 1
100 // NAME 0
101 // IDENT <- *
102 SyntaxKind::IDENT if token.parent().kind() == SyntaxKind::NAME => 1,
103 // `impl .. { foo<|> }`
104 // MACRO_CALL 3
105 // PATH 2
106 // PATH_SEGMENT 1
107 // NAME_REF 0
108 // IDENT <- *
109 SyntaxKind::IDENT if token.parent().kind() == SyntaxKind::NAME_REF => 3,
110 _ => return None,
111 };
112
113 let impl_item = token.ancestors().nth(impl_item_offset)?;
114 // Must directly belong to an impl block.
115 // IMPL
116 // ASSOC_ITEM_LIST
117 // <item>
118 let impl_def = ast::Impl::cast(impl_item.parent()?.parent()?)?;
119 let kind = match impl_item.kind() {
120 // `impl ... { const <|> fn/type/const }`
121 _ if token.kind() == SyntaxKind::CONST_KW => ImplCompletionKind::Const,
122 SyntaxKind::CONST | SyntaxKind::ERROR => ImplCompletionKind::Const,
123 SyntaxKind::TYPE_ALIAS => ImplCompletionKind::TypeAlias,
124 SyntaxKind::FN => ImplCompletionKind::Fn,
125 SyntaxKind::MACRO_CALL => ImplCompletionKind::All,
126 _ => return None,
127 };
128 Some((kind, impl_item, impl_def))
129}
130
131fn add_function_impl(
132 fn_def_node: &SyntaxNode,
133 acc: &mut Completions,
134 ctx: &CompletionContext,
135 func: hir::Function,
136) {
137 let fn_name = func.name(ctx.db).to_string();
138
139 let label = if func.params(ctx.db).is_empty() {
140 format!("fn {}()", fn_name)
141 } else {
142 format!("fn {}(..)", fn_name)
143 };
144
145 let builder = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
146 .lookup_by(fn_name)
147 .set_documentation(func.docs(ctx.db));
148
149 let completion_kind = if func.self_param(ctx.db).is_some() {
150 CompletionItemKind::Method
151 } else {
152 CompletionItemKind::Function
153 };
154 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end());
155
156 let function_decl = function_declaration(&func.source(ctx.db).value);
157 match ctx.config.snippet_cap {
158 Some(cap) => {
159 let snippet = format!("{} {{\n $0\n}}", function_decl);
160 builder.snippet_edit(cap, TextEdit::replace(range, snippet))
161 }
162 None => {
163 let header = format!("{} {{", function_decl);
164 builder.text_edit(TextEdit::replace(range, header))
165 }
166 }
167 .kind(completion_kind)
168 .add_to(acc);
169}
170
171fn add_type_alias_impl(
172 type_def_node: &SyntaxNode,
173 acc: &mut Completions,
174 ctx: &CompletionContext,
175 type_alias: hir::TypeAlias,
176) {
177 let alias_name = type_alias.name(ctx.db).to_string();
178
179 let snippet = format!("type {} = ", alias_name);
180
181 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end());
182
183 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
184 .text_edit(TextEdit::replace(range, snippet))
185 .lookup_by(alias_name)
186 .kind(CompletionItemKind::TypeAlias)
187 .set_documentation(type_alias.docs(ctx.db))
188 .add_to(acc);
189}
190
191fn add_const_impl(
192 const_def_node: &SyntaxNode,
193 acc: &mut Completions,
194 ctx: &CompletionContext,
195 const_: hir::Const,
196) {
197 let const_name = const_.name(ctx.db).map(|n| n.to_string());
198
199 if let Some(const_name) = const_name {
200 let snippet = make_const_compl_syntax(&const_.source(ctx.db).value);
201
202 let range = TextRange::new(const_def_node.text_range().start(), ctx.source_range().end());
203
204 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
205 .text_edit(TextEdit::replace(range, snippet))
206 .lookup_by(const_name)
207 .kind(CompletionItemKind::Const)
208 .set_documentation(const_.docs(ctx.db))
209 .add_to(acc);
210 }
211}
212
213fn make_const_compl_syntax(const_: &ast::Const) -> String {
214 let const_ = edit::remove_attrs_and_docs(const_);
215
216 let const_start = const_.syntax().text_range().start();
217 let const_end = const_.syntax().text_range().end();
218
219 let start =
220 const_.syntax().first_child_or_token().map_or(const_start, |f| f.text_range().start());
221
222 let end = const_
223 .syntax()
224 .children_with_tokens()
225 .find(|s| s.kind() == T![;] || s.kind() == T![=])
226 .map_or(const_end, |f| f.text_range().start());
227
228 let len = end - start;
229 let range = TextRange::new(0.into(), len);
230
231 let syntax = const_.syntax().text().slice(range).to_string();
232
233 format!("{} = ", syntax.trim_end())
234}
235
236#[cfg(test)]
237mod tests {
238 use expect_test::{expect, Expect};
239
240 use crate::completion::{
241 test_utils::{check_edit, completion_list},
242 CompletionKind,
243 };
244
245 fn check(ra_fixture: &str, expect: Expect) {
246 let actual = completion_list(ra_fixture, CompletionKind::Magic);
247 expect.assert_eq(&actual)
248 }
249
250 #[test]
251 fn name_ref_function_type_const() {
252 check(
253 r#"
254trait Test {
255 type TestType;
256 const TEST_CONST: u16;
257 fn test();
258}
259struct T;
260
261impl Test for T {
262 t<|>
263}
264"#,
265 expect![["
266ct const TEST_CONST: u16 = \n\
267fn fn test()
268ta type TestType = \n\
269 "]],
270 );
271 }
272
273 #[test]
274 fn no_completion_inside_fn() {
275 check(
276 r"
277trait Test { fn test(); fn test2(); }
278struct T;
279
280impl Test for T {
281 fn test() {
282 t<|>
283 }
284}
285",
286 expect![[""]],
287 );
288
289 check(
290 r"
291trait Test { fn test(); fn test2(); }
292struct T;
293
294impl Test for T {
295 fn test() {
296 fn t<|>
297 }
298}
299",
300 expect![[""]],
301 );
302
303 check(
304 r"
305trait Test { fn test(); fn test2(); }
306struct T;
307
308impl Test for T {
309 fn test() {
310 fn <|>
311 }
312}
313",
314 expect![[""]],
315 );
316
317 // https://github.com/rust-analyzer/rust-analyzer/pull/5976#issuecomment-692332191
318 check(
319 r"
320trait Test { fn test(); fn test2(); }
321struct T;
322
323impl Test for T {
324 fn test() {
325 foo.<|>
326 }
327}
328",
329 expect![[""]],
330 );
331
332 check(
333 r"
334trait Test { fn test(_: i32); fn test2(); }
335struct T;
336
337impl Test for T {
338 fn test(t<|>)
339}
340",
341 expect![[""]],
342 );
343
344 check(
345 r"
346trait Test { fn test(_: fn()); fn test2(); }
347struct T;
348
349impl Test for T {
350 fn test(f: fn <|>)
351}
352",
353 expect![[""]],
354 );
355 }
356
357 #[test]
358 fn no_completion_inside_const() {
359 check(
360 r"
361trait Test { const TEST: fn(); const TEST2: u32; type Test; fn test(); }
362struct T;
363
364impl Test for T {
365 const TEST: fn <|>
366}
367",
368 expect![[""]],
369 );
370
371 check(
372 r"
373trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
374struct T;
375
376impl Test for T {
377 const TEST: T<|>
378}
379",
380 expect![[""]],
381 );
382
383 check(
384 r"
385trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
386struct T;
387
388impl Test for T {
389 const TEST: u32 = f<|>
390}
391",
392 expect![[""]],
393 );
394
395 check(
396 r"
397trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
398struct T;
399
400impl Test for T {
401 const TEST: u32 = {
402 t<|>
403 };
404}
405",
406 expect![[""]],
407 );
408
409 check(
410 r"
411trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
412struct T;
413
414impl Test for T {
415 const TEST: u32 = {
416 fn <|>
417 };
418}
419",
420 expect![[""]],
421 );
422
423 check(
424 r"
425trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
426struct T;
427
428impl Test for T {
429 const TEST: u32 = {
430 fn t<|>
431 };
432}
433",
434 expect![[""]],
435 );
436 }
437
438 #[test]
439 fn no_completion_inside_type() {
440 check(
441 r"
442trait Test { type Test; type Test2; fn test(); }
443struct T;
444
445impl Test for T {
446 type Test = T<|>;
447}
448",
449 expect![[""]],
450 );
451
452 check(
453 r"
454trait Test { type Test; type Test2; fn test(); }
455struct T;
456
457impl Test for T {
458 type Test = fn <|>;
459}
460",
461 expect![[""]],
462 );
463 }
464
465 #[test]
466 fn name_ref_single_function() {
467 check_edit(
468 "test",
469 r#"
470trait Test {
471 fn test();
472}
473struct T;
474
475impl Test for T {
476 t<|>
477}
478"#,
479 r#"
480trait Test {
481 fn test();
482}
483struct T;
484
485impl Test for T {
486 fn test() {
487 $0
488}
489}
490"#,
491 );
492 }
493
494 #[test]
495 fn single_function() {
496 check_edit(
497 "test",
498 r#"
499trait Test {
500 fn test();
501}
502struct T;
503
504impl Test for T {
505 fn t<|>
506}
507"#,
508 r#"
509trait Test {
510 fn test();
511}
512struct T;
513
514impl Test for T {
515 fn test() {
516 $0
517}
518}
519"#,
520 );
521 }
522
523 #[test]
524 fn hide_implemented_fn() {
525 check(
526 r#"
527trait Test {
528 fn foo();
529 fn foo_bar();
530}
531struct T;
532
533impl Test for T {
534 fn foo() {}
535 fn f<|>
536}
537"#,
538 expect![[r#"
539 fn fn foo_bar()
540 "#]],
541 );
542 }
543
544 #[test]
545 fn generic_fn() {
546 check_edit(
547 "foo",
548 r#"
549trait Test {
550 fn foo<T>();
551}
552struct T;
553
554impl Test for T {
555 fn f<|>
556}
557"#,
558 r#"
559trait Test {
560 fn foo<T>();
561}
562struct T;
563
564impl Test for T {
565 fn foo<T>() {
566 $0
567}
568}
569"#,
570 );
571 check_edit(
572 "foo",
573 r#"
574trait Test {
575 fn foo<T>() where T: Into<String>;
576}
577struct T;
578
579impl Test for T {
580 fn f<|>
581}
582"#,
583 r#"
584trait Test {
585 fn foo<T>() where T: Into<String>;
586}
587struct T;
588
589impl Test for T {
590 fn foo<T>()
591where T: Into<String> {
592 $0
593}
594}
595"#,
596 );
597 }
598
599 #[test]
600 fn associated_type() {
601 check_edit(
602 "SomeType",
603 r#"
604trait Test {
605 type SomeType;
606}
607
608impl Test for () {
609 type S<|>
610}
611"#,
612 "
613trait Test {
614 type SomeType;
615}
616
617impl Test for () {
618 type SomeType = \n\
619}
620",
621 );
622 }
623
624 #[test]
625 fn associated_const() {
626 check_edit(
627 "SOME_CONST",
628 r#"
629trait Test {
630 const SOME_CONST: u16;
631}
632
633impl Test for () {
634 const S<|>
635}
636"#,
637 "
638trait Test {
639 const SOME_CONST: u16;
640}
641
642impl Test for () {
643 const SOME_CONST: u16 = \n\
644}
645",
646 );
647
648 check_edit(
649 "SOME_CONST",
650 r#"
651trait Test {
652 const SOME_CONST: u16 = 92;
653}
654
655impl Test for () {
656 const S<|>
657}
658"#,
659 "
660trait Test {
661 const SOME_CONST: u16 = 92;
662}
663
664impl Test for () {
665 const SOME_CONST: u16 = \n\
666}
667",
668 );
669 }
670
671 #[test]
672 fn complete_without_name() {
673 let test = |completion: &str, hint: &str, completed: &str, next_sibling: &str| {
674 println!(
675 "completion='{}', hint='{}', next_sibling='{}'",
676 completion, hint, next_sibling
677 );
678
679 check_edit(
680 completion,
681 &format!(
682 r#"
683trait Test {{
684 type Foo;
685 const CONST: u16;
686 fn bar();
687}}
688struct T;
689
690impl Test for T {{
691 {}
692 {}
693}}
694"#,
695 hint, next_sibling
696 ),
697 &format!(
698 r#"
699trait Test {{
700 type Foo;
701 const CONST: u16;
702 fn bar();
703}}
704struct T;
705
706impl Test for T {{
707 {}
708 {}
709}}
710"#,
711 completed, next_sibling
712 ),
713 )
714 };
715
716 // Enumerate some possible next siblings.
717 for next_sibling in &[
718 "",
719 "fn other_fn() {}", // `const <|> fn` -> `const fn`
720 "type OtherType = i32;",
721 "const OTHER_CONST: i32 = 0;",
722 "async fn other_fn() {}",
723 "unsafe fn other_fn() {}",
724 "default fn other_fn() {}",
725 "default type OtherType = i32;",
726 "default const OTHER_CONST: i32 = 0;",
727 ] {
728 test("bar", "fn <|>", "fn bar() {\n $0\n}", next_sibling);
729 test("Foo", "type <|>", "type Foo = ", next_sibling);
730 test("CONST", "const <|>", "const CONST: u16 = ", next_sibling);
731 }
732 }
733}
diff --git a/crates/ide/src/completion/complete_unqualified_path.rs b/crates/ide/src/completion/complete_unqualified_path.rs
deleted file mode 100644
index 8b6757195..000000000
--- a/crates/ide/src/completion/complete_unqualified_path.rs
+++ /dev/null
@@ -1,679 +0,0 @@
1//! Completion of names from the current scope, e.g. locals and imported items.
2
3use hir::{Adt, ModuleDef, ScopeDef, Type};
4use syntax::AstNode;
5use test_utils::mark;
6
7use crate::completion::{CompletionContext, Completions};
8
9pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
10 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
11 return;
12 }
13 if ctx.record_lit_syntax.is_some()
14 || ctx.record_pat_syntax.is_some()
15 || ctx.attribute_under_caret.is_some()
16 || ctx.mod_declaration_under_caret.is_some()
17 {
18 return;
19 }
20
21 if let Some(ty) = &ctx.expected_type {
22 complete_enum_variants(acc, ctx, ty);
23 }
24
25 if ctx.is_pat_binding_or_const {
26 return;
27 }
28
29 ctx.scope.process_all_names(&mut |name, res| {
30 if ctx.use_item_syntax.is_some() {
31 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
32 if name_ref.syntax().text() == name.to_string().as_str() {
33 mark::hit!(self_fulfilling_completion);
34 return;
35 }
36 }
37 }
38 acc.add_resolution(ctx, name.to_string(), &res)
39 });
40}
41
42fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
43 if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
44 let variants = enum_data.variants(ctx.db);
45
46 let module = if let Some(module) = ctx.scope.module() {
47 // Compute path from the completion site if available.
48 module
49 } else {
50 // Otherwise fall back to the enum's definition site.
51 enum_data.module(ctx.db)
52 };
53
54 for variant in variants {
55 if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) {
56 // Variants with trivial paths are already added by the existing completion logic,
57 // so we should avoid adding these twice
58 if path.segments.len() > 1 {
59 acc.add_qualified_enum_variant(ctx, variant, path);
60 }
61 }
62 }
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use expect_test::{expect, Expect};
69 use test_utils::mark;
70
71 use crate::completion::{
72 test_utils::{check_edit, completion_list},
73 CompletionKind,
74 };
75
76 fn check(ra_fixture: &str, expect: Expect) {
77 let actual = completion_list(ra_fixture, CompletionKind::Reference);
78 expect.assert_eq(&actual)
79 }
80
81 #[test]
82 fn self_fulfilling_completion() {
83 mark::check!(self_fulfilling_completion);
84 check(
85 r#"
86use foo<|>
87use std::collections;
88"#,
89 expect![[r#"
90 ?? collections
91 "#]],
92 );
93 }
94
95 #[test]
96 fn bind_pat_and_path_ignore_at() {
97 check(
98 r#"
99enum Enum { A, B }
100fn quux(x: Option<Enum>) {
101 match x {
102 None => (),
103 Some(en<|> @ Enum::A) => (),
104 }
105}
106"#,
107 expect![[""]],
108 );
109 }
110
111 #[test]
112 fn bind_pat_and_path_ignore_ref() {
113 check(
114 r#"
115enum Enum { A, B }
116fn quux(x: Option<Enum>) {
117 match x {
118 None => (),
119 Some(ref en<|>) => (),
120 }
121}
122"#,
123 expect![[""]],
124 );
125 }
126
127 #[test]
128 fn bind_pat_and_path() {
129 check(
130 r#"
131enum Enum { A, B }
132fn quux(x: Option<Enum>) {
133 match x {
134 None => (),
135 Some(En<|>) => (),
136 }
137}
138"#,
139 expect![[r#"
140 en Enum
141 "#]],
142 );
143 }
144
145 #[test]
146 fn completes_bindings_from_let() {
147 check(
148 r#"
149fn quux(x: i32) {
150 let y = 92;
151 1 + <|>;
152 let z = ();
153}
154"#,
155 expect![[r#"
156 fn quux(…) fn quux(x: i32)
157 bn x i32
158 bn y i32
159 "#]],
160 );
161 }
162
163 #[test]
164 fn completes_bindings_from_if_let() {
165 check(
166 r#"
167fn quux() {
168 if let Some(x) = foo() {
169 let y = 92;
170 };
171 if let Some(a) = bar() {
172 let b = 62;
173 1 + <|>
174 }
175}
176"#,
177 expect![[r#"
178 bn a
179 bn b i32
180 fn quux() fn quux()
181 "#]],
182 );
183 }
184
185 #[test]
186 fn completes_bindings_from_for() {
187 check(
188 r#"
189fn quux() {
190 for x in &[1, 2, 3] { <|> }
191}
192"#,
193 expect![[r#"
194 fn quux() fn quux()
195 bn x
196 "#]],
197 );
198 }
199
200 #[test]
201 fn completes_if_prefix_is_keyword() {
202 mark::check!(completes_if_prefix_is_keyword);
203 check_edit(
204 "wherewolf",
205 r#"
206fn main() {
207 let wherewolf = 92;
208 drop(where<|>)
209}
210"#,
211 r#"
212fn main() {
213 let wherewolf = 92;
214 drop(wherewolf)
215}
216"#,
217 )
218 }
219
220 #[test]
221 fn completes_generic_params() {
222 check(
223 r#"fn quux<T>() { <|> }"#,
224 expect![[r#"
225 tp T
226 fn quux() fn quux<T>()
227 "#]],
228 );
229 }
230
231 #[test]
232 fn completes_generic_params_in_struct() {
233 check(
234 r#"struct S<T> { x: <|>}"#,
235 expect![[r#"
236 st S<…>
237 tp Self
238 tp T
239 "#]],
240 );
241 }
242
243 #[test]
244 fn completes_self_in_enum() {
245 check(
246 r#"enum X { Y(<|>) }"#,
247 expect![[r#"
248 tp Self
249 en X
250 "#]],
251 );
252 }
253
254 #[test]
255 fn completes_module_items() {
256 check(
257 r#"
258struct S;
259enum E {}
260fn quux() { <|> }
261"#,
262 expect![[r#"
263 en E
264 st S
265 fn quux() fn quux()
266 "#]],
267 );
268 }
269
270 /// Regression test for issue #6091.
271 #[test]
272 fn correctly_completes_module_items_prefixed_with_underscore() {
273 check_edit(
274 "_alpha",
275 r#"
276fn main() {
277 _<|>
278}
279fn _alpha() {}
280"#,
281 r#"
282fn main() {
283 _alpha()$0
284}
285fn _alpha() {}
286"#,
287 )
288 }
289
290 #[test]
291 fn completes_extern_prelude() {
292 check(
293 r#"
294//- /lib.rs crate:main deps:other_crate
295use <|>;
296
297//- /other_crate/lib.rs crate:other_crate
298// nothing here
299"#,
300 expect![[r#"
301 md other_crate
302 "#]],
303 );
304 }
305
306 #[test]
307 fn completes_module_items_in_nested_modules() {
308 check(
309 r#"
310struct Foo;
311mod m {
312 struct Bar;
313 fn quux() { <|> }
314}
315"#,
316 expect![[r#"
317 st Bar
318 fn quux() fn quux()
319 "#]],
320 );
321 }
322
323 #[test]
324 fn completes_return_type() {
325 check(
326 r#"
327struct Foo;
328fn x() -> <|>
329"#,
330 expect![[r#"
331 st Foo
332 fn x() fn x()
333 "#]],
334 );
335 }
336
337 #[test]
338 fn dont_show_both_completions_for_shadowing() {
339 check(
340 r#"
341fn foo() {
342 let bar = 92;
343 {
344 let bar = 62;
345 drop(<|>)
346 }
347}
348"#,
349 // FIXME: should be only one bar here
350 expect![[r#"
351 bn bar i32
352 bn bar i32
353 fn foo() fn foo()
354 "#]],
355 );
356 }
357
358 #[test]
359 fn completes_self_in_methods() {
360 check(
361 r#"impl S { fn foo(&self) { <|> } }"#,
362 expect![[r#"
363 tp Self
364 bn self &{unknown}
365 "#]],
366 );
367 }
368
369 #[test]
370 fn completes_prelude() {
371 check(
372 r#"
373//- /main.rs crate:main deps:std
374fn foo() { let x: <|> }
375
376//- /std/lib.rs crate:std
377#[prelude_import]
378use prelude::*;
379
380mod prelude { struct Option; }
381"#,
382 expect![[r#"
383 st Option
384 fn foo() fn foo()
385 md std
386 "#]],
387 );
388 }
389
390 #[test]
391 fn completes_std_prelude_if_core_is_defined() {
392 check(
393 r#"
394//- /main.rs crate:main deps:core,std
395fn foo() { let x: <|> }
396
397//- /core/lib.rs crate:core
398#[prelude_import]
399use prelude::*;
400
401mod prelude { struct Option; }
402
403//- /std/lib.rs crate:std deps:core
404#[prelude_import]
405use prelude::*;
406
407mod prelude { struct String; }
408"#,
409 expect![[r#"
410 st String
411 md core
412 fn foo() fn foo()
413 md std
414 "#]],
415 );
416 }
417
418 #[test]
419 fn completes_macros_as_value() {
420 check(
421 r#"
422macro_rules! foo { () => {} }
423
424#[macro_use]
425mod m1 {
426 macro_rules! bar { () => {} }
427}
428
429mod m2 {
430 macro_rules! nope { () => {} }
431
432 #[macro_export]
433 macro_rules! baz { () => {} }
434}
435
436fn main() { let v = <|> }
437"#,
438 expect![[r##"
439 ma bar!(…) macro_rules! bar
440 ma baz!(…) #[macro_export]
441 macro_rules! baz
442 ma foo!(…) macro_rules! foo
443 md m1
444 md m2
445 fn main() fn main()
446 "##]],
447 );
448 }
449
450 #[test]
451 fn completes_both_macro_and_value() {
452 check(
453 r#"
454macro_rules! foo { () => {} }
455fn foo() { <|> }
456"#,
457 expect![[r#"
458 ma foo!(…) macro_rules! foo
459 fn foo() fn foo()
460 "#]],
461 );
462 }
463
464 #[test]
465 fn completes_macros_as_type() {
466 check(
467 r#"
468macro_rules! foo { () => {} }
469fn main() { let x: <|> }
470"#,
471 expect![[r#"
472 ma foo!(…) macro_rules! foo
473 fn main() fn main()
474 "#]],
475 );
476 }
477
478 #[test]
479 fn completes_macros_as_stmt() {
480 check(
481 r#"
482macro_rules! foo { () => {} }
483fn main() { <|> }
484"#,
485 expect![[r#"
486 ma foo!(…) macro_rules! foo
487 fn main() fn main()
488 "#]],
489 );
490 }
491
492 #[test]
493 fn completes_local_item() {
494 check(
495 r#"
496fn main() {
497 return f<|>;
498 fn frobnicate() {}
499}
500"#,
501 expect![[r#"
502 fn frobnicate() fn frobnicate()
503 fn main() fn main()
504 "#]],
505 );
506 }
507
508 #[test]
509 fn completes_in_simple_macro_1() {
510 check(
511 r#"
512macro_rules! m { ($e:expr) => { $e } }
513fn quux(x: i32) {
514 let y = 92;
515 m!(<|>);
516}
517"#,
518 expect![[r#"
519 ma m!(…) macro_rules! m
520 fn quux(…) fn quux(x: i32)
521 bn x i32
522 bn y i32
523 "#]],
524 );
525 }
526
527 #[test]
528 fn completes_in_simple_macro_2() {
529 check(
530 r"
531macro_rules! m { ($e:expr) => { $e } }
532fn quux(x: i32) {
533 let y = 92;
534 m!(x<|>);
535}
536",
537 expect![[r#"
538 ma m!(…) macro_rules! m
539 fn quux(…) fn quux(x: i32)
540 bn x i32
541 bn y i32
542 "#]],
543 );
544 }
545
546 #[test]
547 fn completes_in_simple_macro_without_closing_parens() {
548 check(
549 r#"
550macro_rules! m { ($e:expr) => { $e } }
551fn quux(x: i32) {
552 let y = 92;
553 m!(x<|>
554}
555"#,
556 expect![[r#"
557 ma m!(…) macro_rules! m
558 fn quux(…) fn quux(x: i32)
559 bn x i32
560 bn y i32
561 "#]],
562 );
563 }
564
565 #[test]
566 fn completes_unresolved_uses() {
567 check(
568 r#"
569use spam::Quux;
570
571fn main() { <|> }
572"#,
573 expect![[r#"
574 ?? Quux
575 fn main() fn main()
576 "#]],
577 );
578 }
579 #[test]
580 fn completes_enum_variant_matcharm() {
581 check(
582 r#"
583enum Foo { Bar, Baz, Quux }
584
585fn main() {
586 let foo = Foo::Quux;
587 match foo { Qu<|> }
588}
589"#,
590 expect![[r#"
591 en Foo
592 ev Foo::Bar ()
593 ev Foo::Baz ()
594 ev Foo::Quux ()
595 "#]],
596 )
597 }
598
599 #[test]
600 fn completes_enum_variant_iflet() {
601 check(
602 r#"
603enum Foo { Bar, Baz, Quux }
604
605fn main() {
606 let foo = Foo::Quux;
607 if let Qu<|> = foo { }
608}
609"#,
610 expect![[r#"
611 en Foo
612 ev Foo::Bar ()
613 ev Foo::Baz ()
614 ev Foo::Quux ()
615 "#]],
616 )
617 }
618
619 #[test]
620 fn completes_enum_variant_basic_expr() {
621 check(
622 r#"
623enum Foo { Bar, Baz, Quux }
624fn main() { let foo: Foo = Q<|> }
625"#,
626 expect![[r#"
627 en Foo
628 ev Foo::Bar ()
629 ev Foo::Baz ()
630 ev Foo::Quux ()
631 fn main() fn main()
632 "#]],
633 )
634 }
635
636 #[test]
637 fn completes_enum_variant_from_module() {
638 check(
639 r#"
640mod m { pub enum E { V } }
641fn f() -> m::E { V<|> }
642"#,
643 expect![[r#"
644 fn f() fn f() -> m::E
645 md m
646 ev m::E::V ()
647 "#]],
648 )
649 }
650
651 #[test]
652 fn dont_complete_attr() {
653 check(
654 r#"
655struct Foo;
656#[<|>]
657fn f() {}
658"#,
659 expect![[""]],
660 )
661 }
662
663 #[test]
664 fn completes_type_or_trait_in_impl_block() {
665 check(
666 r#"
667trait MyTrait {}
668struct MyStruct {}
669
670impl My<|>
671"#,
672 expect![[r#"
673 st MyStruct
674 tt MyTrait
675 tp Self
676 "#]],
677 )
678 }
679}
diff --git a/crates/ide/src/completion/completion_config.rs b/crates/ide/src/completion/completion_config.rs
deleted file mode 100644
index 71b49ace8..000000000
--- a/crates/ide/src/completion/completion_config.rs
+++ /dev/null
@@ -1,35 +0,0 @@
1//! Settings for tweaking completion.
2//!
3//! The fun thing here is `SnippetCap` -- this type can only be created in this
4//! module, and we use to statically check that we only produce snippet
5//! completions if we are allowed to.
6
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct CompletionConfig {
9 pub enable_postfix_completions: bool,
10 pub add_call_parenthesis: bool,
11 pub add_call_argument_snippets: bool,
12 pub snippet_cap: Option<SnippetCap>,
13}
14
15impl CompletionConfig {
16 pub fn allow_snippets(&mut self, yes: bool) {
17 self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None }
18 }
19}
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub struct SnippetCap {
23 _private: (),
24}
25
26impl Default for CompletionConfig {
27 fn default() -> Self {
28 CompletionConfig {
29 enable_postfix_completions: true,
30 add_call_parenthesis: true,
31 add_call_argument_snippets: true,
32 snippet_cap: Some(SnippetCap { _private: () }),
33 }
34 }
35}
diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs
deleted file mode 100644
index d9f90477c..000000000
--- a/crates/ide/src/completion/completion_context.rs
+++ /dev/null
@@ -1,523 +0,0 @@
1//! FIXME: write short doc here
2
3use base_db::SourceDatabase;
4use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type};
5use ide_db::RootDatabase;
6use syntax::{
7 algo::{find_covering_element, find_node_at_offset},
8 ast, match_ast, AstNode, NodeOrToken,
9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextSize,
11};
12use test_utils::mark;
13use text_edit::Indel;
14
15use crate::{
16 call_info::ActiveParameter,
17 completion::{
18 patterns::{
19 fn_is_prev, for_is_prev2, has_bind_pat_parent, has_block_expr_parent,
20 has_field_list_parent, has_impl_as_prev_sibling, has_impl_parent,
21 has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling,
22 has_trait_parent, if_is_prev, inside_impl_trait_block, is_in_loop_body, is_match_arm,
23 unsafe_is_prev,
24 },
25 CompletionConfig,
26 },
27 FilePosition,
28};
29
30/// `CompletionContext` is created early during completion to figure out, where
31/// exactly is the cursor, syntax-wise.
32#[derive(Debug)]
33pub(crate) struct CompletionContext<'a> {
34 pub(super) sema: Semantics<'a, RootDatabase>,
35 pub(super) scope: SemanticsScope<'a>,
36 pub(super) db: &'a RootDatabase,
37 pub(super) config: &'a CompletionConfig,
38 pub(super) position: FilePosition,
39 /// The token before the cursor, in the original file.
40 pub(super) original_token: SyntaxToken,
41 /// The token before the cursor, in the macro-expanded file.
42 pub(super) token: SyntaxToken,
43 pub(super) krate: Option<hir::Crate>,
44 pub(super) expected_type: Option<Type>,
45 pub(super) name_ref_syntax: Option<ast::NameRef>,
46 pub(super) function_syntax: Option<ast::Fn>,
47 pub(super) use_item_syntax: Option<ast::Use>,
48 pub(super) record_lit_syntax: Option<ast::RecordExpr>,
49 pub(super) record_pat_syntax: Option<ast::RecordPat>,
50 pub(super) record_field_syntax: Option<ast::RecordExprField>,
51 pub(super) impl_def: Option<ast::Impl>,
52 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
53 pub(super) active_parameter: Option<ActiveParameter>,
54 pub(super) is_param: bool,
55 /// If a name-binding or reference to a const in a pattern.
56 /// Irrefutable patterns (like let) are excluded.
57 pub(super) is_pat_binding_or_const: bool,
58 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
59 pub(super) is_trivial_path: bool,
60 /// If not a trivial path, the prefix (qualifier).
61 pub(super) path_qual: Option<ast::Path>,
62 pub(super) after_if: bool,
63 /// `true` if we are a statement or a last expr in the block.
64 pub(super) can_be_stmt: bool,
65 /// `true` if we expect an expression at the cursor position.
66 pub(super) is_expr: bool,
67 /// Something is typed at the "top" level, in module or impl/trait.
68 pub(super) is_new_item: bool,
69 /// The receiver if this is a field or method access, i.e. writing something.<|>
70 pub(super) dot_receiver: Option<ast::Expr>,
71 pub(super) dot_receiver_is_ambiguous_float_literal: bool,
72 /// If this is a call (method or function) in particular, i.e. the () are already there.
73 pub(super) is_call: bool,
74 /// Like `is_call`, but for tuple patterns.
75 pub(super) is_pattern_call: bool,
76 /// If this is a macro call, i.e. the () are already there.
77 pub(super) is_macro_call: bool,
78 pub(super) is_path_type: bool,
79 pub(super) has_type_args: bool,
80 pub(super) attribute_under_caret: Option<ast::Attr>,
81 pub(super) mod_declaration_under_caret: Option<ast::Module>,
82 pub(super) unsafe_is_prev: bool,
83 pub(super) if_is_prev: bool,
84 pub(super) block_expr_parent: bool,
85 pub(super) bind_pat_parent: bool,
86 pub(super) ref_pat_parent: bool,
87 pub(super) in_loop_body: bool,
88 pub(super) has_trait_parent: bool,
89 pub(super) has_impl_parent: bool,
90 pub(super) inside_impl_trait_block: bool,
91 pub(super) has_field_list_parent: bool,
92 pub(super) trait_as_prev_sibling: bool,
93 pub(super) impl_as_prev_sibling: bool,
94 pub(super) is_match_arm: bool,
95 pub(super) has_item_list_or_source_file_parent: bool,
96 pub(super) for_is_prev2: bool,
97 pub(super) fn_is_prev: bool,
98 pub(super) locals: Vec<(String, Local)>,
99}
100
101impl<'a> CompletionContext<'a> {
102 pub(super) fn new(
103 db: &'a RootDatabase,
104 position: FilePosition,
105 config: &'a CompletionConfig,
106 ) -> Option<CompletionContext<'a>> {
107 let sema = Semantics::new(db);
108
109 let original_file = sema.parse(position.file_id);
110
111 // Insert a fake ident to get a valid parse tree. We will use this file
112 // to determine context, though the original_file will be used for
113 // actual completion.
114 let file_with_fake_ident = {
115 let parse = db.parse(position.file_id);
116 let edit = Indel::insert(position.offset, "intellijRulezz".to_string());
117 parse.reparse(&edit).tree()
118 };
119 let fake_ident_token =
120 file_with_fake_ident.syntax().token_at_offset(position.offset).right_biased().unwrap();
121
122 let krate = sema.to_module_def(position.file_id).map(|m| m.krate());
123 let original_token =
124 original_file.syntax().token_at_offset(position.offset).left_biased()?;
125 let token = sema.descend_into_macros(original_token.clone());
126 let scope = sema.scope_at_offset(&token.parent(), position.offset);
127 let mut locals = vec![];
128 scope.process_all_names(&mut |name, scope| {
129 if let ScopeDef::Local(local) = scope {
130 locals.push((name.to_string(), local));
131 }
132 });
133 let mut ctx = CompletionContext {
134 sema,
135 scope,
136 db,
137 config,
138 original_token,
139 token,
140 position,
141 krate,
142 expected_type: None,
143 name_ref_syntax: None,
144 function_syntax: None,
145 use_item_syntax: None,
146 record_lit_syntax: None,
147 record_pat_syntax: None,
148 record_field_syntax: None,
149 impl_def: None,
150 active_parameter: ActiveParameter::at(db, position),
151 is_param: false,
152 is_pat_binding_or_const: false,
153 is_trivial_path: false,
154 path_qual: None,
155 after_if: false,
156 can_be_stmt: false,
157 is_expr: false,
158 is_new_item: false,
159 dot_receiver: None,
160 is_call: false,
161 is_pattern_call: false,
162 is_macro_call: false,
163 is_path_type: false,
164 has_type_args: false,
165 dot_receiver_is_ambiguous_float_literal: false,
166 attribute_under_caret: None,
167 mod_declaration_under_caret: None,
168 unsafe_is_prev: false,
169 in_loop_body: false,
170 ref_pat_parent: false,
171 bind_pat_parent: false,
172 block_expr_parent: false,
173 has_trait_parent: false,
174 has_impl_parent: false,
175 inside_impl_trait_block: false,
176 has_field_list_parent: false,
177 trait_as_prev_sibling: false,
178 impl_as_prev_sibling: false,
179 if_is_prev: false,
180 is_match_arm: false,
181 has_item_list_or_source_file_parent: false,
182 for_is_prev2: false,
183 fn_is_prev: false,
184 locals,
185 };
186
187 let mut original_file = original_file.syntax().clone();
188 let mut hypothetical_file = file_with_fake_ident.syntax().clone();
189 let mut offset = position.offset;
190 let mut fake_ident_token = fake_ident_token;
191
192 // Are we inside a macro call?
193 while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
194 find_node_at_offset::<ast::MacroCall>(&original_file, offset),
195 find_node_at_offset::<ast::MacroCall>(&hypothetical_file, offset),
196 ) {
197 if actual_macro_call.path().as_ref().map(|s| s.syntax().text())
198 != macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text())
199 {
200 break;
201 }
202 let hypothetical_args = match macro_call_with_fake_ident.token_tree() {
203 Some(tt) => tt,
204 None => break,
205 };
206 if let (Some(actual_expansion), Some(hypothetical_expansion)) = (
207 ctx.sema.expand(&actual_macro_call),
208 ctx.sema.speculative_expand(
209 &actual_macro_call,
210 &hypothetical_args,
211 fake_ident_token,
212 ),
213 ) {
214 let new_offset = hypothetical_expansion.1.text_range().start();
215 if new_offset > actual_expansion.text_range().end() {
216 break;
217 }
218 original_file = actual_expansion;
219 hypothetical_file = hypothetical_expansion.0;
220 fake_ident_token = hypothetical_expansion.1;
221 offset = new_offset;
222 } else {
223 break;
224 }
225 }
226 ctx.fill_keyword_patterns(&hypothetical_file, offset);
227 ctx.fill(&original_file, hypothetical_file, offset);
228 Some(ctx)
229 }
230
231 /// Checks whether completions in that particular case don't make much sense.
232 /// Examples:
233 /// - `fn <|>` -- we expect function name, it's unlikely that "hint" will be helpful.
234 /// Exception for this case is `impl Trait for Foo`, where we would like to hint trait method names.
235 /// - `for _ i<|>` -- obviously, it'll be "in" keyword.
236 pub(crate) fn no_completion_required(&self) -> bool {
237 (self.fn_is_prev && !self.inside_impl_trait_block) || self.for_is_prev2
238 }
239
240 /// The range of the identifier that is being completed.
241 pub(crate) fn source_range(&self) -> TextRange {
242 // check kind of macro-expanded token, but use range of original token
243 let kind = self.token.kind();
244 if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() {
245 mark::hit!(completes_if_prefix_is_keyword);
246 self.original_token.text_range()
247 } else {
248 TextRange::empty(self.position.offset)
249 }
250 }
251
252 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
253 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
254 let syntax_element = NodeOrToken::Token(fake_ident_token);
255 self.block_expr_parent = has_block_expr_parent(syntax_element.clone());
256 self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone());
257 self.if_is_prev = if_is_prev(syntax_element.clone());
258 self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone());
259 self.ref_pat_parent = has_ref_parent(syntax_element.clone());
260 self.in_loop_body = is_in_loop_body(syntax_element.clone());
261 self.has_trait_parent = has_trait_parent(syntax_element.clone());
262 self.has_impl_parent = has_impl_parent(syntax_element.clone());
263 self.inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone());
264 self.has_field_list_parent = has_field_list_parent(syntax_element.clone());
265 self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone());
266 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone());
267 self.is_match_arm = is_match_arm(syntax_element.clone());
268 self.has_item_list_or_source_file_parent =
269 has_item_list_or_source_file_parent(syntax_element.clone());
270 self.mod_declaration_under_caret =
271 find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset)
272 .filter(|module| module.item_list().is_none());
273 self.for_is_prev2 = for_is_prev2(syntax_element.clone());
274 self.fn_is_prev = fn_is_prev(syntax_element.clone());
275 }
276
277 fn fill(
278 &mut self,
279 original_file: &SyntaxNode,
280 file_with_fake_ident: SyntaxNode,
281 offset: TextSize,
282 ) {
283 // FIXME: this is wrong in at least two cases:
284 // * when there's no token `foo(<|>)`
285 // * when there is a token, but it happens to have type of it's own
286 self.expected_type = self
287 .token
288 .ancestors()
289 .find_map(|node| {
290 let ty = match_ast! {
291 match node {
292 ast::Pat(it) => self.sema.type_of_pat(&it),
293 ast::Expr(it) => self.sema.type_of_expr(&it),
294 _ => return None,
295 }
296 };
297 Some(ty)
298 })
299 .flatten();
300 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
301
302 // First, let's try to complete a reference to some declaration.
303 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
304 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
305 // See RFC#1685.
306 if is_node::<ast::Param>(name_ref.syntax()) {
307 self.is_param = true;
308 return;
309 }
310 // FIXME: remove this (V) duplication and make the check more precise
311 if name_ref.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() {
312 self.record_pat_syntax =
313 self.sema.find_node_at_offset_with_macros(&original_file, offset);
314 }
315 self.classify_name_ref(original_file, name_ref, offset);
316 }
317
318 // Otherwise, see if this is a declaration. We can use heuristics to
319 // suggest declaration names, see `CompletionKind::Magic`.
320 if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) {
321 if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::IdentPat::cast) {
322 self.is_pat_binding_or_const = true;
323 if bind_pat.at_token().is_some()
324 || bind_pat.ref_token().is_some()
325 || bind_pat.mut_token().is_some()
326 {
327 self.is_pat_binding_or_const = false;
328 }
329 if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() {
330 self.is_pat_binding_or_const = false;
331 }
332 if let Some(let_stmt) = bind_pat.syntax().ancestors().find_map(ast::LetStmt::cast) {
333 if let Some(pat) = let_stmt.pat() {
334 if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range())
335 {
336 self.is_pat_binding_or_const = false;
337 }
338 }
339 }
340 }
341 if is_node::<ast::Param>(name.syntax()) {
342 self.is_param = true;
343 return;
344 }
345 // FIXME: remove this (^) duplication and make the check more precise
346 if name.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() {
347 self.record_pat_syntax =
348 self.sema.find_node_at_offset_with_macros(&original_file, offset);
349 }
350 }
351 }
352
353 fn classify_name_ref(
354 &mut self,
355 original_file: &SyntaxNode,
356 name_ref: ast::NameRef,
357 offset: TextSize,
358 ) {
359 self.name_ref_syntax =
360 find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
361 let name_range = name_ref.syntax().text_range();
362 if ast::RecordExprField::for_field_name(&name_ref).is_some() {
363 self.record_lit_syntax =
364 self.sema.find_node_at_offset_with_macros(&original_file, offset);
365 }
366
367 self.impl_def = self
368 .sema
369 .ancestors_with_macros(self.token.parent())
370 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
371 .find_map(ast::Impl::cast);
372
373 let top_node = name_ref
374 .syntax()
375 .ancestors()
376 .take_while(|it| it.text_range() == name_range)
377 .last()
378 .unwrap();
379
380 match top_node.parent().map(|it| it.kind()) {
381 Some(SOURCE_FILE) | Some(ITEM_LIST) => {
382 self.is_new_item = true;
383 return;
384 }
385 _ => (),
386 }
387
388 self.use_item_syntax =
389 self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::Use::cast);
390
391 self.function_syntax = self
392 .sema
393 .ancestors_with_macros(self.token.parent())
394 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
395 .find_map(ast::Fn::cast);
396
397 self.record_field_syntax = self
398 .sema
399 .ancestors_with_macros(self.token.parent())
400 .take_while(|it| {
401 it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR
402 })
403 .find_map(ast::RecordExprField::cast);
404
405 let parent = match name_ref.syntax().parent() {
406 Some(it) => it,
407 None => return,
408 };
409
410 if let Some(segment) = ast::PathSegment::cast(parent.clone()) {
411 let path = segment.parent_path();
412 self.is_call = path
413 .syntax()
414 .parent()
415 .and_then(ast::PathExpr::cast)
416 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
417 .is_some();
418 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
419 self.is_pattern_call =
420 path.syntax().parent().and_then(ast::TupleStructPat::cast).is_some();
421
422 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
423 self.has_type_args = segment.generic_arg_list().is_some();
424
425 if let Some(path) = path_or_use_tree_qualifier(&path) {
426 self.path_qual = path
427 .segment()
428 .and_then(|it| {
429 find_node_with_range::<ast::PathSegment>(
430 original_file,
431 it.syntax().text_range(),
432 )
433 })
434 .map(|it| it.parent_path());
435 return;
436 }
437
438 if let Some(segment) = path.segment() {
439 if segment.coloncolon_token().is_some() {
440 return;
441 }
442 }
443
444 self.is_trivial_path = true;
445
446 // Find either enclosing expr statement (thing with `;`) or a
447 // block. If block, check that we are the last expr.
448 self.can_be_stmt = name_ref
449 .syntax()
450 .ancestors()
451 .find_map(|node| {
452 if let Some(stmt) = ast::ExprStmt::cast(node.clone()) {
453 return Some(stmt.syntax().text_range() == name_ref.syntax().text_range());
454 }
455 if let Some(block) = ast::BlockExpr::cast(node) {
456 return Some(
457 block.expr().map(|e| e.syntax().text_range())
458 == Some(name_ref.syntax().text_range()),
459 );
460 }
461 None
462 })
463 .unwrap_or(false);
464 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
465
466 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
467 if let Some(if_expr) =
468 self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off)
469 {
470 if if_expr.syntax().text_range().end() < name_ref.syntax().text_range().start()
471 {
472 self.after_if = true;
473 }
474 }
475 }
476 }
477 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
478 // The receiver comes before the point of insertion of the fake
479 // ident, so it should have the same range in the non-modified file
480 self.dot_receiver = field_expr
481 .expr()
482 .map(|e| e.syntax().text_range())
483 .and_then(|r| find_node_with_range(original_file, r));
484 self.dot_receiver_is_ambiguous_float_literal =
485 if let Some(ast::Expr::Literal(l)) = &self.dot_receiver {
486 match l.kind() {
487 ast::LiteralKind::FloatNumber { .. } => l.token().text().ends_with('.'),
488 _ => false,
489 }
490 } else {
491 false
492 };
493 }
494 if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) {
495 // As above
496 self.dot_receiver = method_call_expr
497 .receiver()
498 .map(|e| e.syntax().text_range())
499 .and_then(|r| find_node_with_range(original_file, r));
500 self.is_call = true;
501 }
502 }
503}
504
505fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
506 find_covering_element(syntax, range).ancestors().find_map(N::cast)
507}
508
509fn is_node<N: AstNode>(node: &SyntaxNode) -> bool {
510 match node.ancestors().find_map(N::cast) {
511 None => false,
512 Some(n) => n.syntax().text_range() == node.text_range(),
513 }
514}
515
516fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<ast::Path> {
517 if let Some(qual) = path.qualifier() {
518 return Some(qual);
519 }
520 let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
521 let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?;
522 use_tree.path()
523}
diff --git a/crates/ide/src/completion/completion_item.rs b/crates/ide/src/completion/completion_item.rs
deleted file mode 100644
index 9377cdc57..000000000
--- a/crates/ide/src/completion/completion_item.rs
+++ /dev/null
@@ -1,384 +0,0 @@
1//! FIXME: write short doc here
2
3use std::fmt;
4
5use hir::Documentation;
6use syntax::TextRange;
7use text_edit::TextEdit;
8
9use crate::completion::completion_config::SnippetCap;
10
11/// `CompletionItem` describes a single completion variant in the editor pop-up.
12/// It is basically a POD with various properties. To construct a
13/// `CompletionItem`, use `new` method and the `Builder` struct.
14pub struct CompletionItem {
15 /// Used only internally in tests, to check only specific kind of
16 /// completion (postfix, keyword, reference, etc).
17 #[allow(unused)]
18 pub(crate) completion_kind: CompletionKind,
19 /// Label in the completion pop up which identifies completion.
20 label: String,
21 /// Range of identifier that is being completed.
22 ///
23 /// It should be used primarily for UI, but we also use this to convert
24 /// genetic TextEdit into LSP's completion edit (see conv.rs).
25 ///
26 /// `source_range` must contain the completion offset. `insert_text` should
27 /// start with what `source_range` points to, or VSCode will filter out the
28 /// completion silently.
29 source_range: TextRange,
30 /// What happens when user selects this item.
31 ///
32 /// Typically, replaces `source_range` with new identifier.
33 text_edit: TextEdit,
34 insert_text_format: InsertTextFormat,
35
36 /// What item (struct, function, etc) are we completing.
37 kind: Option<CompletionItemKind>,
38
39 /// Lookup is used to check if completion item indeed can complete current
40 /// ident.
41 ///
42 /// That is, in `foo.bar<|>` lookup of `abracadabra` will be accepted (it
43 /// contains `bar` sub sequence), and `quux` will rejected.
44 lookup: Option<String>,
45
46 /// Additional info to show in the UI pop up.
47 detail: Option<String>,
48 documentation: Option<Documentation>,
49
50 /// Whether this item is marked as deprecated
51 deprecated: bool,
52
53 /// If completing a function call, ask the editor to show parameter popup
54 /// after completion.
55 trigger_call_info: bool,
56
57 /// Score is useful to pre select or display in better order completion items
58 score: Option<CompletionScore>,
59}
60
61// We use custom debug for CompletionItem to make snapshot tests more readable.
62impl fmt::Debug for CompletionItem {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 let mut s = f.debug_struct("CompletionItem");
65 s.field("label", &self.label()).field("source_range", &self.source_range());
66 if self.text_edit().len() == 1 {
67 let atom = &self.text_edit().iter().next().unwrap();
68 s.field("delete", &atom.delete);
69 s.field("insert", &atom.insert);
70 } else {
71 s.field("text_edit", &self.text_edit);
72 }
73 if let Some(kind) = self.kind().as_ref() {
74 s.field("kind", kind);
75 }
76 if self.lookup() != self.label() {
77 s.field("lookup", &self.lookup());
78 }
79 if let Some(detail) = self.detail() {
80 s.field("detail", &detail);
81 }
82 if let Some(documentation) = self.documentation() {
83 s.field("documentation", &documentation);
84 }
85 if self.deprecated {
86 s.field("deprecated", &true);
87 }
88 if let Some(score) = &self.score {
89 s.field("score", score);
90 }
91 if self.trigger_call_info {
92 s.field("trigger_call_info", &true);
93 }
94 s.finish()
95 }
96}
97
98#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
99pub enum CompletionScore {
100 /// If only type match
101 TypeMatch,
102 /// If type and name match
103 TypeAndNameMatch,
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub enum CompletionItemKind {
108 Snippet,
109 Keyword,
110 Module,
111 Function,
112 BuiltinType,
113 Struct,
114 Enum,
115 EnumVariant,
116 Binding,
117 Field,
118 Static,
119 Const,
120 Trait,
121 TypeAlias,
122 Method,
123 TypeParam,
124 Macro,
125 Attribute,
126 UnresolvedReference,
127}
128
129impl CompletionItemKind {
130 #[cfg(test)]
131 pub(crate) fn tag(&self) -> &'static str {
132 match self {
133 CompletionItemKind::Attribute => "at",
134 CompletionItemKind::Binding => "bn",
135 CompletionItemKind::BuiltinType => "bt",
136 CompletionItemKind::Const => "ct",
137 CompletionItemKind::Enum => "en",
138 CompletionItemKind::EnumVariant => "ev",
139 CompletionItemKind::Field => "fd",
140 CompletionItemKind::Function => "fn",
141 CompletionItemKind::Keyword => "kw",
142 CompletionItemKind::Macro => "ma",
143 CompletionItemKind::Method => "me",
144 CompletionItemKind::Module => "md",
145 CompletionItemKind::Snippet => "sn",
146 CompletionItemKind::Static => "sc",
147 CompletionItemKind::Struct => "st",
148 CompletionItemKind::Trait => "tt",
149 CompletionItemKind::TypeAlias => "ta",
150 CompletionItemKind::TypeParam => "tp",
151 CompletionItemKind::UnresolvedReference => "??",
152 }
153 }
154}
155
156#[derive(Debug, PartialEq, Eq, Copy, Clone)]
157pub(crate) enum CompletionKind {
158 /// Parser-based keyword completion.
159 Keyword,
160 /// Your usual "complete all valid identifiers".
161 Reference,
162 /// "Secret sauce" completions.
163 Magic,
164 Snippet,
165 Postfix,
166 BuiltinType,
167 Attribute,
168}
169
170#[derive(Debug, PartialEq, Eq, Copy, Clone)]
171pub enum InsertTextFormat {
172 PlainText,
173 Snippet,
174}
175
176impl CompletionItem {
177 pub(crate) fn new(
178 completion_kind: CompletionKind,
179 source_range: TextRange,
180 label: impl Into<String>,
181 ) -> Builder {
182 let label = label.into();
183 Builder {
184 source_range,
185 completion_kind,
186 label,
187 insert_text: None,
188 insert_text_format: InsertTextFormat::PlainText,
189 detail: None,
190 documentation: None,
191 lookup: None,
192 kind: None,
193 text_edit: None,
194 deprecated: None,
195 trigger_call_info: None,
196 score: None,
197 }
198 }
199 /// What user sees in pop-up in the UI.
200 pub fn label(&self) -> &str {
201 &self.label
202 }
203 pub fn source_range(&self) -> TextRange {
204 self.source_range
205 }
206
207 pub fn insert_text_format(&self) -> InsertTextFormat {
208 self.insert_text_format
209 }
210
211 pub fn text_edit(&self) -> &TextEdit {
212 &self.text_edit
213 }
214
215 /// Short one-line additional information, like a type
216 pub fn detail(&self) -> Option<&str> {
217 self.detail.as_deref()
218 }
219 /// A doc-comment
220 pub fn documentation(&self) -> Option<Documentation> {
221 self.documentation.clone()
222 }
223 /// What string is used for filtering.
224 pub fn lookup(&self) -> &str {
225 self.lookup.as_deref().unwrap_or(&self.label)
226 }
227
228 pub fn kind(&self) -> Option<CompletionItemKind> {
229 self.kind
230 }
231
232 pub fn deprecated(&self) -> bool {
233 self.deprecated
234 }
235
236 pub fn score(&self) -> Option<CompletionScore> {
237 self.score
238 }
239
240 pub fn trigger_call_info(&self) -> bool {
241 self.trigger_call_info
242 }
243}
244
245/// A helper to make `CompletionItem`s.
246#[must_use]
247pub(crate) struct Builder {
248 source_range: TextRange,
249 completion_kind: CompletionKind,
250 label: String,
251 insert_text: Option<String>,
252 insert_text_format: InsertTextFormat,
253 detail: Option<String>,
254 documentation: Option<Documentation>,
255 lookup: Option<String>,
256 kind: Option<CompletionItemKind>,
257 text_edit: Option<TextEdit>,
258 deprecated: Option<bool>,
259 trigger_call_info: Option<bool>,
260 score: Option<CompletionScore>,
261}
262
263impl Builder {
264 pub(crate) fn add_to(self, acc: &mut Completions) {
265 acc.add(self.build())
266 }
267
268 pub(crate) fn build(self) -> CompletionItem {
269 let label = self.label;
270 let text_edit = match self.text_edit {
271 Some(it) => it,
272 None => TextEdit::replace(
273 self.source_range,
274 self.insert_text.unwrap_or_else(|| label.clone()),
275 ),
276 };
277
278 CompletionItem {
279 source_range: self.source_range,
280 label,
281 insert_text_format: self.insert_text_format,
282 text_edit,
283 detail: self.detail,
284 documentation: self.documentation,
285 lookup: self.lookup,
286 kind: self.kind,
287 completion_kind: self.completion_kind,
288 deprecated: self.deprecated.unwrap_or(false),
289 trigger_call_info: self.trigger_call_info.unwrap_or(false),
290 score: self.score,
291 }
292 }
293 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
294 self.lookup = Some(lookup.into());
295 self
296 }
297 pub(crate) fn label(mut self, label: impl Into<String>) -> Builder {
298 self.label = label.into();
299 self
300 }
301 pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder {
302 self.insert_text = Some(insert_text.into());
303 self
304 }
305 pub(crate) fn insert_snippet(
306 mut self,
307 _cap: SnippetCap,
308 snippet: impl Into<String>,
309 ) -> Builder {
310 self.insert_text_format = InsertTextFormat::Snippet;
311 self.insert_text(snippet)
312 }
313 pub(crate) fn kind(mut self, kind: CompletionItemKind) -> Builder {
314 self.kind = Some(kind);
315 self
316 }
317 pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder {
318 self.text_edit = Some(edit);
319 self
320 }
321 pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder {
322 self.insert_text_format = InsertTextFormat::Snippet;
323 self.text_edit(edit)
324 }
325 #[allow(unused)]
326 pub(crate) fn detail(self, detail: impl Into<String>) -> Builder {
327 self.set_detail(Some(detail))
328 }
329 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder {
330 self.detail = detail.map(Into::into);
331 self
332 }
333 #[allow(unused)]
334 pub(crate) fn documentation(self, docs: Documentation) -> Builder {
335 self.set_documentation(Some(docs))
336 }
337 pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder {
338 self.documentation = docs.map(Into::into);
339 self
340 }
341 pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder {
342 self.deprecated = Some(deprecated);
343 self
344 }
345 pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder {
346 self.score = Some(score);
347 self
348 }
349 pub(crate) fn trigger_call_info(mut self) -> Builder {
350 self.trigger_call_info = Some(true);
351 self
352 }
353}
354
355impl<'a> Into<CompletionItem> for Builder {
356 fn into(self) -> CompletionItem {
357 self.build()
358 }
359}
360
361/// Represents an in-progress set of completions being built.
362#[derive(Debug, Default)]
363pub(crate) struct Completions {
364 buf: Vec<CompletionItem>,
365}
366
367impl Completions {
368 pub(crate) fn add(&mut self, item: impl Into<CompletionItem>) {
369 self.buf.push(item.into())
370 }
371 pub(crate) fn add_all<I>(&mut self, items: I)
372 where
373 I: IntoIterator,
374 I::Item: Into<CompletionItem>,
375 {
376 items.into_iter().for_each(|item| self.add(item.into()))
377 }
378}
379
380impl Into<Vec<CompletionItem>> for Completions {
381 fn into(self) -> Vec<CompletionItem> {
382 self.buf
383 }
384}
diff --git a/crates/ide/src/completion/generated_features.rs b/crates/ide/src/completion/generated_features.rs
deleted file mode 100644
index 24754a8cf..000000000
--- a/crates/ide/src/completion/generated_features.rs
+++ /dev/null
@@ -1,4 +0,0 @@
1//! Generated file, do not edit by hand, see `xtask/src/codegen`
2
3use crate::completion::complete_attribute::LintCompletion;
4pub ( super ) const FEATURES : & [ LintCompletion ] = & [ LintCompletion { label : "doc_cfg" , description : "# `doc_cfg`\n\nThe tracking issue for this feature is: [#43781]\n\n------\n\nThe `doc_cfg` feature allows an API be documented as only available in some specific platforms.\nThis attribute has two effects:\n\n1. In the annotated item's documentation, there will be a message saying \"This is supported on\n (platform) only\".\n\n2. The item's doc-tests will only run on the specific platform.\n\nIn addition to allowing the use of the `#[doc(cfg)]` attribute, this feature enables the use of a\nspecial conditional compilation flag, `#[cfg(doc)]`, set whenever building documentation on your\ncrate.\n\nThis feature was introduced as part of PR [#43348] to allow the platform-specific parts of the\nstandard library be documented.\n\n```rust\n#![feature(doc_cfg)]\n\n#[cfg(any(windows, doc))]\n#[doc(cfg(windows))]\n/// The application's icon in the notification area (a.k.a. system tray).\n///\n/// # Examples\n///\n/// ```no_run\n/// extern crate my_awesome_ui_library;\n/// use my_awesome_ui_library::current_app;\n/// use my_awesome_ui_library::windows::notification;\n///\n/// let icon = current_app().get::<notification::Icon>();\n/// icon.show();\n/// icon.show_message(\"Hello\");\n/// ```\npub struct Icon {\n // ...\n}\n```\n\n[#43781]: https://github.com/rust-lang/rust/issues/43781\n[#43348]: https://github.com/rust-lang/rust/issues/43348\n" } , LintCompletion { label : "impl_trait_in_bindings" , description : "# `impl_trait_in_bindings`\n\nThe tracking issue for this feature is: [#63065]\n\n[#63065]: https://github.com/rust-lang/rust/issues/63065\n\n------------------------\n\nThe `impl_trait_in_bindings` feature gate lets you use `impl Trait` syntax in\n`let`, `static`, and `const` bindings.\n\nA simple example is:\n\n```rust\n#![feature(impl_trait_in_bindings)]\n\nuse std::fmt::Debug;\n\nfn main() {\n let a: impl Debug + Clone = 42;\n let b = a.clone();\n println!(\"{:?}\", b); // prints `42`\n}\n```\n\nNote however that because the types of `a` and `b` are opaque in the above\nexample, calling inherent methods or methods outside of the specified traits\n(e.g., `a.abs()` or `b.abs()`) is not allowed, and yields an error.\n" } , LintCompletion { label : "plugin" , description : "# `plugin`\n\nThe tracking issue for this feature is: [#29597]\n\n[#29597]: https://github.com/rust-lang/rust/issues/29597\n\n\nThis feature is part of \"compiler plugins.\" It will often be used with the\n[`plugin_registrar`] and `rustc_private` features.\n\n[`plugin_registrar`]: plugin-registrar.md\n\n------------------------\n\n`rustc` can load compiler plugins, which are user-provided libraries that\nextend the compiler's behavior with new lint checks, etc.\n\nA plugin is a dynamic library crate with a designated *registrar* function that\nregisters extensions with `rustc`. Other crates can load these extensions using\nthe crate attribute `#![plugin(...)]`. See the\n`rustc_driver::plugin` documentation for more about the\nmechanics of defining and loading a plugin.\n\nIn the vast majority of cases, a plugin should *only* be used through\n`#![plugin]` and not through an `extern crate` item. Linking a plugin would\npull in all of librustc_ast and librustc as dependencies of your crate. This is\ngenerally unwanted unless you are building another plugin.\n\nThe usual practice is to put compiler plugins in their own crate, separate from\nany `macro_rules!` macros or ordinary Rust code meant to be used by consumers\nof a library.\n\n# Lint plugins\n\nPlugins can extend [Rust's lint\ninfrastructure](../../reference/attributes/diagnostics.md#lint-check-attributes) with\nadditional checks for code style, safety, etc. Now let's write a plugin\n[`lint-plugin-test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/ui-fulldeps/auxiliary/lint-plugin-test.rs)\nthat warns about any item named `lintme`.\n\n```rust,ignore\n#![feature(plugin_registrar)]\n#![feature(box_syntax, rustc_private)]\n\nextern crate rustc_ast;\n\n// Load rustc as a plugin to get macros\nextern crate rustc_driver;\n#[macro_use]\nextern crate rustc_lint;\n#[macro_use]\nextern crate rustc_session;\n\nuse rustc_driver::plugin::Registry;\nuse rustc_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};\nuse rustc_ast::ast;\ndeclare_lint!(TEST_LINT, Warn, \"Warn about items named 'lintme'\");\n\ndeclare_lint_pass!(Pass => [TEST_LINT]);\n\nimpl EarlyLintPass for Pass {\n fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) {\n if it.ident.name.as_str() == \"lintme\" {\n cx.lint(TEST_LINT, |lint| {\n lint.build(\"item is named 'lintme'\").set_span(it.span).emit()\n });\n }\n }\n}\n\n#[plugin_registrar]\npub fn plugin_registrar(reg: &mut Registry) {\n reg.lint_store.register_lints(&[&TEST_LINT]);\n reg.lint_store.register_early_pass(|| box Pass);\n}\n```\n\nThen code like\n\n```rust,ignore\n#![feature(plugin)]\n#![plugin(lint_plugin_test)]\n\nfn lintme() { }\n```\n\nwill produce a compiler warning:\n\n```txt\nfoo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default\nfoo.rs:4 fn lintme() { }\n ^~~~~~~~~~~~~~~\n```\n\nThe components of a lint plugin are:\n\n* one or more `declare_lint!` invocations, which define static `Lint` structs;\n\n* a struct holding any state needed by the lint pass (here, none);\n\n* a `LintPass`\n implementation defining how to check each syntax element. A single\n `LintPass` may call `span_lint` for several different `Lint`s, but should\n register them all through the `get_lints` method.\n\nLint passes are syntax traversals, but they run at a late stage of compilation\nwhere type information is available. `rustc`'s [built-in\nlints](https://github.com/rust-lang/rust/blob/master/src/librustc_session/lint/builtin.rs)\nmostly use the same infrastructure as lint plugins, and provide examples of how\nto access type information.\n\nLints defined by plugins are controlled by the usual [attributes and compiler\nflags](../../reference/attributes/diagnostics.md#lint-check-attributes), e.g.\n`#[allow(test_lint)]` or `-A test-lint`. These identifiers are derived from the\nfirst argument to `declare_lint!`, with appropriate case and punctuation\nconversion.\n\nYou can run `rustc -W help foo.rs` to see a list of lints known to `rustc`,\nincluding those provided by plugins loaded by `foo.rs`.\n" } , LintCompletion { label : "infer_static_outlives_requirements" , description : "# `infer_static_outlives_requirements`\n\nThe tracking issue for this feature is: [#54185]\n\n[#54185]: https://github.com/rust-lang/rust/issues/54185\n\n------------------------\nThe `infer_static_outlives_requirements` feature indicates that certain\n`'static` outlives requirements can be inferred by the compiler rather than\nstating them explicitly.\n\nNote: It is an accompanying feature to `infer_outlives_requirements`,\nwhich must be enabled to infer outlives requirements.\n\nFor example, currently generic struct definitions that contain\nreferences, require where-clauses of the form T: 'static. By using\nthis feature the outlives predicates will be inferred, although\nthey may still be written explicitly.\n\n```rust,ignore (pseudo-Rust)\nstruct Foo<U> where U: 'static { // <-- currently required\n bar: Bar<U>\n}\nstruct Bar<T: 'static> {\n x: T,\n}\n```\n\n\n## Examples:\n\n```rust,ignore (pseudo-Rust)\n#![feature(infer_outlives_requirements)]\n#![feature(infer_static_outlives_requirements)]\n\n#[rustc_outlives]\n// Implicitly infer U: 'static\nstruct Foo<U> {\n bar: Bar<U>\n}\nstruct Bar<T: 'static> {\n x: T,\n}\n```\n\n" } , LintCompletion { label : "doc_alias" , description : "# `doc_alias`\n\nThe tracking issue for this feature is: [#50146]\n\n[#50146]: https://github.com/rust-lang/rust/issues/50146\n\n------------------------\n\nYou can add alias(es) to an item when using the `rustdoc` search through the\n`doc(alias)` attribute. Example:\n\n```rust,no_run\n#![feature(doc_alias)]\n\n#[doc(alias = \"x\")]\n#[doc(alias = \"big\")]\npub struct BigX;\n```\n\nThen, when looking for it through the `rustdoc` search, if you enter \"x\" or\n\"big\", search will show the `BigX` struct first.\n\nNote that this feature is currently hidden behind the `feature(doc_alias)` gate.\n" } , LintCompletion { label : "optin_builtin_traits" , description : "# `optin_builtin_traits`\n\nThe tracking issue for this feature is [#13231] \n\n[#13231]: https://github.com/rust-lang/rust/issues/13231\n\n----\n\nThe `optin_builtin_traits` feature gate allows you to define auto traits.\n\nAuto traits, like [`Send`] or [`Sync`] in the standard library, are marker traits\nthat are automatically implemented for every type, unless the type, or a type it contains, \nhas explicitly opted out via a negative impl. (Negative impls are separately controlled\nby the `negative_impls` feature.)\n\n[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html\n[`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html\n\n```rust,ignore\nimpl !Trait for Type\n```\n\nExample:\n\n```rust\n#![feature(negative_impls)]\n#![feature(optin_builtin_traits)]\n\nauto trait Valid {}\n\nstruct True;\nstruct False;\n\nimpl !Valid for False {}\n\nstruct MaybeValid<T>(T);\n\nfn must_be_valid<T: Valid>(_t: T) { }\n\nfn main() {\n // works\n must_be_valid( MaybeValid(True) );\n \n // compiler error - trait bound not satisfied\n // must_be_valid( MaybeValid(False) );\n}\n```\n\n## Automatic trait implementations\n\nWhen a type is declared as an `auto trait`, we will automatically\ncreate impls for every struct/enum/union, unless an explicit impl is\nprovided. These automatic impls contain a where clause for each field\nof the form `T: AutoTrait`, where `T` is the type of the field and\n`AutoTrait` is the auto trait in question. As an example, consider the\nstruct `List` and the auto trait `Send`:\n\n```rust\nstruct List<T> {\n data: T,\n next: Option<Box<List<T>>>,\n}\n```\n\nPresuming that there is no explicit impl of `Send` for `List`, the\ncompiler will supply an automatic impl of the form:\n\n```rust\nstruct List<T> {\n data: T,\n next: Option<Box<List<T>>>,\n}\n\nunsafe impl<T> Send for List<T>\nwhere\n T: Send, // from the field `data`\n Option<Box<List<T>>>: Send, // from the field `next`\n{ }\n```\n\nExplicit impls may be either positive or negative. They take the form:\n\n```rust,ignore\nimpl<...> AutoTrait for StructName<..> { }\nimpl<...> !AutoTrait for StructName<..> { }\n```\n\n## Coinduction: Auto traits permit cyclic matching\n\nUnlike ordinary trait matching, auto traits are **coinductive**. This\nmeans, in short, that cycles which occur in trait matching are\nconsidered ok. As an example, consider the recursive struct `List`\nintroduced in the previous section. In attempting to determine whether\n`List: Send`, we would wind up in a cycle: to apply the impl, we must\nshow that `Option<Box<List>>: Send`, which will in turn require\n`Box<List>: Send` and then finally `List: Send` again. Under ordinary\ntrait matching, this cycle would be an error, but for an auto trait it\nis considered a successful match.\n\n## Items\n\nAuto traits cannot have any trait items, such as methods or associated types. This ensures that we can generate default implementations.\n\n## Supertraits\n\nAuto traits cannot have supertraits. This is for soundness reasons, as the interaction of coinduction with implied bounds is difficult to reconcile.\n\n" } , LintCompletion { label : "const_in_array_repeat_expressions" , description : "# `const_in_array_repeat_expressions`\n\nThe tracking issue for this feature is: [#49147]\n\n[#49147]: https://github.com/rust-lang/rust/issues/49147\n\n------------------------\n\nRelaxes the rules for repeat expressions, `[x; N]` such that `x` may also be `const` (strictly\nspeaking rvalue promotable), in addition to `typeof(x): Copy`. The result of `[x; N]` where `x` is\n`const` is itself also `const`.\n" } , LintCompletion { label : "generators" , description : "# `generators`\n\nThe tracking issue for this feature is: [#43122]\n\n[#43122]: https://github.com/rust-lang/rust/issues/43122\n\n------------------------\n\nThe `generators` feature gate in Rust allows you to define generator or\ncoroutine literals. A generator is a \"resumable function\" that syntactically\nresembles a closure but compiles to much different semantics in the compiler\nitself. The primary feature of a generator is that it can be suspended during\nexecution to be resumed at a later date. Generators use the `yield` keyword to\n\"return\", and then the caller can `resume` a generator to resume execution just\nafter the `yield` keyword.\n\nGenerators are an extra-unstable feature in the compiler right now. Added in\n[RFC 2033] they're mostly intended right now as a information/constraint\ngathering phase. The intent is that experimentation can happen on the nightly\ncompiler before actual stabilization. A further RFC will be required to\nstabilize generators/coroutines and will likely contain at least a few small\ntweaks to the overall design.\n\n[RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033\n\nA syntactical example of a generator is:\n\n```rust\n#![feature(generators, generator_trait)]\n\nuse std::ops::{Generator, GeneratorState};\nuse std::pin::Pin;\n\nfn main() {\n let mut generator = || {\n yield 1;\n return \"foo\"\n };\n\n match Pin::new(&mut generator).resume(()) {\n GeneratorState::Yielded(1) => {}\n _ => panic!(\"unexpected value from resume\"),\n }\n match Pin::new(&mut generator).resume(()) {\n GeneratorState::Complete(\"foo\") => {}\n _ => panic!(\"unexpected value from resume\"),\n }\n}\n```\n\nGenerators are closure-like literals which can contain a `yield` statement. The\n`yield` statement takes an optional expression of a value to yield out of the\ngenerator. All generator literals implement the `Generator` trait in the\n`std::ops` module. The `Generator` trait has one main method, `resume`, which\nresumes execution of the generator at the previous suspension point.\n\nAn example of the control flow of generators is that the following example\nprints all numbers in order:\n\n```rust\n#![feature(generators, generator_trait)]\n\nuse std::ops::Generator;\nuse std::pin::Pin;\n\nfn main() {\n let mut generator = || {\n println!(\"2\");\n yield;\n println!(\"4\");\n };\n\n println!(\"1\");\n Pin::new(&mut generator).resume(());\n println!(\"3\");\n Pin::new(&mut generator).resume(());\n println!(\"5\");\n}\n```\n\nAt this time the main intended use case of generators is an implementation\nprimitive for async/await syntax, but generators will likely be extended to\nergonomic implementations of iterators and other primitives in the future.\nFeedback on the design and usage is always appreciated!\n\n### The `Generator` trait\n\nThe `Generator` trait in `std::ops` currently looks like:\n\n```rust\n# #![feature(arbitrary_self_types, generator_trait)]\n# use std::ops::GeneratorState;\n# use std::pin::Pin;\n\npub trait Generator<R = ()> {\n type Yield;\n type Return;\n fn resume(self: Pin<&mut Self>, resume: R) -> GeneratorState<Self::Yield, Self::Return>;\n}\n```\n\nThe `Generator::Yield` type is the type of values that can be yielded with the\n`yield` statement. The `Generator::Return` type is the returned type of the\ngenerator. This is typically the last expression in a generator's definition or\nany value passed to `return` in a generator. The `resume` function is the entry\npoint for executing the `Generator` itself.\n\nThe return value of `resume`, `GeneratorState`, looks like:\n\n```rust\npub enum GeneratorState<Y, R> {\n Yielded(Y),\n Complete(R),\n}\n```\n\nThe `Yielded` variant indicates that the generator can later be resumed. This\ncorresponds to a `yield` point in a generator. The `Complete` variant indicates\nthat the generator is complete and cannot be resumed again. Calling `resume`\nafter a generator has returned `Complete` will likely result in a panic of the\nprogram.\n\n### Closure-like semantics\n\nThe closure-like syntax for generators alludes to the fact that they also have\nclosure-like semantics. Namely:\n\n* When created, a generator executes no code. A closure literal does not\n actually execute any of the closure's code on construction, and similarly a\n generator literal does not execute any code inside the generator when\n constructed.\n\n* Generators can capture outer variables by reference or by move, and this can\n be tweaked with the `move` keyword at the beginning of the closure. Like\n closures all generators will have an implicit environment which is inferred by\n the compiler. Outer variables can be moved into a generator for use as the\n generator progresses.\n\n* Generator literals produce a value with a unique type which implements the\n `std::ops::Generator` trait. This allows actual execution of the generator\n through the `Generator::resume` method as well as also naming it in return\n types and such.\n\n* Traits like `Send` and `Sync` are automatically implemented for a `Generator`\n depending on the captured variables of the environment. Unlike closures,\n generators also depend on variables live across suspension points. This means\n that although the ambient environment may be `Send` or `Sync`, the generator\n itself may not be due to internal variables live across `yield` points being\n not-`Send` or not-`Sync`. Note that generators do\n not implement traits like `Copy` or `Clone` automatically.\n\n* Whenever a generator is dropped it will drop all captured environment\n variables.\n\n### Generators as state machines\n\nIn the compiler, generators are currently compiled as state machines. Each\n`yield` expression will correspond to a different state that stores all live\nvariables over that suspension point. Resumption of a generator will dispatch on\nthe current state and then execute internally until a `yield` is reached, at\nwhich point all state is saved off in the generator and a value is returned.\n\nLet's take a look at an example to see what's going on here:\n\n```rust\n#![feature(generators, generator_trait)]\n\nuse std::ops::Generator;\nuse std::pin::Pin;\n\nfn main() {\n let ret = \"foo\";\n let mut generator = move || {\n yield 1;\n return ret\n };\n\n Pin::new(&mut generator).resume(());\n Pin::new(&mut generator).resume(());\n}\n```\n\nThis generator literal will compile down to something similar to:\n\n```rust\n#![feature(arbitrary_self_types, generators, generator_trait)]\n\nuse std::ops::{Generator, GeneratorState};\nuse std::pin::Pin;\n\nfn main() {\n let ret = \"foo\";\n let mut generator = {\n enum __Generator {\n Start(&'static str),\n Yield1(&'static str),\n Done,\n }\n\n impl Generator for __Generator {\n type Yield = i32;\n type Return = &'static str;\n\n fn resume(mut self: Pin<&mut Self>, resume: ()) -> GeneratorState<i32, &'static str> {\n use std::mem;\n match mem::replace(&mut *self, __Generator::Done) {\n __Generator::Start(s) => {\n *self = __Generator::Yield1(s);\n GeneratorState::Yielded(1)\n }\n\n __Generator::Yield1(s) => {\n *self = __Generator::Done;\n GeneratorState::Complete(s)\n }\n\n __Generator::Done => {\n panic!(\"generator resumed after completion\")\n }\n }\n }\n }\n\n __Generator::Start(ret)\n };\n\n Pin::new(&mut generator).resume(());\n Pin::new(&mut generator).resume(());\n}\n```\n\nNotably here we can see that the compiler is generating a fresh type,\n`__Generator` in this case. This type has a number of states (represented here\nas an `enum`) corresponding to each of the conceptual states of the generator.\nAt the beginning we're closing over our outer variable `foo` and then that\nvariable is also live over the `yield` point, so it's stored in both states.\n\nWhen the generator starts it'll immediately yield 1, but it saves off its state\njust before it does so indicating that it has reached the yield point. Upon\nresuming again we'll execute the `return ret` which returns the `Complete`\nstate.\n\nHere we can also note that the `Done` state, if resumed, panics immediately as\nit's invalid to resume a completed generator. It's also worth noting that this\nis just a rough desugaring, not a normative specification for what the compiler\ndoes.\n" } , LintCompletion { label : "unsized_tuple_coercion" , description : "# `unsized_tuple_coercion`\n\nThe tracking issue for this feature is: [#42877]\n\n[#42877]: https://github.com/rust-lang/rust/issues/42877\n\n------------------------\n\nThis is a part of [RFC0401]. According to the RFC, there should be an implementation like this:\n\n```rust,ignore\nimpl<..., T, U: ?Sized> Unsized<(..., U)> for (..., T) where T: Unsized<U> {}\n```\n\nThis implementation is currently gated behind `#[feature(unsized_tuple_coercion)]` to avoid insta-stability. Therefore you can use it like this:\n\n```rust\n#![feature(unsized_tuple_coercion)]\n\nfn main() {\n let x : ([i32; 3], [i32; 3]) = ([1, 2, 3], [4, 5, 6]);\n let y : &([i32; 3], [i32]) = &x;\n assert_eq!(y.1[0], 4);\n}\n```\n\n[RFC0401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md\n" } , LintCompletion { label : "cfg_version" , description : "# `cfg_version`\n\nThe tracking issue for this feature is: [#64796]\n\n[#64796]: https://github.com/rust-lang/rust/issues/64796\n\n------------------------\n\nThe `cfg_version` feature makes it possible to execute different code\ndepending on the compiler version.\n\n## Examples\n\n```rust\n#![feature(cfg_version)]\n\n#[cfg(version(\"1.42\"))]\nfn a() {\n // ...\n}\n\n#[cfg(not(version(\"1.42\")))]\nfn a() {\n // ...\n}\n\nfn b() {\n if cfg!(version(\"1.42\")) {\n // ...\n } else {\n // ...\n }\n}\n```\n" } , LintCompletion { label : "ffi_const" , description : "# `ffi_const`\n\nThe `#[ffi_const]` attribute applies clang's `const` attribute to foreign\nfunctions declarations.\n\nThat is, `#[ffi_const]` functions shall have no effects except for its return\nvalue, which can only depend on the values of the function parameters, and is\nnot affected by changes to the observable state of the program.\n\nApplying the `#[ffi_const]` attribute to a function that violates these\nrequirements is undefined behaviour.\n\nThis attribute enables Rust to perform common optimizations, like sub-expression\nelimination, and it can avoid emitting some calls in repeated invocations of the\nfunction with the same argument values regardless of other operations being\nperformed in between these functions calls (as opposed to `#[ffi_pure]`\nfunctions).\n\n## Pitfalls\n\nA `#[ffi_const]` function can only read global memory that would not affect\nits return value for the whole execution of the program (e.g. immutable global\nmemory). `#[ffi_const]` functions are referentially-transparent and therefore\nmore strict than `#[ffi_pure]` functions.\n\nA common pitfall involves applying the `#[ffi_const]` attribute to a\nfunction that reads memory through pointer arguments which do not necessarily\npoint to immutable global memory.\n\nA `#[ffi_const]` function that returns unit has no effect on the abstract\nmachine's state, and a `#[ffi_const]` function cannot be `#[ffi_pure]`.\n\nA `#[ffi_const]` function must not diverge, neither via a side effect (e.g. a\ncall to `abort`) nor by infinite loops.\n\nWhen translating C headers to Rust FFI, it is worth verifying for which targets\nthe `const` attribute is enabled in those headers, and using the appropriate\n`cfg` macros in the Rust side to match those definitions. While the semantics of\n`const` are implemented identically by many C and C++ compilers, e.g., clang,\n[GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily\nimplemented in this way on all of them. It is therefore also worth verifying\nthat the semantics of the C toolchain used to compile the binary being linked\nagainst are compatible with those of the `#[ffi_const]`.\n\n[ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacgigch.html\n[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-const-function-attribute\n[IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_const.htm\n" } , LintCompletion { label : "const_fn" , description : "# `const_fn`\n\nThe tracking issue for this feature is: [#57563]\n\n[#57563]: https://github.com/rust-lang/rust/issues/57563\n\n------------------------\n\nThe `const_fn` feature allows marking free functions and inherent methods as\n`const`, enabling them to be called in constants contexts, with constant\narguments.\n\n## Examples\n\n```rust\n#![feature(const_fn)]\n\nconst fn double(x: i32) -> i32 {\n x * 2\n}\n\nconst FIVE: i32 = 5;\nconst TEN: i32 = double(FIVE);\n\nfn main() {\n assert_eq!(5, FIVE);\n assert_eq!(10, TEN);\n}\n```\n" } , LintCompletion { label : "unsized_locals" , description : "# `unsized_locals`\n\nThe tracking issue for this feature is: [#48055]\n\n[#48055]: https://github.com/rust-lang/rust/issues/48055\n\n------------------------\n\nThis implements [RFC1909]. When turned on, you can have unsized arguments and locals:\n\n[RFC1909]: https://github.com/rust-lang/rfcs/blob/master/text/1909-unsized-rvalues.md\n\n```rust\n#![feature(unsized_locals)]\n\nuse std::any::Any;\n\nfn main() {\n let x: Box<dyn Any> = Box::new(42);\n let x: dyn Any = *x;\n // ^ unsized local variable\n // ^^ unsized temporary\n foo(x);\n}\n\nfn foo(_: dyn Any) {}\n// ^^^^^^ unsized argument\n```\n\nThe RFC still forbids the following unsized expressions:\n\n```rust,ignore\n#![feature(unsized_locals)]\n\nuse std::any::Any;\n\nstruct MyStruct<T: ?Sized> {\n content: T,\n}\n\nstruct MyTupleStruct<T: ?Sized>(T);\n\nfn answer() -> Box<dyn Any> {\n Box::new(42)\n}\n\nfn main() {\n // You CANNOT have unsized statics.\n static X: dyn Any = *answer(); // ERROR\n const Y: dyn Any = *answer(); // ERROR\n\n // You CANNOT have struct initialized unsized.\n MyStruct { content: *answer() }; // ERROR\n MyTupleStruct(*answer()); // ERROR\n (42, *answer()); // ERROR\n\n // You CANNOT have unsized return types.\n fn my_function() -> dyn Any { *answer() } // ERROR\n\n // You CAN have unsized local variables...\n let mut x: dyn Any = *answer(); // OK\n // ...but you CANNOT reassign to them.\n x = *answer(); // ERROR\n\n // You CANNOT even initialize them separately.\n let y: dyn Any; // OK\n y = *answer(); // ERROR\n\n // Not mentioned in the RFC, but by-move captured variables are also Sized.\n let x: dyn Any = *answer();\n (move || { // ERROR\n let y = x;\n })();\n\n // You CAN create a closure with unsized arguments,\n // but you CANNOT call it.\n // This is an implementation detail and may be changed in the future.\n let f = |x: dyn Any| {};\n f(*answer()); // ERROR\n}\n```\n\n## By-value trait objects\n\nWith this feature, you can have by-value `self` arguments without `Self: Sized` bounds.\n\n```rust\n#![feature(unsized_locals)]\n\ntrait Foo {\n fn foo(self) {}\n}\n\nimpl<T: ?Sized> Foo for T {}\n\nfn main() {\n let slice: Box<[i32]> = Box::new([1, 2, 3]);\n <[i32] as Foo>::foo(*slice);\n}\n```\n\nAnd `Foo` will also be object-safe.\n\n```rust\n#![feature(unsized_locals)]\n\ntrait Foo {\n fn foo(self) {}\n}\n\nimpl<T: ?Sized> Foo for T {}\n\nfn main () {\n let slice: Box<dyn Foo> = Box::new([1, 2, 3]);\n // doesn't compile yet\n <dyn Foo as Foo>::foo(*slice);\n}\n```\n\nOne of the objectives of this feature is to allow `Box<dyn FnOnce>`.\n\n## Variable length arrays\n\nThe RFC also describes an extension to the array literal syntax: `[e; dyn n]`. In the syntax, `n` isn't necessarily a constant expression. The array is dynamically allocated on the stack and has the type of `[T]`, instead of `[T; n]`.\n\n```rust,ignore\n#![feature(unsized_locals)]\n\nfn mergesort<T: Ord>(a: &mut [T]) {\n let mut tmp = [T; dyn a.len()];\n // ...\n}\n\nfn main() {\n let mut a = [3, 1, 5, 6];\n mergesort(&mut a);\n assert_eq!(a, [1, 3, 5, 6]);\n}\n```\n\nVLAs are not implemented yet. The syntax isn't final, either. We may need an alternative syntax for Rust 2015 because, in Rust 2015, expressions like `[e; dyn(1)]` would be ambiguous. One possible alternative proposed in the RFC is `[e; n]`: if `n` captures one or more local variables, then it is considered as `[e; dyn n]`.\n\n## Advisory on stack usage\n\nIt's advised not to casually use the `#![feature(unsized_locals)]` feature. Typical use-cases are:\n\n- When you need a by-value trait objects.\n- When you really need a fast allocation of small temporary arrays.\n\nAnother pitfall is repetitive allocation and temporaries. Currently the compiler simply extends the stack frame every time it encounters an unsized assignment. So for example, the code\n\n```rust\n#![feature(unsized_locals)]\n\nfn main() {\n let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);\n let _x = {{{{{{{{{{*x}}}}}}}}}};\n}\n```\n\nand the code\n\n```rust\n#![feature(unsized_locals)]\n\nfn main() {\n for _ in 0..10 {\n let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);\n let _x = *x;\n }\n}\n```\n\nwill unnecessarily extend the stack frame.\n" } , LintCompletion { label : "or_patterns" , description : "# `or_patterns`\n\nThe tracking issue for this feature is: [#54883]\n\n[#54883]: https://github.com/rust-lang/rust/issues/54883\n\n------------------------\n\nThe `or_pattern` language feature allows `|` to be arbitrarily nested within\na pattern, for example, `Some(A(0) | B(1 | 2))` becomes a valid pattern.\n\n## Examples\n\n```rust,ignore\n#![feature(or_patterns)]\n\npub enum Foo {\n Bar,\n Baz,\n Quux,\n}\n\npub fn example(maybe_foo: Option<Foo>) {\n match maybe_foo {\n Some(Foo::Bar | Foo::Baz) => {\n println!(\"The value contained `Bar` or `Baz`\");\n }\n Some(_) => {\n println!(\"The value did not contain `Bar` or `Baz`\");\n }\n None => {\n println!(\"The value was `None`\");\n }\n }\n}\n```\n" } , LintCompletion { label : "no_sanitize" , description : "# `no_sanitize`\n\nThe tracking issue for this feature is: [#39699]\n\n[#39699]: https://github.com/rust-lang/rust/issues/39699\n\n------------------------\n\nThe `no_sanitize` attribute can be used to selectively disable sanitizer\ninstrumentation in an annotated function. This might be useful to: avoid\ninstrumentation overhead in a performance critical function, or avoid\ninstrumenting code that contains constructs unsupported by given sanitizer.\n\nThe precise effect of this annotation depends on particular sanitizer in use.\nFor example, with `no_sanitize(thread)`, the thread sanitizer will no longer\ninstrument non-atomic store / load operations, but it will instrument atomic\noperations to avoid reporting false positives and provide meaning full stack\ntraces.\n\n## Examples\n\n``` rust\n#![feature(no_sanitize)]\n\n#[no_sanitize(address)]\nfn foo() {\n // ...\n}\n```\n" } , LintCompletion { label : "doc_spotlight" , description : "# `doc_spotlight`\n\nThe tracking issue for this feature is: [#45040]\n\nThe `doc_spotlight` feature allows the use of the `spotlight` parameter to the `#[doc]` attribute,\nto \"spotlight\" a specific trait on the return values of functions. Adding a `#[doc(spotlight)]`\nattribute to a trait definition will make rustdoc print extra information for functions which return\na type that implements that trait. This attribute is applied to the `Iterator`, `io::Read`, and\n`io::Write` traits in the standard library.\n\nYou can do this on your own traits, like this:\n\n```\n#![feature(doc_spotlight)]\n\n#[doc(spotlight)]\npub trait MyTrait {}\n\npub struct MyStruct;\nimpl MyTrait for MyStruct {}\n\n/// The docs for this function will have an extra line about `MyStruct` implementing `MyTrait`,\n/// without having to write that yourself!\npub fn my_fn() -> MyStruct { MyStruct }\n```\n\nThis feature was originally implemented in PR [#45039].\n\n[#45040]: https://github.com/rust-lang/rust/issues/45040\n[#45039]: https://github.com/rust-lang/rust/pull/45039\n" } , LintCompletion { label : "cfg_sanitize" , description : "# `cfg_sanitize`\n\nThe tracking issue for this feature is: [#39699]\n\n[#39699]: https://github.com/rust-lang/rust/issues/39699\n\n------------------------\n\nThe `cfg_sanitize` feature makes it possible to execute different code\ndepending on whether a particular sanitizer is enabled or not.\n\n## Examples\n\n```rust\n#![feature(cfg_sanitize)]\n\n#[cfg(sanitize = \"thread\")]\nfn a() {\n // ...\n}\n\n#[cfg(not(sanitize = \"thread\"))]\nfn a() {\n // ...\n}\n\nfn b() {\n if cfg!(sanitize = \"leak\") {\n // ...\n } else {\n // ...\n }\n}\n```\n" } , LintCompletion { label : "doc_masked" , description : "# `doc_masked`\n\nThe tracking issue for this feature is: [#44027]\n\n-----\n\nThe `doc_masked` feature allows a crate to exclude types from a given crate from appearing in lists\nof trait implementations. The specifics of the feature are as follows:\n\n1. When rustdoc encounters an `extern crate` statement annotated with a `#[doc(masked)]` attribute,\n it marks the crate as being masked.\n\n2. When listing traits a given type implements, rustdoc ensures that traits from masked crates are\n not emitted into the documentation.\n\n3. When listing types that implement a given trait, rustdoc ensures that types from masked crates\n are not emitted into the documentation.\n\nThis feature was introduced in PR [#44026] to ensure that compiler-internal and\nimplementation-specific types and traits were not included in the standard library's documentation.\nSuch types would introduce broken links into the documentation.\n\n[#44026]: https://github.com/rust-lang/rust/pull/44026\n[#44027]: https://github.com/rust-lang/rust/pull/44027\n" } , LintCompletion { label : "abi_thiscall" , description : "# `abi_thiscall`\n\nThe tracking issue for this feature is: [#42202]\n\n[#42202]: https://github.com/rust-lang/rust/issues/42202\n\n------------------------\n\nThe MSVC ABI on x86 Windows uses the `thiscall` calling convention for C++\ninstance methods by default; it is identical to the usual (C) calling\nconvention on x86 Windows except that the first parameter of the method,\nthe `this` pointer, is passed in the ECX register.\n" } , LintCompletion { label : "lang_items" , description : "# `lang_items`\n\nThe tracking issue for this feature is: None.\n\n------------------------\n\nThe `rustc` compiler has certain pluggable operations, that is,\nfunctionality that isn't hard-coded into the language, but is\nimplemented in libraries, with a special marker to tell the compiler\nit exists. The marker is the attribute `#[lang = \"...\"]` and there are\nvarious different values of `...`, i.e. various different 'lang\nitems'.\n\nFor example, `Box` pointers require two lang items, one for allocation\nand one for deallocation. A freestanding program that uses the `Box`\nsugar for dynamic allocations via `malloc` and `free`:\n\n```rust,ignore\n#![feature(lang_items, box_syntax, start, libc, core_intrinsics)]\n#![no_std]\nuse core::intrinsics;\nuse core::panic::PanicInfo;\n\nextern crate libc;\n\n#[lang = \"owned_box\"]\npub struct Box<T>(*mut T);\n\n#[lang = \"exchange_malloc\"]\nunsafe fn allocate(size: usize, _align: usize) -> *mut u8 {\n let p = libc::malloc(size as libc::size_t) as *mut u8;\n\n // Check if `malloc` failed:\n if p as usize == 0 {\n intrinsics::abort();\n }\n\n p\n}\n\n#[lang = \"box_free\"]\nunsafe fn box_free<T: ?Sized>(ptr: *mut T) {\n libc::free(ptr as *mut libc::c_void)\n}\n\n#[start]\nfn main(_argc: isize, _argv: *const *const u8) -> isize {\n let _x = box 1;\n\n 0\n}\n\n#[lang = \"eh_personality\"] extern fn rust_eh_personality() {}\n#[lang = \"panic_impl\"] extern fn rust_begin_panic(info: &PanicInfo) -> ! { unsafe { intrinsics::abort() } }\n#[no_mangle] pub extern fn rust_eh_register_frames () {}\n#[no_mangle] pub extern fn rust_eh_unregister_frames () {}\n```\n\nNote the use of `abort`: the `exchange_malloc` lang item is assumed to\nreturn a valid pointer, and so needs to do the check internally.\n\nOther features provided by lang items include:\n\n- overloadable operators via traits: the traits corresponding to the\n `==`, `<`, dereferencing (`*`) and `+` (etc.) operators are all\n marked with lang items; those specific four are `eq`, `ord`,\n `deref`, and `add` respectively.\n- stack unwinding and general failure; the `eh_personality`,\n `panic` and `panic_bounds_checks` lang items.\n- the traits in `std::marker` used to indicate types of\n various kinds; lang items `send`, `sync` and `copy`.\n- the marker types and variance indicators found in\n `std::marker`; lang items `covariant_type`,\n `contravariant_lifetime`, etc.\n\nLang items are loaded lazily by the compiler; e.g. if one never uses\n`Box` then there is no need to define functions for `exchange_malloc`\nand `box_free`. `rustc` will emit an error when an item is needed\nbut not found in the current crate or any that it depends on.\n\nMost lang items are defined by `libcore`, but if you're trying to build\nan executable without the standard library, you'll run into the need\nfor lang items. The rest of this page focuses on this use-case, even though\nlang items are a bit broader than that.\n\n### Using libc\n\nIn order to build a `#[no_std]` executable we will need libc as a dependency.\nWe can specify this using our `Cargo.toml` file:\n\n```toml\n[dependencies]\nlibc = { version = \"0.2.14\", default-features = false }\n```\n\nNote that the default features have been disabled. This is a critical step -\n**the default features of libc include the standard library and so must be\ndisabled.**\n\n### Writing an executable without stdlib\n\nControlling the entry point is possible in two ways: the `#[start]` attribute,\nor overriding the default shim for the C `main` function with your own.\n\nThe function marked `#[start]` is passed the command line parameters\nin the same format as C:\n\n```rust,ignore\n#![feature(lang_items, core_intrinsics)]\n#![feature(start)]\n#![no_std]\nuse core::intrinsics;\nuse core::panic::PanicInfo;\n\n// Pull in the system libc library for what crt0.o likely requires.\nextern crate libc;\n\n// Entry point for this program.\n#[start]\nfn start(_argc: isize, _argv: *const *const u8) -> isize {\n 0\n}\n\n// These functions are used by the compiler, but not\n// for a bare-bones hello world. These are normally\n// provided by libstd.\n#[lang = \"eh_personality\"]\n#[no_mangle]\npub extern fn rust_eh_personality() {\n}\n\n#[lang = \"panic_impl\"]\n#[no_mangle]\npub extern fn rust_begin_panic(info: &PanicInfo) -> ! {\n unsafe { intrinsics::abort() }\n}\n```\n\nTo override the compiler-inserted `main` shim, one has to disable it\nwith `#![no_main]` and then create the appropriate symbol with the\ncorrect ABI and the correct name, which requires overriding the\ncompiler's name mangling too:\n\n```rust,ignore\n#![feature(lang_items, core_intrinsics)]\n#![feature(start)]\n#![no_std]\n#![no_main]\nuse core::intrinsics;\nuse core::panic::PanicInfo;\n\n// Pull in the system libc library for what crt0.o likely requires.\nextern crate libc;\n\n// Entry point for this program.\n#[no_mangle] // ensure that this symbol is called `main` in the output\npub extern fn main(_argc: i32, _argv: *const *const u8) -> i32 {\n 0\n}\n\n// These functions are used by the compiler, but not\n// for a bare-bones hello world. These are normally\n// provided by libstd.\n#[lang = \"eh_personality\"]\n#[no_mangle]\npub extern fn rust_eh_personality() {\n}\n\n#[lang = \"panic_impl\"]\n#[no_mangle]\npub extern fn rust_begin_panic(info: &PanicInfo) -> ! {\n unsafe { intrinsics::abort() }\n}\n```\n\nIn many cases, you may need to manually link to the `compiler_builtins` crate\nwhen building a `no_std` binary. You may observe this via linker error messages\nsuch as \"```undefined reference to `__rust_probestack'```\".\n\n## More about the language items\n\nThe compiler currently makes a few assumptions about symbols which are\navailable in the executable to call. Normally these functions are provided by\nthe standard library, but without it you must define your own. These symbols\nare called \"language items\", and they each have an internal name, and then a\nsignature that an implementation must conform to.\n\nThe first of these functions, `rust_eh_personality`, is used by the failure\nmechanisms of the compiler. This is often mapped to GCC's personality function\n(see the [libstd implementation][unwind] for more information), but crates\nwhich do not trigger a panic can be assured that this function is never\ncalled. The language item's name is `eh_personality`.\n\n[unwind]: https://github.com/rust-lang/rust/blob/master/src/libpanic_unwind/gcc.rs\n\nThe second function, `rust_begin_panic`, is also used by the failure mechanisms of the\ncompiler. When a panic happens, this controls the message that's displayed on\nthe screen. While the language item's name is `panic_impl`, the symbol name is\n`rust_begin_panic`.\n\nFinally, a `eh_catch_typeinfo` static is needed for certain targets which\nimplement Rust panics on top of C++ exceptions.\n\n## List of all language items\n\nThis is a list of all language items in Rust along with where they are located in\nthe source code.\n\n- Primitives\n - `i8`: `libcore/num/mod.rs`\n - `i16`: `libcore/num/mod.rs`\n - `i32`: `libcore/num/mod.rs`\n - `i64`: `libcore/num/mod.rs`\n - `i128`: `libcore/num/mod.rs`\n - `isize`: `libcore/num/mod.rs`\n - `u8`: `libcore/num/mod.rs`\n - `u16`: `libcore/num/mod.rs`\n - `u32`: `libcore/num/mod.rs`\n - `u64`: `libcore/num/mod.rs`\n - `u128`: `libcore/num/mod.rs`\n - `usize`: `libcore/num/mod.rs`\n - `f32`: `libstd/f32.rs`\n - `f64`: `libstd/f64.rs`\n - `char`: `libcore/char.rs`\n - `slice`: `liballoc/slice.rs`\n - `str`: `liballoc/str.rs`\n - `const_ptr`: `libcore/ptr.rs`\n - `mut_ptr`: `libcore/ptr.rs`\n - `unsafe_cell`: `libcore/cell.rs`\n- Runtime\n - `start`: `libstd/rt.rs`\n - `eh_personality`: `libpanic_unwind/emcc.rs` (EMCC)\n - `eh_personality`: `libpanic_unwind/gcc.rs` (GNU)\n - `eh_personality`: `libpanic_unwind/seh.rs` (SEH)\n - `eh_catch_typeinfo`: `libpanic_unwind/emcc.rs` (EMCC)\n - `panic`: `libcore/panicking.rs`\n - `panic_bounds_check`: `libcore/panicking.rs`\n - `panic_impl`: `libcore/panicking.rs`\n - `panic_impl`: `libstd/panicking.rs`\n- Allocations\n - `owned_box`: `liballoc/boxed.rs`\n - `exchange_malloc`: `liballoc/heap.rs`\n - `box_free`: `liballoc/heap.rs`\n- Operands\n - `not`: `libcore/ops/bit.rs`\n - `bitand`: `libcore/ops/bit.rs`\n - `bitor`: `libcore/ops/bit.rs`\n - `bitxor`: `libcore/ops/bit.rs`\n - `shl`: `libcore/ops/bit.rs`\n - `shr`: `libcore/ops/bit.rs`\n - `bitand_assign`: `libcore/ops/bit.rs`\n - `bitor_assign`: `libcore/ops/bit.rs`\n - `bitxor_assign`: `libcore/ops/bit.rs`\n - `shl_assign`: `libcore/ops/bit.rs`\n - `shr_assign`: `libcore/ops/bit.rs`\n - `deref`: `libcore/ops/deref.rs`\n - `deref_mut`: `libcore/ops/deref.rs`\n - `index`: `libcore/ops/index.rs`\n - `index_mut`: `libcore/ops/index.rs`\n - `add`: `libcore/ops/arith.rs`\n - `sub`: `libcore/ops/arith.rs`\n - `mul`: `libcore/ops/arith.rs`\n - `div`: `libcore/ops/arith.rs`\n - `rem`: `libcore/ops/arith.rs`\n - `neg`: `libcore/ops/arith.rs`\n - `add_assign`: `libcore/ops/arith.rs`\n - `sub_assign`: `libcore/ops/arith.rs`\n - `mul_assign`: `libcore/ops/arith.rs`\n - `div_assign`: `libcore/ops/arith.rs`\n - `rem_assign`: `libcore/ops/arith.rs`\n - `eq`: `libcore/cmp.rs`\n - `ord`: `libcore/cmp.rs`\n- Functions\n - `fn`: `libcore/ops/function.rs`\n - `fn_mut`: `libcore/ops/function.rs`\n - `fn_once`: `libcore/ops/function.rs`\n - `generator_state`: `libcore/ops/generator.rs`\n - `generator`: `libcore/ops/generator.rs`\n- Other\n - `coerce_unsized`: `libcore/ops/unsize.rs`\n - `drop`: `libcore/ops/drop.rs`\n - `drop_in_place`: `libcore/ptr.rs`\n - `clone`: `libcore/clone.rs`\n - `copy`: `libcore/marker.rs`\n - `send`: `libcore/marker.rs`\n - `sized`: `libcore/marker.rs`\n - `unsize`: `libcore/marker.rs`\n - `sync`: `libcore/marker.rs`\n - `phantom_data`: `libcore/marker.rs`\n - `discriminant_kind`: `libcore/marker.rs`\n - `freeze`: `libcore/marker.rs`\n - `debug_trait`: `libcore/fmt/mod.rs`\n - `non_zero`: `libcore/nonzero.rs`\n - `arc`: `liballoc/sync.rs`\n - `rc`: `liballoc/rc.rs`\n" } , LintCompletion { label : "abi_msp430_interrupt" , description : "# `abi_msp430_interrupt`\n\nThe tracking issue for this feature is: [#38487]\n\n[#38487]: https://github.com/rust-lang/rust/issues/38487\n\n------------------------\n\nIn the MSP430 architecture, interrupt handlers have a special calling\nconvention. You can use the `\"msp430-interrupt\"` ABI to make the compiler apply\nthe right calling convention to the interrupt handlers you define.\n\n<!-- NOTE(ignore) this example is specific to the msp430 target -->\n\n``` rust,ignore\n#![feature(abi_msp430_interrupt)]\n#![no_std]\n\n// Place the interrupt handler at the appropriate memory address\n// (Alternatively, you can use `#[used]` and remove `pub` and `#[no_mangle]`)\n#[link_section = \"__interrupt_vector_10\"]\n#[no_mangle]\npub static TIM0_VECTOR: extern \"msp430-interrupt\" fn() = tim0;\n\n// The interrupt handler\nextern \"msp430-interrupt\" fn tim0() {\n // ..\n}\n```\n\n``` text\n$ msp430-elf-objdump -CD ./target/msp430/release/app\nDisassembly of section __interrupt_vector_10:\n\n0000fff2 <TIM0_VECTOR>:\n fff2: 00 c0 interrupt service routine at 0xc000\n\nDisassembly of section .text:\n\n0000c000 <int::tim0>:\n c000: 00 13 reti\n```\n" } , LintCompletion { label : "link_args" , description : "# `link_args`\n\nThe tracking issue for this feature is: [#29596]\n\n[#29596]: https://github.com/rust-lang/rust/issues/29596\n\n------------------------\n\nYou can tell `rustc` how to customize linking, and that is via the `link_args`\nattribute. This attribute is applied to `extern` blocks and specifies raw flags\nwhich need to get passed to the linker when producing an artifact. An example\nusage would be:\n\n```rust,no_run\n#![feature(link_args)]\n\n#[link_args = \"-foo -bar -baz\"]\nextern {}\n# fn main() {}\n```\n\nNote that this feature is currently hidden behind the `feature(link_args)` gate\nbecause this is not a sanctioned way of performing linking. Right now `rustc`\nshells out to the system linker (`gcc` on most systems, `link.exe` on MSVC), so\nit makes sense to provide extra command line arguments, but this will not\nalways be the case. In the future `rustc` may use LLVM directly to link native\nlibraries, in which case `link_args` will have no meaning. You can achieve the\nsame effect as the `link_args` attribute with the `-C link-args` argument to\n`rustc`.\n\nIt is highly recommended to *not* use this attribute, and rather use the more\nformal `#[link(...)]` attribute on `extern` blocks instead.\n" } , LintCompletion { label : "const_eval_limit" , description : "# `const_eval_limit`\n\nThe tracking issue for this feature is: [#67217]\n\n[#67217]: https://github.com/rust-lang/rust/issues/67217\n\nThe `const_eval_limit` allows someone to limit the evaluation steps the CTFE undertakes to evaluate a `const fn`.\n" } , LintCompletion { label : "negative_impls" , description : "# `negative_impls`\n\nThe tracking issue for this feature is [#68318].\n\n[#68318]: https://github.com/rust-lang/rust/issues/68318\n\n----\n\nWith the feature gate `negative_impls`, you can write negative impls as well as positive ones:\n\n```rust\n#![feature(negative_impls)]\ntrait DerefMut { }\nimpl<T: ?Sized> !DerefMut for &T { }\n```\n\nNegative impls indicate a semver guarantee that the given trait will not be implemented for the given types. Negative impls play an additional purpose for auto traits, described below.\n\nNegative impls have the following characteristics:\n\n* They do not have any items.\n* They must obey the orphan rules as if they were a positive impl.\n* They cannot \"overlap\" with any positive impls.\n\n## Semver interaction\n\nIt is a breaking change to remove a negative impl. Negative impls are a commitment not to implement the given trait for the named types.\n\n## Orphan and overlap rules\n\nNegative impls must obey the same orphan rules as a positive impl. This implies you cannot add a negative impl for types defined in upstream crates and so forth.\n\nSimilarly, negative impls cannot overlap with positive impls, again using the same \"overlap\" check that we ordinarily use to determine if two impls overlap. (Note that positive impls typically cannot overlap with one another either, except as permitted by specialization.)\n\n## Interaction with auto traits\n\nDeclaring a negative impl `impl !SomeAutoTrait for SomeType` for an\nauto-trait serves two purposes:\n\n* as with any trait, it declares that `SomeType` will never implement `SomeAutoTrait`;\n* it disables the automatic `SomeType: SomeAutoTrait` impl that would otherwise have been generated.\n\nNote that, at present, there is no way to indicate that a given type\ndoes not implement an auto trait *but that it may do so in the\nfuture*. For ordinary types, this is done by simply not declaring any\nimpl at all, but that is not an option for auto traits. A workaround\nis that one could embed a marker type as one of the fields, where the\nmarker type is `!AutoTrait`.\n\n## Immediate uses\n\nNegative impls are used to declare that `&T: !DerefMut` and `&mut T: !Clone`, as required to fix the soundness of `Pin` described in [#66544](https://github.com/rust-lang/rust/issues/66544).\n\nThis serves two purposes:\n\n* For proving the correctness of unsafe code, we can use that impl as evidence that no `DerefMut` or `Clone` impl exists.\n* It prevents downstream crates from creating such impls.\n" } , LintCompletion { label : "non_ascii_idents" , description : "# `non_ascii_idents`\n\nThe tracking issue for this feature is: [#55467]\n\n[#55467]: https://github.com/rust-lang/rust/issues/55467\n\n------------------------\n\nThe `non_ascii_idents` feature adds support for non-ASCII identifiers.\n\n## Examples\n\n```rust\n#![feature(non_ascii_idents)]\n\nconst ε: f64 = 0.00001f64;\nconst Π: f64 = 3.14f64;\n```\n\n## Changes to the language reference\n\n> **<sup>Lexer:<sup>** \n> IDENTIFIER : \n> &nbsp;&nbsp; &nbsp;&nbsp; XID_start XID_continue<sup>\\*</sup> \n> &nbsp;&nbsp; | `_` XID_continue<sup>+</sup> \n\nAn identifier is any nonempty Unicode string of the following form:\n\nEither\n\n * The first character has property [`XID_start`]\n * The remaining characters have property [`XID_continue`]\n\nOr\n\n * The first character is `_`\n * The identifier is more than one character, `_` alone is not an identifier\n * The remaining characters have property [`XID_continue`]\n\nthat does _not_ occur in the set of [strict keywords].\n\n> **Note**: [`XID_start`] and [`XID_continue`] as character properties cover the\n> character ranges used to form the more familiar C and Java language-family\n> identifiers.\n\n[`XID_start`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Start%3A%5D&abb=on&g=&i=\n[`XID_continue`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Continue%3A%5D&abb=on&g=&i=\n[strict keywords]: ../../reference/keywords.md#strict-keywords\n" } , LintCompletion { label : "transparent_unions" , description : "# `transparent_unions`\n\nThe tracking issue for this feature is [#60405]\n\n[#60405]: https://github.com/rust-lang/rust/issues/60405\n\n----\n\nThe `transparent_unions` feature allows you mark `union`s as\n`#[repr(transparent)]`. A `union` may be `#[repr(transparent)]` in exactly the\nsame conditions in which a `struct` may be `#[repr(transparent)]` (generally,\nthis means the `union` must have exactly one non-zero-sized field). Some\nconcrete illustrations follow.\n\n```rust\n#![feature(transparent_unions)]\n\n// This union has the same representation as `f32`.\n#[repr(transparent)]\nunion SingleFieldUnion {\n field: f32,\n}\n\n// This union has the same representation as `usize`.\n#[repr(transparent)]\nunion MultiFieldUnion {\n field: usize,\n nothing: (),\n}\n```\n\nFor consistency with transparent `struct`s, `union`s must have exactly one\nnon-zero-sized field. If all fields are zero-sized, the `union` must not be\n`#[repr(transparent)]`:\n\n```rust\n#![feature(transparent_unions)]\n\n// This (non-transparent) union is already valid in stable Rust:\npub union GoodUnion {\n pub nothing: (),\n}\n\n// Error: transparent union needs exactly one non-zero-sized field, but has 0\n// #[repr(transparent)]\n// pub union BadUnion {\n// pub nothing: (),\n// }\n```\n\nThe one exception is if the `union` is generic over `T` and has a field of type\n`T`, it may be `#[repr(transparent)]` even if `T` is a zero-sized type:\n\n```rust\n#![feature(transparent_unions)]\n\n// This union has the same representation as `T`.\n#[repr(transparent)]\npub union GenericUnion<T: Copy> { // Unions with non-`Copy` fields are unstable.\n pub field: T,\n pub nothing: (),\n}\n\n// This is okay even though `()` is a zero-sized type.\npub const THIS_IS_OKAY: GenericUnion<()> = GenericUnion { field: () };\n```\n\nLike transarent `struct`s, a transparent `union` of type `U` has the same\nlayout, size, and ABI as its single non-ZST field. If it is generic over a type\n`T`, and all its fields are ZSTs except for exactly one field of type `T`, then\nit has the same layout and ABI as `T` (even if `T` is a ZST when monomorphized).\n\nLike transparent `struct`s, transparent `union`s are FFI-safe if and only if\ntheir underlying representation type is also FFI-safe.\n\nA `union` may not be eligible for the same nonnull-style optimizations that a\n`struct` or `enum` (with the same fields) are eligible for. Adding\n`#[repr(transparent)]` to `union` does not change this. To give a more concrete\nexample, it is unspecified whether `size_of::<T>()` is equal to\n`size_of::<Option<T>>()`, where `T` is a `union` (regardless of whether or not\nit is transparent). The Rust compiler is free to perform this optimization if\npossible, but is not required to, and different compiler versions may differ in\ntheir application of these optimizations.\n" } , LintCompletion { label : "box_syntax" , description : "# `box_syntax`\n\nThe tracking issue for this feature is: [#49733]\n\n[#49733]: https://github.com/rust-lang/rust/issues/49733\n\nSee also [`box_patterns`](box-patterns.md)\n\n------------------------\n\nCurrently the only stable way to create a `Box` is via the `Box::new` method.\nAlso it is not possible in stable Rust to destructure a `Box` in a match\npattern. The unstable `box` keyword can be used to create a `Box`. An example\nusage would be:\n\n```rust\n#![feature(box_syntax)]\n\nfn main() {\n let b = box 5;\n}\n```\n" } , LintCompletion { label : "repr128" , description : "# `repr128`\n\nThe tracking issue for this feature is: [#56071]\n\n[#56071]: https://github.com/rust-lang/rust/issues/56071\n\n------------------------\n\nThe `repr128` feature adds support for `#[repr(u128)]` on `enum`s.\n\n```rust\n#![feature(repr128)]\n\n#[repr(u128)]\nenum Foo {\n Bar(u64),\n}\n```\n" } , LintCompletion { label : "member_constraints" , description : "# `member_constraints`\n\nThe tracking issue for this feature is: [#61997]\n\n[#61997]: https://github.com/rust-lang/rust/issues/61997\n\n------------------------\n\nThe `member_constraints` feature gate lets you use `impl Trait` syntax with\nmultiple unrelated lifetime parameters.\n\nA simple example is:\n\n```rust\n#![feature(member_constraints)]\n\ntrait Trait<'a, 'b> { }\nimpl<T> Trait<'_, '_> for T {}\n\nfn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> {\n (x, y)\n}\n\nfn main() { }\n```\n\nWithout the `member_constraints` feature gate, the above example is an\nerror because both `'a` and `'b` appear in the impl Trait bounds, but\nneither outlives the other.\n" } , LintCompletion { label : "link_cfg" , description : "# `link_cfg`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "c_variadic" , description : "# `c_variadic`\n\nThe tracking issue for this feature is: [#44930]\n\n[#44930]: https://github.com/rust-lang/rust/issues/44930\n\n------------------------\n\nThe `c_variadic` language feature enables C-variadic functions to be\ndefined in Rust. The may be called both from within Rust and via FFI.\n\n## Examples\n\n```rust\n#![feature(c_variadic)]\n\npub unsafe extern \"C\" fn add(n: usize, mut args: ...) -> usize {\n let mut sum = 0;\n for _ in 0..n {\n sum += args.arg::<usize>();\n }\n sum\n}\n```\n" } , LintCompletion { label : "abi_ptx" , description : "# `abi_ptx`\n\nThe tracking issue for this feature is: [#38788]\n\n[#38788]: https://github.com/rust-lang/rust/issues/38788\n\n------------------------\n\nWhen emitting PTX code, all vanilla Rust functions (`fn`) get translated to\n\"device\" functions. These functions are *not* callable from the host via the\nCUDA API so a crate with only device functions is not too useful!\n\nOTOH, \"global\" functions *can* be called by the host; you can think of them\nas the real public API of your crate. To produce a global function use the\n`\"ptx-kernel\"` ABI.\n\n<!-- NOTE(ignore) this example is specific to the nvptx targets -->\n\n``` rust,ignore\n#![feature(abi_ptx)]\n#![no_std]\n\npub unsafe extern \"ptx-kernel\" fn global_function() {\n device_function();\n}\n\npub fn device_function() {\n // ..\n}\n```\n\n``` text\n$ xargo rustc --target nvptx64-nvidia-cuda --release -- --emit=asm\n\n$ cat $(find -name '*.s')\n//\n// Generated by LLVM NVPTX Back-End\n//\n\n.version 3.2\n.target sm_20\n.address_size 64\n\n // .globl _ZN6kernel15global_function17h46111ebe6516b382E\n\n.visible .entry _ZN6kernel15global_function17h46111ebe6516b382E()\n{\n\n\n ret;\n}\n\n // .globl _ZN6kernel15device_function17hd6a0e4993bbf3f78E\n.visible .func _ZN6kernel15device_function17hd6a0e4993bbf3f78E()\n{\n\n\n ret;\n}\n```\n" } , LintCompletion { label : "ffi_pure" , description : "# `ffi_pure`\n\nThe `#[ffi_pure]` attribute applies clang's `pure` attribute to foreign\nfunctions declarations.\n\nThat is, `#[ffi_pure]` functions shall have no effects except for its return\nvalue, which shall not change across two consecutive function calls with\nthe same parameters.\n\nApplying the `#[ffi_pure]` attribute to a function that violates these\nrequirements is undefined behavior.\n\nThis attribute enables Rust to perform common optimizations, like sub-expression\nelimination and loop optimizations. Some common examples of pure functions are\n`strlen` or `memcmp`.\n\nThese optimizations are only applicable when the compiler can prove that no\nprogram state observable by the `#[ffi_pure]` function has changed between calls\nof the function, which could alter the result. See also the `#[ffi_const]`\nattribute, which provides stronger guarantees regarding the allowable behavior\nof a function, enabling further optimization.\n\n## Pitfalls\n\nA `#[ffi_pure]` function can read global memory through the function\nparameters (e.g. pointers), globals, etc. `#[ffi_pure]` functions are not\nreferentially-transparent, and are therefore more relaxed than `#[ffi_const]`\nfunctions.\n\nHowever, accesing global memory through volatile or atomic reads can violate the\nrequirement that two consecutive function calls shall return the same value.\n\nA `pure` function that returns unit has no effect on the abstract machine's\nstate.\n\nA `#[ffi_pure]` function must not diverge, neither via a side effect (e.g. a\ncall to `abort`) nor by infinite loops.\n\nWhen translating C headers to Rust FFI, it is worth verifying for which targets\nthe `pure` attribute is enabled in those headers, and using the appropriate\n`cfg` macros in the Rust side to match those definitions. While the semantics of\n`pure` are implemented identically by many C and C++ compilers, e.g., clang,\n[GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily\nimplemented in this way on all of them. It is therefore also worth verifying\nthat the semantics of the C toolchain used to compile the binary being linked\nagainst are compatible with those of the `#[ffi_pure]`.\n\n\n[ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacigdac.html\n[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-pure-function-attribute\n[IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_pure.htm\n" } , LintCompletion { label : "compiler_builtins" , description : "# `compiler_builtins`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "unboxed_closures" , description : "# `unboxed_closures`\n\nThe tracking issue for this feature is [#29625]\n\nSee Also: [`fn_traits`](../library-features/fn-traits.md)\n\n[#29625]: https://github.com/rust-lang/rust/issues/29625\n\n----\n\nThe `unboxed_closures` feature allows you to write functions using the `\"rust-call\"` ABI,\nrequired for implementing the [`Fn*`] family of traits. `\"rust-call\"` functions must have \nexactly one (non self) argument, a tuple representing the argument list.\n\n[`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html\n\n```rust\n#![feature(unboxed_closures)]\n\nextern \"rust-call\" fn add_args(args: (u32, u32)) -> u32 {\n args.0 + args.1\n}\n\nfn main() {}\n```\n" } , LintCompletion { label : "arbitrary_enum_discriminant" , description : "# `arbitrary_enum_discriminant`\n\nThe tracking issue for this feature is: [#60553]\n\n[#60553]: https://github.com/rust-lang/rust/issues/60553\n\n------------------------\n\nThe `arbitrary_enum_discriminant` feature permits tuple-like and\nstruct-like enum variants with `#[repr(<int-type>)]` to have explicit discriminants.\n\n## Examples\n\n```rust\n#![feature(arbitrary_enum_discriminant)]\n\n#[allow(dead_code)]\n#[repr(u8)]\nenum Enum {\n Unit = 3,\n Tuple(u16) = 2,\n Struct {\n a: u8,\n b: u16,\n } = 1,\n}\n\nimpl Enum {\n fn tag(&self) -> u8 {\n unsafe { *(self as *const Self as *const u8) }\n }\n}\n\nassert_eq!(3, Enum::Unit.tag());\nassert_eq!(2, Enum::Tuple(5).tag());\nassert_eq!(1, Enum::Struct{a: 7, b: 11}.tag());\n```\n" } , LintCompletion { label : "marker_trait_attr" , description : "# `marker_trait_attr`\n\nThe tracking issue for this feature is: [#29864]\n\n[#29864]: https://github.com/rust-lang/rust/issues/29864\n\n------------------------\n\nNormally, Rust keeps you from adding trait implementations that could\noverlap with each other, as it would be ambiguous which to use. This\nfeature, however, carves out an exception to that rule: a trait can\nopt-in to having overlapping implementations, at the cost that those\nimplementations are not allowed to override anything (and thus the\ntrait itself cannot have any associated items, as they're pointless\nwhen they'd need to do the same thing for every type anyway).\n\n```rust\n#![feature(marker_trait_attr)]\n\n#[marker] trait CheapToClone: Clone {}\n\nimpl<T: Copy> CheapToClone for T {}\n\n// These could potentially overlap with the blanket implementation above,\n// so are only allowed because CheapToClone is a marker trait.\nimpl<T: CheapToClone, U: CheapToClone> CheapToClone for (T, U) {}\nimpl<T: CheapToClone> CheapToClone for std::ops::Range<T> {}\n\nfn cheap_clone<T: CheapToClone>(t: T) -> T {\n t.clone()\n}\n```\n\nThis is expected to replace the unstable `overlapping_marker_traits`\nfeature, which applied to all empty traits (without needing an opt-in).\n" } , LintCompletion { label : "plugin_registrar" , description : "# `plugin_registrar`\n\nThe tracking issue for this feature is: [#29597]\n\n[#29597]: https://github.com/rust-lang/rust/issues/29597\n\nThis feature is part of \"compiler plugins.\" It will often be used with the\n[`plugin`] and `rustc_private` features as well. For more details, see\ntheir docs.\n\n[`plugin`]: plugin.md\n\n------------------------\n" } , LintCompletion { label : "profiler_runtime" , description : "# `profiler_runtime`\n\nThe tracking issue for this feature is: [#42524](https://github.com/rust-lang/rust/issues/42524).\n\n------------------------\n" } , LintCompletion { label : "trait_alias" , description : "# `trait_alias`\n\nThe tracking issue for this feature is: [#41517]\n\n[#41517]: https://github.com/rust-lang/rust/issues/41517\n\n------------------------\n\nThe `trait_alias` feature adds support for trait aliases. These allow aliases\nto be created for one or more traits (currently just a single regular trait plus\nany number of auto-traits), and used wherever traits would normally be used as\neither bounds or trait objects.\n\n```rust\n#![feature(trait_alias)]\n\ntrait Foo = std::fmt::Debug + Send;\ntrait Bar = Foo + Sync;\n\n// Use trait alias as bound on type parameter.\nfn foo<T: Foo>(v: &T) {\n println!(\"{:?}\", v);\n}\n\npub fn main() {\n foo(&1);\n\n // Use trait alias for trait objects.\n let a: &Bar = &123;\n println!(\"{:?}\", a);\n let b = Box::new(456) as Box<dyn Foo>;\n println!(\"{:?}\", b);\n}\n```\n" } , LintCompletion { label : "try_blocks" , description : "# `try_blocks`\n\nThe tracking issue for this feature is: [#31436]\n\n[#31436]: https://github.com/rust-lang/rust/issues/31436\n\n------------------------\n\nThe `try_blocks` feature adds support for `try` blocks. A `try`\nblock creates a new scope one can use the `?` operator in.\n\n```rust,edition2018\n#![feature(try_blocks)]\n\nuse std::num::ParseIntError;\n\nlet result: Result<i32, ParseIntError> = try {\n \"1\".parse::<i32>()?\n + \"2\".parse::<i32>()?\n + \"3\".parse::<i32>()?\n};\nassert_eq!(result, Ok(6));\n\nlet result: Result<i32, ParseIntError> = try {\n \"1\".parse::<i32>()?\n + \"foo\".parse::<i32>()?\n + \"3\".parse::<i32>()?\n};\nassert!(result.is_err());\n```\n" } , LintCompletion { label : "box_patterns" , description : "# `box_patterns`\n\nThe tracking issue for this feature is: [#29641]\n\n[#29641]: https://github.com/rust-lang/rust/issues/29641\n\nSee also [`box_syntax`](box-syntax.md)\n\n------------------------\n\nBox patterns let you match on `Box<T>`s:\n\n\n```rust\n#![feature(box_patterns)]\n\nfn main() {\n let b = Some(Box::new(5));\n match b {\n Some(box n) if n < 0 => {\n println!(\"Box contains negative number {}\", n);\n },\n Some(box n) if n >= 0 => {\n println!(\"Box contains non-negative number {}\", n);\n },\n None => {\n println!(\"No box\");\n },\n _ => unreachable!()\n }\n}\n```\n" } , LintCompletion { label : "crate_visibility_modifier" , description : "# `crate_visibility_modifier`\n\nThe tracking issue for this feature is: [#53120]\n\n[#53120]: https://github.com/rust-lang/rust/issues/53120\n\n-----\n\nThe `crate_visibility_modifier` feature allows the `crate` keyword to be used\nas a visibility modifier synonymous to `pub(crate)`, indicating that a type\n(function, _&c._) is to be visible to the entire enclosing crate, but not to\nother crates.\n\n```rust\n#![feature(crate_visibility_modifier)]\n\ncrate struct Foo {\n bar: usize,\n}\n```\n" } , LintCompletion { label : "allocator_internals" , description : "# `allocator_internals`\n\nThis feature does not have a tracking issue, it is an unstable implementation\ndetail of the `global_allocator` feature not intended for use outside the\ncompiler.\n\n------------------------\n" } , LintCompletion { label : "intrinsics" , description : "# `intrinsics`\n\nThe tracking issue for this feature is: None.\n\nIntrinsics are never intended to be stable directly, but intrinsics are often\nexported in some sort of stable manner. Prefer using the stable interfaces to\nthe intrinsic directly when you can.\n\n------------------------\n\n\nThese are imported as if they were FFI functions, with the special\n`rust-intrinsic` ABI. For example, if one was in a freestanding\ncontext, but wished to be able to `transmute` between types, and\nperform efficient pointer arithmetic, one would import those functions\nvia a declaration like\n\n```rust\n#![feature(intrinsics)]\n# fn main() {}\n\nextern \"rust-intrinsic\" {\n fn transmute<T, U>(x: T) -> U;\n\n fn offset<T>(dst: *const T, offset: isize) -> *const T;\n}\n```\n\nAs with any other FFI functions, these are always `unsafe` to call.\n\n" } , LintCompletion { label : "custom_test_frameworks" , description : "# `custom_test_frameworks`\n\nThe tracking issue for this feature is: [#50297]\n\n[#50297]: https://github.com/rust-lang/rust/issues/50297\n\n------------------------\n\nThe `custom_test_frameworks` feature allows the use of `#[test_case]` and `#![test_runner]`.\nAny function, const, or static can be annotated with `#[test_case]` causing it to be aggregated (like `#[test]`)\nand be passed to the test runner determined by the `#![test_runner]` crate attribute.\n\n```rust\n#![feature(custom_test_frameworks)]\n#![test_runner(my_runner)]\n\nfn my_runner(tests: &[&i32]) {\n for t in tests {\n if **t == 0 {\n println!(\"PASSED\");\n } else {\n println!(\"FAILED\");\n }\n }\n}\n\n#[test_case]\nconst WILL_PASS: i32 = 0;\n\n#[test_case]\nconst WILL_FAIL: i32 = 4;\n```\n\n" } , LintCompletion { label : "external_doc" , description : "# `external_doc`\n\nThe tracking issue for this feature is: [#44732]\n\nThe `external_doc` feature allows the use of the `include` parameter to the `#[doc]` attribute, to\ninclude external files in documentation. Use the attribute in place of, or in addition to, regular\ndoc comments and `#[doc]` attributes, and `rustdoc` will load the given file when it renders\ndocumentation for your crate.\n\nWith the following files in the same directory:\n\n`external-doc.md`:\n\n```markdown\n# My Awesome Type\n\nThis is the documentation for this spectacular type.\n```\n\n`lib.rs`:\n\n```no_run (needs-external-files)\n#![feature(external_doc)]\n\n#[doc(include = \"external-doc.md\")]\npub struct MyAwesomeType;\n```\n\n`rustdoc` will load the file `external-doc.md` and use it as the documentation for the `MyAwesomeType`\nstruct.\n\nWhen locating files, `rustdoc` will base paths in the `src/` directory, as if they were alongside the\n`lib.rs` for your crate. So if you want a `docs/` folder to live alongside the `src/` directory,\nstart your paths with `../docs/` for `rustdoc` to properly find the file.\n\nThis feature was proposed in [RFC #1990] and initially implemented in PR [#44781].\n\n[#44732]: https://github.com/rust-lang/rust/issues/44732\n[RFC #1990]: https://github.com/rust-lang/rfcs/pull/1990\n[#44781]: https://github.com/rust-lang/rust/pull/44781\n" } , LintCompletion { label : "rustc_attrs" , description : "# `rustc_attrs`\n\nThis feature has no tracking issue, and is therefore internal to\nthe compiler, not being intended for general use.\n\nNote: `rustc_attrs` enables many rustc-internal attributes and this page\nonly discuss a few of them.\n\n------------------------\n\nThe `rustc_attrs` feature allows debugging rustc type layouts by using\n`#[rustc_layout(...)]` to debug layout at compile time (it even works\nwith `cargo check`) as an alternative to `rustc -Z print-type-sizes`\nthat is way more verbose.\n\nOptions provided by `#[rustc_layout(...)]` are `debug`, `size`, `abi`.\nNote that it only work best with sized type without generics.\n\n## Examples\n\n```rust,ignore\n#![feature(rustc_attrs)]\n\n#[rustc_layout(abi, size)]\npub enum X {\n Y(u8, u8, u8),\n Z(isize),\n}\n```\n\nWhen that is compiled, the compiler will error with something like\n\n```text\nerror: abi: Aggregate { sized: true }\n --> src/lib.rs:4:1\n |\n4 | / pub enum T {\n5 | | Y(u8, u8, u8),\n6 | | Z(isize),\n7 | | }\n | |_^\n\nerror: size: Size { raw: 16 }\n --> src/lib.rs:4:1\n |\n4 | / pub enum T {\n5 | | Y(u8, u8, u8),\n6 | | Z(isize),\n7 | | }\n | |_^\n\nerror: aborting due to 2 previous errors\n```\n" } , LintCompletion { label : "profiler_runtime_lib" , description : "# `profiler_runtime_lib`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "fmt_internals" , description : "# `fmt_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "libstd_io_internals" , description : "# `libstd_io_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "dec2flt" , description : "# `dec2flt`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "try_trait" , description : "# `try_trait`\n\nThe tracking issue for this feature is: [#42327]\n\n[#42327]: https://github.com/rust-lang/rust/issues/42327\n\n------------------------\n\nThis introduces a new trait `Try` for extending the `?` operator to types\nother than `Result` (a part of [RFC 1859]). The trait provides the canonical\nway to _view_ a type in terms of a success/failure dichotomy. This will\nallow `?` to supplant the `try_opt!` macro on `Option` and the `try_ready!`\nmacro on `Poll`, among other things.\n\n[RFC 1859]: https://github.com/rust-lang/rfcs/pull/1859\n\nHere's an example implementation of the trait:\n\n```rust,ignore\n/// A distinct type to represent the `None` value of an `Option`.\n///\n/// This enables using the `?` operator on `Option`; it's rarely useful alone.\n#[derive(Debug)]\n#[unstable(feature = \"try_trait\", issue = \"42327\")]\npub struct None { _priv: () }\n\n#[unstable(feature = \"try_trait\", issue = \"42327\")]\nimpl<T> ops::Try for Option<T> {\n type Ok = T;\n type Error = None;\n\n fn into_result(self) -> Result<T, None> {\n self.ok_or(None { _priv: () })\n }\n\n fn from_ok(v: T) -> Self {\n Some(v)\n }\n\n fn from_error(_: None) -> Self {\n None\n }\n}\n```\n\nNote the `Error` associated type here is a new marker. The `?` operator\nallows interconversion between different `Try` implementers only when\nthe error type can be converted `Into` the error type of the enclosing\nfunction (or catch block). Having a distinct error type (as opposed to\njust `()`, or similar) restricts this to where it's semantically meaningful.\n" } , LintCompletion { label : "windows_handle" , description : "# `windows_handle`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "windows_stdio" , description : "# `windows_stdio`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "int_error_internals" , description : "# `int_error_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "core_panic" , description : "# `core_panic`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "core_private_bignum" , description : "# `core_private_bignum`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "derive_eq" , description : "# `derive_eq`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "thread_local_internals" , description : "# `thread_local_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "print_internals" , description : "# `print_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "c_void_variant" , description : "# `c_void_variant`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "fn_traits" , description : "# `fn_traits`\n\nThe tracking issue for this feature is [#29625]\n\nSee Also: [`unboxed_closures`](../language-features/unboxed-closures.md)\n\n[#29625]: https://github.com/rust-lang/rust/issues/29625\n\n----\n\nThe `fn_traits` feature allows for implementation of the [`Fn*`] traits\nfor creating custom closure-like types.\n\n[`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html\n\n```rust\n#![feature(unboxed_closures)]\n#![feature(fn_traits)]\n\nstruct Adder {\n a: u32\n}\n\nimpl FnOnce<(u32, )> for Adder {\n type Output = u32;\n extern \"rust-call\" fn call_once(self, b: (u32, )) -> Self::Output {\n self.a + b.0\n }\n}\n\nfn main() {\n let adder = Adder { a: 3 };\n assert_eq!(adder(2), 5);\n}\n```\n" } , LintCompletion { label : "rt" , description : "# `rt`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "default_free_fn" , description : "# `default_free_fn`\n\nThe tracking issue for this feature is: [#73014]\n\n[#73014]: https://github.com/rust-lang/rust/issues/73014\n\n------------------------\n\nAdds a free `default()` function to the `std::default` module. This function\njust forwards to [`Default::default()`], but may remove repetition of the word\n\"default\" from the call site.\n\nHere is an example:\n\n```rust\n#![feature(default_free_fn)]\nuse std::default::default;\n\n#[derive(Default)]\nstruct AppConfig {\n foo: FooConfig,\n bar: BarConfig,\n}\n\n#[derive(Default)]\nstruct FooConfig {\n foo: i32,\n}\n\n#[derive(Default)]\nstruct BarConfig {\n bar: f32,\n baz: u8,\n}\n\nfn main() {\n let options = AppConfig {\n foo: default(),\n bar: BarConfig {\n bar: 10.1,\n ..default()\n },\n };\n}\n```\n" } , LintCompletion { label : "update_panic_count" , description : "# `update_panic_count`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "str_internals" , description : "# `str_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "fd" , description : "# `fd`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "char_error_internals" , description : "# `char_error_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "core_intrinsics" , description : "# `core_intrinsics`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "windows_c" , description : "# `windows_c`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "libstd_sys_internals" , description : "# `libstd_sys_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "fd_read" , description : "# `fd_read`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "c_variadic" , description : "# `c_variadic`\n\nThe tracking issue for this feature is: [#44930]\n\n[#44930]: https://github.com/rust-lang/rust/issues/44930\n\n------------------------\n\nThe `c_variadic` library feature exposes the `VaList` structure,\nRust's analogue of C's `va_list` type.\n\n## Examples\n\n```rust\n#![feature(c_variadic)]\n\nuse std::ffi::VaList;\n\npub unsafe extern \"C\" fn vadd(n: usize, mut args: VaList) -> usize {\n let mut sum = 0;\n for _ in 0..n {\n sum += args.arg::<usize>();\n }\n sum\n}\n```\n" } , LintCompletion { label : "allocator_api" , description : "# `allocator_api`\n\nThe tracking issue for this feature is [#32838]\n\n[#32838]: https://github.com/rust-lang/rust/issues/32838\n\n------------------------\n\nSometimes you want the memory for one collection to use a different\nallocator than the memory for another collection. In this case,\nreplacing the global allocator is not a workable option. Instead,\nyou need to pass in an instance of an `AllocRef` to each collection\nfor which you want a custom allocator.\n\nTBD\n" } , LintCompletion { label : "flt2dec" , description : "# `flt2dec`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "global_asm" , description : "# `global_asm`\n\nThe tracking issue for this feature is: [#35119]\n\n[#35119]: https://github.com/rust-lang/rust/issues/35119\n\n------------------------\n\nThe `global_asm!` macro allows the programmer to write arbitrary\nassembly outside the scope of a function body, passing it through\n`rustc` and `llvm` to the assembler. The macro is a no-frills\ninterface to LLVM's concept of [module-level inline assembly]. That is,\nall caveats applicable to LLVM's module-level inline assembly apply\nto `global_asm!`.\n\n[module-level inline assembly]: http://llvm.org/docs/LangRef.html#module-level-inline-assembly\n\n`global_asm!` fills a role not currently satisfied by either `asm!`\nor `#[naked]` functions. The programmer has _all_ features of the\nassembler at their disposal. The linker will expect to resolve any\nsymbols defined in the inline assembly, modulo any symbols marked as\nexternal. It also means syntax for directives and assembly follow the\nconventions of the assembler in your toolchain.\n\nA simple usage looks like this:\n\n```rust,ignore\n# #![feature(global_asm)]\n# you also need relevant target_arch cfgs\nglobal_asm!(include_str!(\"something_neato.s\"));\n```\n\nAnd a more complicated usage looks like this:\n\n```rust,ignore\n# #![feature(global_asm)]\n# #![cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))]\n\npub mod sally {\n global_asm!(r#\"\n .global foo\n foo:\n jmp baz\n \"#);\n\n #[no_mangle]\n pub unsafe extern \"C\" fn baz() {}\n}\n\n// the symbols `foo` and `bar` are global, no matter where\n// `global_asm!` was used.\nextern \"C\" {\n fn foo();\n fn bar();\n}\n\npub mod harry {\n global_asm!(r#\"\n .global bar\n bar:\n jmp quux\n \"#);\n\n #[no_mangle]\n pub unsafe extern \"C\" fn quux() {}\n}\n```\n\nYou may use `global_asm!` multiple times, anywhere in your crate, in\nwhatever way suits you. The effect is as if you concatenated all\nusages and placed the larger, single usage in the crate root.\n\n------------------------\n\nIf you don't need quite as much power and flexibility as\n`global_asm!` provides, and you don't mind restricting your inline\nassembly to `fn` bodies only, you might try the\n[asm](asm.md) feature instead.\n" } , LintCompletion { label : "asm" , description : "# `asm`\n\nThe tracking issue for this feature is: [#72016]\n\n[#72016]: https://github.com/rust-lang/rust/issues/72016\n\n------------------------\n\nFor extremely low-level manipulations and performance reasons, one\nmight wish to control the CPU directly. Rust supports using inline\nassembly to do this via the `asm!` macro.\n\n# Guide-level explanation\n[guide-level-explanation]: #guide-level-explanation\n\nRust provides support for inline assembly via the `asm!` macro.\nIt can be used to embed handwritten assembly in the assembly output generated by the compiler.\nGenerally this should not be necessary, but might be where the required performance or timing\ncannot be otherwise achieved. Accessing low level hardware primitives, e.g. in kernel code, may also demand this functionality.\n\n> **Note**: the examples here are given in x86/x86-64 assembly, but other architectures are also supported.\n\nInline assembly is currently supported on the following architectures:\n- x86 and x86-64\n- ARM\n- AArch64\n- RISC-V\n- NVPTX\n- Hexagon\n\n## Basic usage\n\nLet us start with the simplest possible example:\n\n```rust,allow_fail\n# #![feature(asm)]\nunsafe {\n asm!(\"nop\");\n}\n```\n\nThis will insert a NOP (no operation) instruction into the assembly generated by the compiler.\nNote that all `asm!` invocations have to be inside an `unsafe` block, as they could insert\narbitrary instructions and break various invariants. The instructions to be inserted are listed\nin the first argument of the `asm!` macro as a string literal.\n\n## Inputs and outputs\n\nNow inserting an instruction that does nothing is rather boring. Let us do something that\nactually acts on data:\n\n```rust,allow_fail\n# #![feature(asm)]\nlet x: u64;\nunsafe {\n asm!(\"mov {}, 5\", out(reg) x);\n}\nassert_eq!(x, 5);\n```\n\nThis will write the value `5` into the `u64` variable `x`.\nYou can see that the string literal we use to specify instructions is actually a template string.\nIt is governed by the same rules as Rust [format strings][format-syntax].\nThe arguments that are inserted into the template however look a bit different then you may\nbe familiar with. First we need to specify if the variable is an input or an output of the\ninline assembly. In this case it is an output. We declared this by writing `out`.\nWe also need to specify in what kind of register the assembly expects the variable.\nIn this case we put it in an arbitrary general purpose register by specifying `reg`.\nThe compiler will choose an appropriate register to insert into\nthe template and will read the variable from there after the inline assembly finishes executing.\n\nLet us see another example that also uses an input:\n\n```rust,allow_fail\n# #![feature(asm)]\nlet i: u64 = 3;\nlet o: u64;\nunsafe {\n asm!(\n \"mov {0}, {1}\",\n \"add {0}, {number}\",\n out(reg) o,\n in(reg) i,\n number = const 5,\n );\n}\nassert_eq!(o, 8);\n```\n\nThis will add `5` to the input in variable `i` and write the result to variable `o`.\nThe particular way this assembly does this is first copying the value from `i` to the output,\nand then adding `5` to it.\n\nThe example shows a few things:\n\nFirst, we can see that `asm!` allows multiple template string arguments; each\none is treated as a separate line of assembly code, as if they were all joined\ntogether with newlines between them. This makes it easy to format assembly\ncode.\n\nSecond, we can see that inputs are declared by writing `in` instead of `out`.\n\nThird, one of our operands has a type we haven't seen yet, `const`.\nThis tells the compiler to expand this argument to value directly inside the assembly template.\nThis is only possible for constants and literals.\n\nFourth, we can see that we can specify an argument number, or name as in any format string.\nFor inline assembly templates this is particularly useful as arguments are often used more than once.\nFor more complex inline assembly using this facility is generally recommended, as it improves\nreadability, and allows reordering instructions without changing the argument order.\n\nWe can further refine the above example to avoid the `mov` instruction:\n\n```rust,allow_fail\n# #![feature(asm)]\nlet mut x: u64 = 3;\nunsafe {\n asm!(\"add {0}, {number}\", inout(reg) x, number = const 5);\n}\nassert_eq!(x, 8);\n```\n\nWe can see that `inout` is used to specify an argument that is both input and output.\nThis is different from specifying an input and output separately in that it is guaranteed to assign both to the same register.\n\nIt is also possible to specify different variables for the input and output parts of an `inout` operand:\n\n```rust,allow_fail\n# #![feature(asm)]\nlet x: u64 = 3;\nlet y: u64;\nunsafe {\n asm!(\"add {0}, {number}\", inout(reg) x => y, number = const 5);\n}\nassert_eq!(y, 8);\n```\n\n## Late output operands\n\nThe Rust compiler is conservative with its allocation of operands. It is assumed that an `out`\ncan be written at any time, and can therefore not share its location with any other argument.\nHowever, to guarantee optimal performance it is important to use as few registers as possible,\nso they won't have to be saved and reloaded around the inline assembly block.\nTo achieve this Rust provides a `lateout` specifier. This can be used on any output that is\nwritten only after all inputs have been consumed.\nThere is also a `inlateout` variant of this specifier.\n\nHere is an example where `inlateout` *cannot* be used:\n\n```rust,allow_fail\n# #![feature(asm)]\nlet mut a: u64 = 4;\nlet b: u64 = 4;\nlet c: u64 = 4;\nunsafe {\n asm!(\n \"add {0}, {1}\",\n \"add {0}, {2}\",\n inout(reg) a,\n in(reg) b,\n in(reg) c,\n );\n}\nassert_eq!(a, 12);\n```\n\nHere the compiler is free to allocate the same register for inputs `b` and `c` since it knows they have the same value. However it must allocate a separate register for `a` since it uses `inout` and not `inlateout`. If `inlateout` was used, then `a` and `c` could be allocated to the same register, in which case the first instruction to overwrite the value of `c` and cause the assembly code to produce the wrong result.\n\nHowever the following example can use `inlateout` since the output is only modified after all input registers have been read:\n\n```rust,allow_fail\n# #![feature(asm)]\nlet mut a: u64 = 4;\nlet b: u64 = 4;\nunsafe {\n asm!(\"add {0}, {1}\", inlateout(reg) a, in(reg) b);\n}\nassert_eq!(a, 8);\n```\n\nAs you can see, this assembly fragment will still work correctly if `a` and `b` are assigned to the same register.\n\n## Explicit register operands\n\nSome instructions require that the operands be in a specific register.\nTherefore, Rust inline assembly provides some more specific constraint specifiers.\nWhile `reg` is generally available on any architecture, these are highly architecture specific. E.g. for x86 the general purpose registers `eax`, `ebx`, `ecx`, `edx`, `ebp`, `esi`, and `edi`\namong others can be addressed by their name.\n\n```rust,allow_fail,no_run\n# #![feature(asm)]\nlet cmd = 0xd1;\nunsafe {\n asm!(\"out 0x64, eax\", in(\"eax\") cmd);\n}\n```\n\nIn this example we call the `out` instruction to output the content of the `cmd` variable\nto port `0x64`. Since the `out` instruction only accepts `eax` (and its sub registers) as operand\nwe had to use the `eax` constraint specifier.\n\nNote that unlike other operand types, explicit register operands cannot be used in the template string: you can't use `{}` and should write the register name directly instead. Also, they must appear at the end of the operand list after all other operand types.\n\nConsider this example which uses the x86 `mul` instruction:\n\n```rust,allow_fail\n# #![feature(asm)]\nfn mul(a: u64, b: u64) -> u128 {\n let lo: u64;\n let hi: u64;\n\n unsafe {\n asm!(\n // The x86 mul instruction takes rax as an implicit input and writes\n // the 128-bit result of the multiplication to rax:rdx.\n \"mul {}\",\n in(reg) a,\n inlateout(\"rax\") b => lo,\n lateout(\"rdx\") hi\n );\n }\n\n ((hi as u128) << 64) + lo as u128\n}\n```\n\nThis uses the `mul` instruction to multiply two 64-bit inputs with a 128-bit result.\nThe only explicit operand is a register, that we fill from the variable `a`.\nThe second operand is implicit, and must be the `rax` register, which we fill from the variable `b`.\nThe lower 64 bits of the result are stored in `rax` from which we fill the variable `lo`.\nThe higher 64 bits are stored in `rdx` from which we fill the variable `hi`.\n\n## Clobbered registers\n\nIn many cases inline assembly will modify state that is not needed as an output.\nUsually this is either because we have to use a scratch register in the assembly,\nor instructions modify state that we don't need to further examine.\nThis state is generally referred to as being \"clobbered\".\nWe need to tell the compiler about this since it may need to save and restore this state\naround the inline assembly block.\n\n```rust,allow_fail\n# #![feature(asm)]\nlet ebx: u32;\nlet ecx: u32;\n\nunsafe {\n asm!(\n \"cpuid\",\n // EAX 4 selects the \"Deterministic Cache Parameters\" CPUID leaf\n inout(\"eax\") 4 => _,\n // ECX 0 selects the L0 cache information.\n inout(\"ecx\") 0 => ecx,\n lateout(\"ebx\") ebx,\n lateout(\"edx\") _,\n );\n}\n\nprintln!(\n \"L1 Cache: {}\",\n ((ebx >> 22) + 1) * (((ebx >> 12) & 0x3ff) + 1) * ((ebx & 0xfff) + 1) * (ecx + 1)\n);\n```\n\nIn the example above we use the `cpuid` instruction to get the L1 cache size.\nThis instruction writes to `eax`, `ebx`, `ecx`, and `edx`, but for the cache size we only care about the contents of `ebx` and `ecx`.\n\nHowever we still need to tell the compiler that `eax` and `edx` have been modified so that it can save any values that were in these registers before the asm. This is done by declaring these as outputs but with `_` instead of a variable name, which indicates that the output value is to be discarded.\n\nThis can also be used with a general register class (e.g. `reg`) to obtain a scratch register for use inside the asm code:\n\n```rust,allow_fail\n# #![feature(asm)]\n// Multiply x by 6 using shifts and adds\nlet mut x: u64 = 4;\nunsafe {\n asm!(\n \"mov {tmp}, {x}\",\n \"shl {tmp}, 1\",\n \"shl {x}, 2\",\n \"add {x}, {tmp}\",\n x = inout(reg) x,\n tmp = out(reg) _,\n );\n}\nassert_eq!(x, 4 * 6);\n```\n\n## Symbol operands\n\nA special operand type, `sym`, allows you to use the symbol name of a `fn` or `static` in inline assembly code.\nThis allows you to call a function or access a global variable without needing to keep its address in a register.\n\n```rust,allow_fail\n# #![feature(asm)]\nextern \"C\" fn foo(arg: i32) {\n println!(\"arg = {}\", arg);\n}\n\nfn call_foo(arg: i32) {\n unsafe {\n asm!(\n \"call {}\",\n sym foo,\n // 1st argument in rdi, which is caller-saved\n inout(\"rdi\") arg => _,\n // All caller-saved registers must be marked as clobberred\n out(\"rax\") _, out(\"rcx\") _, out(\"rdx\") _, out(\"rsi\") _,\n out(\"r8\") _, out(\"r9\") _, out(\"r10\") _, out(\"r11\") _,\n out(\"xmm0\") _, out(\"xmm1\") _, out(\"xmm2\") _, out(\"xmm3\") _,\n out(\"xmm4\") _, out(\"xmm5\") _, out(\"xmm6\") _, out(\"xmm7\") _,\n out(\"xmm8\") _, out(\"xmm9\") _, out(\"xmm10\") _, out(\"xmm11\") _,\n out(\"xmm12\") _, out(\"xmm13\") _, out(\"xmm14\") _, out(\"xmm15\") _,\n )\n }\n}\n```\n\nNote that the `fn` or `static` item does not need to be public or `#[no_mangle]`:\nthe compiler will automatically insert the appropriate mangled symbol name into the assembly code.\n\n## Register template modifiers\n\nIn some cases, fine control is needed over the way a register name is formatted when inserted into the template string. This is needed when an architecture's assembly language has several names for the same register, each typically being a \"view\" over a subset of the register (e.g. the low 32 bits of a 64-bit register).\n\nBy default the compiler will always choose the name that refers to the full register size (e.g. `rax` on x86-64, `eax` on x86, etc).\n\nThis default can be overriden by using modifiers on the template string operands, just like you would with format strings:\n\n```rust,allow_fail\n# #![feature(asm)]\nlet mut x: u16 = 0xab;\n\nunsafe {\n asm!(\"mov {0:h}, {0:l}\", inout(reg_abcd) x);\n}\n\nassert_eq!(x, 0xabab);\n```\n\nIn this example, we use the `reg_abcd` register class to restrict the register allocator to the 4 legacy x86 register (`ax`, `bx`, `cx`, `dx`) of which the first two bytes can be addressed independently.\n\nLet us assume that the register allocator has chosen to allocate `x` in the `ax` register.\nThe `h` modifier will emit the register name for the high byte of that register and the `l` modifier will emit the register name for the low byte. The asm code will therefore be expanded as `mov ah, al` which copies the low byte of the value into the high byte.\n\nIf you use a smaller data type (e.g. `u16`) with an operand and forget the use template modifiers, the compiler will emit a warning and suggest the correct modifier to use.\n\n## Options\n\nBy default, an inline assembly block is treated the same way as an external FFI function call with a custom calling convention: it may read/write memory, have observable side effects, etc. However in many cases, it is desirable to give the compiler more information about what the assembly code is actually doing so that it can optimize better.\n\nLet's take our previous example of an `add` instruction:\n\n```rust,allow_fail\n# #![feature(asm)]\nlet mut a: u64 = 4;\nlet b: u64 = 4;\nunsafe {\n asm!(\n \"add {0}, {1}\",\n inlateout(reg) a, in(reg) b,\n options(pure, nomem, nostack),\n );\n}\nassert_eq!(a, 8);\n```\n\nOptions can be provided as an optional final argument to the `asm!` macro. We specified three options here:\n- `pure` means that the asm code has no observable side effects and that its output depends only on its inputs. This allows the compiler optimizer to call the inline asm fewer times or even eliminate it entirely.\n- `nomem` means that the asm code does not read or write to memory. By default the compiler will assume that inline assembly can read or write any memory address that is accessible to it (e.g. through a pointer passed as an operand, or a global).\n- `nostack` means that the asm code does not push any data onto the stack. This allows the compiler to use optimizations such as the stack red zone on x86-64 to avoid stack pointer adjustments.\n\nThese allow the compiler to better optimize code using `asm!`, for example by eliminating pure `asm!` blocks whose outputs are not needed.\n\nSee the reference for the full list of available options and their effects.\n\n# Reference-level explanation\n[reference-level-explanation]: #reference-level-explanation\n\nInline assembler is implemented as an unsafe macro `asm!()`.\nThe first argument to this macro is a template string literal used to build the final assembly.\nThe following arguments specify input and output operands.\nWhen required, options are specified as the final argument.\n\nThe following ABNF specifies the general syntax:\n\n```ignore\ndir_spec := \"in\" / \"out\" / \"lateout\" / \"inout\" / \"inlateout\"\nreg_spec := <register class> / \"<explicit register>\"\noperand_expr := expr / \"_\" / expr \"=>\" expr / expr \"=>\" \"_\"\nreg_operand := dir_spec \"(\" reg_spec \")\" operand_expr\noperand := reg_operand / \"const\" const_expr / \"sym\" path\noption := \"pure\" / \"nomem\" / \"readonly\" / \"preserves_flags\" / \"noreturn\" / \"att_syntax\"\noptions := \"options(\" option *[\",\" option] [\",\"] \")\"\nasm := \"asm!(\" format_string *(\",\" format_string) *(\",\" [ident \"=\"] operand) [\",\" options] [\",\"] \")\"\n```\n\nThe macro will initially be supported only on ARM, AArch64, Hexagon, x86, x86-64 and RISC-V targets. Support for more targets may be added in the future. The compiler will emit an error if `asm!` is used on an unsupported target.\n\n[format-syntax]: https://doc.rust-lang.org/std/fmt/#syntax\n\n## Template string arguments\n\nThe assembler template uses the same syntax as [format strings][format-syntax] (i.e. placeholders are specified by curly braces). The corresponding arguments are accessed in order, by index, or by name. However, implicit named arguments (introduced by [RFC #2795][rfc-2795]) are not supported.\n\nAn `asm!` invocation may have one or more template string arguments; an `asm!` with multiple template string arguments is treated as if all the strings were concatenated with a `\\n` between them. The expected usage is for each template string argument to correspond to a line of assembly code. All template string arguments must appear before any other arguments.\n\nAs with format strings, named arguments must appear after positional arguments. Explicit register operands must appear at the end of the operand list, after named arguments if any.\n\nExplicit register operands cannot be used by placeholders in the template string. All other named and positional operands must appear at least once in the template string, otherwise a compiler error is generated.\n\nThe exact assembly code syntax is target-specific and opaque to the compiler except for the way operands are substituted into the template string to form the code passed to the assembler.\n\nThe 5 targets specified in this RFC (x86, ARM, AArch64, RISC-V, Hexagon) all use the assembly code syntax of the GNU assembler (GAS). On x86, the `.intel_syntax noprefix` mode of GAS is used by default. On ARM, the `.syntax unified` mode is used. These targets impose an additional restriction on the assembly code: any assembler state (e.g. the current section which can be changed with `.section`) must be restored to its original value at the end of the asm string. Assembly code that does not conform to the GAS syntax will result in assembler-specific behavior.\n\n[rfc-2795]: https://github.com/rust-lang/rfcs/pull/2795\n\n## Operand type\n\nSeveral types of operands are supported:\n\n* `in(<reg>) <expr>`\n - `<reg>` can refer to a register class or an explicit register. The allocated register name is substituted into the asm template string.\n - The allocated register will contain the value of `<expr>` at the start of the asm code.\n - The allocated register must contain the same value at the end of the asm code (except if a `lateout` is allocated to the same register).\n* `out(<reg>) <expr>`\n - `<reg>` can refer to a register class or an explicit register. The allocated register name is substituted into the asm template string.\n - The allocated register will contain an undefined value at the start of the asm code.\n - `<expr>` must be a (possibly uninitialized) place expression, to which the contents of the allocated register is written to at the end of the asm code.\n - An underscore (`_`) may be specified instead of an expression, which will cause the contents of the register to be discarded at the end of the asm code (effectively acting as a clobber).\n* `lateout(<reg>) <expr>`\n - Identical to `out` except that the register allocator can reuse a register allocated to an `in`.\n - You should only write to the register after all inputs are read, otherwise you may clobber an input.\n* `inout(<reg>) <expr>`\n - `<reg>` can refer to a register class or an explicit register. The allocated register name is substituted into the asm template string.\n - The allocated register will contain the value of `<expr>` at the start of the asm code.\n - `<expr>` must be a mutable initialized place expression, to which the contents of the allocated register is written to at the end of the asm code.\n* `inout(<reg>) <in expr> => <out expr>`\n - Same as `inout` except that the initial value of the register is taken from the value of `<in expr>`.\n - `<out expr>` must be a (possibly uninitialized) place expression, to which the contents of the allocated register is written to at the end of the asm code.\n - An underscore (`_`) may be specified instead of an expression for `<out expr>`, which will cause the contents of the register to be discarded at the end of the asm code (effectively acting as a clobber).\n - `<in expr>` and `<out expr>` may have different types.\n* `inlateout(<reg>) <expr>` / `inlateout(<reg>) <in expr> => <out expr>`\n - Identical to `inout` except that the register allocator can reuse a register allocated to an `in` (this can happen if the compiler knows the `in` has the same initial value as the `inlateout`).\n - You should only write to the register after all inputs are read, otherwise you may clobber an input.\n* `const <expr>`\n - `<expr>` must be an integer or floating-point constant expression.\n - The value of the expression is formatted as a string and substituted directly into the asm template string.\n* `sym <path>`\n - `<path>` must refer to a `fn` or `static`.\n - A mangled symbol name referring to the item is substituted into the asm template string.\n - The substituted string does not include any modifiers (e.g. GOT, PLT, relocations, etc).\n - `<path>` is allowed to point to a `#[thread_local]` static, in which case the asm code can combine the symbol with relocations (e.g. `@plt`, `@TPOFF`) to read from thread-local data.\n\nOperand expressions are evaluated from left to right, just like function call arguments. After the `asm!` has executed, outputs are written to in left to right order. This is significant if two outputs point to the same place: that place will contain the value of the rightmost output.\n\n## Register operands\n\nInput and output operands can be specified either as an explicit register or as a register class from which the register allocator can select a register. Explicit registers are specified as string literals (e.g. `\"eax\"`) while register classes are specified as identifiers (e.g. `reg`). Using string literals for register names enables support for architectures that use special characters in register names, such as MIPS (`$0`, `$1`, etc).\n\nNote that explicit registers treat register aliases (e.g. `r14` vs `lr` on ARM) and smaller views of a register (e.g. `eax` vs `rax`) as equivalent to the base register. It is a compile-time error to use the same explicit register for two input operands or two output operands. Additionally, it is also a compile-time error to use overlapping registers (e.g. ARM VFP) in input operands or in output operands.\n\nOnly the following types are allowed as operands for inline assembly:\n- Integers (signed and unsigned)\n- Floating-point numbers\n- Pointers (thin only)\n- Function pointers\n- SIMD vectors (structs defined with `#[repr(simd)]` and which implement `Copy`). This includes architecture-specific vector types defined in `std::arch` such as `__m128` (x86) or `int8x16_t` (ARM).\n\nHere is the list of currently supported register classes:\n\n| Architecture | Register class | Registers | LLVM constraint code |\n| ------------ | -------------- | --------- | -------------------- |\n| x86 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `r[8-15]` (x86-64 only) | `r` |\n| x86 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | `Q` |\n| x86-32 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `ah`, `bh`, `ch`, `dh` | `q` |\n| x86-64 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `r[8-15]b`, `ah`\\*, `bh`\\*, `ch`\\*, `dh`\\* | `q` |\n| x86 | `xmm_reg` | `xmm[0-7]` (x86) `xmm[0-15]` (x86-64) | `x` |\n| x86 | `ymm_reg` | `ymm[0-7]` (x86) `ymm[0-15]` (x86-64) | `x` |\n| x86 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `v` |\n| x86 | `kreg` | `k[1-7]` | `Yk` |\n| AArch64 | `reg` | `x[0-28]`, `x30` | `r` |\n| AArch64 | `vreg` | `v[0-31]` | `w` |\n| AArch64 | `vreg_low16` | `v[0-15]` | `x` |\n| ARM | `reg` | `r[0-5]` `r7`\\*, `r[8-10]`, `r11`\\*, `r12`, `r14` | `r` |\n| ARM (Thumb) | `reg_thumb` | `r[0-r7]` | `l` |\n| ARM (ARM) | `reg_thumb` | `r[0-r10]`, `r12`, `r14` | `l` |\n| ARM | `sreg` | `s[0-31]` | `t` |\n| ARM | `sreg_low16` | `s[0-15]` | `x` |\n| ARM | `dreg` | `d[0-31]` | `w` |\n| ARM | `dreg_low16` | `d[0-15]` | `t` |\n| ARM | `dreg_low8` | `d[0-8]` | `x` |\n| ARM | `qreg` | `q[0-15]` | `w` |\n| ARM | `qreg_low8` | `q[0-7]` | `t` |\n| ARM | `qreg_low4` | `q[0-3]` | `x` |\n| NVPTX | `reg16` | None\\* | `h` |\n| NVPTX | `reg32` | None\\* | `r` |\n| NVPTX | `reg64` | None\\* | `l` |\n| RISC-V | `reg` | `x1`, `x[5-7]`, `x[9-15]`, `x[16-31]` (non-RV32E) | `r` |\n| RISC-V | `freg` | `f[0-31]` | `f` |\n| Hexagon | `reg` | `r[0-28]` | `r` |\n\n> **Note**: On x86 we treat `reg_byte` differently from `reg` because the compiler can allocate `al` and `ah` separately whereas `reg` reserves the whole register.\n>\n> Note #2: On x86-64 the high byte registers (e.g. `ah`) are only available when used as an explicit register. Specifying the `reg_byte` register class for an operand will always allocate a low byte register.\n>\n> Note #3: NVPTX doesn't have a fixed register set, so named registers are not supported.\n>\n> Note #4: On ARM the frame pointer is either `r7` or `r11` depending on the platform.\n\nAdditional register classes may be added in the future based on demand (e.g. MMX, x87, etc).\n\nEach register class has constraints on which value types they can be used with. This is necessary because the way a value is loaded into a register depends on its type. For example, on big-endian systems, loading a `i32x4` and a `i8x16` into a SIMD register may result in different register contents even if the byte-wise memory representation of both values is identical. The availability of supported types for a particular register class may depend on what target features are currently enabled.\n\n| Architecture | Register class | Target feature | Allowed types |\n| ------------ | -------------- | -------------- | ------------- |\n| x86-32 | `reg` | None | `i16`, `i32`, `f32` |\n| x86-64 | `reg` | None | `i16`, `i32`, `f32`, `i64`, `f64` |\n| x86 | `reg_byte` | None | `i8` |\n| x86 | `xmm_reg` | `sse` | `i32`, `f32`, `i64`, `f64`, <br> `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` |\n| x86 | `ymm_reg` | `avx` | `i32`, `f32`, `i64`, `f64`, <br> `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` <br> `i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4` |\n| x86 | `zmm_reg` | `avx512f` | `i32`, `f32`, `i64`, `f64`, <br> `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` <br> `i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4` <br> `i8x64`, `i16x32`, `i32x16`, `i64x8`, `f32x16`, `f64x8` |\n| x86 | `kreg` | `axv512f` | `i8`, `i16` |\n| x86 | `kreg` | `axv512bw` | `i32`, `i64` |\n| AArch64 | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` |\n| AArch64 | `vreg` | `fp` | `i8`, `i16`, `i32`, `f32`, `i64`, `f64`, <br> `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2`, `f64x1`, <br> `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` |\n| ARM | `reg` | None | `i8`, `i16`, `i32`, `f32` |\n| ARM | `sreg` | `vfp2` | `i32`, `f32` |\n| ARM | `dreg` | `vfp2` | `i64`, `f64`, `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2` |\n| ARM | `qreg` | `neon` | `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4` |\n| NVPTX | `reg16` | None | `i8`, `i16` |\n| NVPTX | `reg32` | None | `i8`, `i16`, `i32`, `f32` |\n| NVPTX | `reg64` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` |\n| RISC-V32 | `reg` | None | `i8`, `i16`, `i32`, `f32` |\n| RISC-V64 | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` |\n| RISC-V | `freg` | `f` | `f32` |\n| RISC-V | `freg` | `d` | `f64` |\n| Hexagon | `reg` | None | `i8`, `i16`, `i32`, `f32` |\n\n> **Note**: For the purposes of the above table pointers, function pointers and `isize`/`usize` are treated as the equivalent integer type (`i16`/`i32`/`i64` depending on the target).\n\nIf a value is of a smaller size than the register it is allocated in then the upper bits of that register will have an undefined value for inputs and will be ignored for outputs. The only exception is the `freg` register class on RISC-V where `f32` values are NaN-boxed in a `f64` as required by the RISC-V architecture.\n\nWhen separate input and output expressions are specified for an `inout` operand, both expressions must have the same type. The only exception is if both operands are pointers or integers, in which case they are only required to have the same size. This restriction exists because the register allocators in LLVM and GCC sometimes cannot handle tied operands with different types.\n\n## Register names\n\nSome registers have multiple names. These are all treated by the compiler as identical to the base register name. Here is the list of all supported register aliases:\n\n| Architecture | Base register | Aliases |\n| ------------ | ------------- | ------- |\n| x86 | `ax` | `eax`, `rax` |\n| x86 | `bx` | `ebx`, `rbx` |\n| x86 | `cx` | `ecx`, `rcx` |\n| x86 | `dx` | `edx`, `rdx` |\n| x86 | `si` | `esi`, `rsi` |\n| x86 | `di` | `edi`, `rdi` |\n| x86 | `bp` | `bpl`, `ebp`, `rbp` |\n| x86 | `sp` | `spl`, `esp`, `rsp` |\n| x86 | `ip` | `eip`, `rip` |\n| x86 | `st(0)` | `st` |\n| x86 | `r[8-15]` | `r[8-15]b`, `r[8-15]w`, `r[8-15]d` |\n| x86 | `xmm[0-31]` | `ymm[0-31]`, `zmm[0-31]` |\n| AArch64 | `x[0-30]` | `w[0-30]` |\n| AArch64 | `x29` | `fp` |\n| AArch64 | `x30` | `lr` |\n| AArch64 | `sp` | `wsp` |\n| AArch64 | `xzr` | `wzr` |\n| AArch64 | `v[0-31]` | `b[0-31]`, `h[0-31]`, `s[0-31]`, `d[0-31]`, `q[0-31]` |\n| ARM | `r[0-3]` | `a[1-4]` |\n| ARM | `r[4-9]` | `v[1-6]` |\n| ARM | `r9` | `rfp` |\n| ARM | `r10` | `sl` |\n| ARM | `r11` | `fp` |\n| ARM | `r12` | `ip` |\n| ARM | `r13` | `sp` |\n| ARM | `r14` | `lr` |\n| ARM | `r15` | `pc` |\n| RISC-V | `x0` | `zero` |\n| RISC-V | `x1` | `ra` |\n| RISC-V | `x2` | `sp` |\n| RISC-V | `x3` | `gp` |\n| RISC-V | `x4` | `tp` |\n| RISC-V | `x[5-7]` | `t[0-2]` |\n| RISC-V | `x8` | `fp`, `s0` |\n| RISC-V | `x9` | `s1` |\n| RISC-V | `x[10-17]` | `a[0-7]` |\n| RISC-V | `x[18-27]` | `s[2-11]` |\n| RISC-V | `x[28-31]` | `t[3-6]` |\n| RISC-V | `f[0-7]` | `ft[0-7]` |\n| RISC-V | `f[8-9]` | `fs[0-1]` |\n| RISC-V | `f[10-17]` | `fa[0-7]` |\n| RISC-V | `f[18-27]` | `fs[2-11]` |\n| RISC-V | `f[28-31]` | `ft[8-11]` |\n| Hexagon | `r29` | `sp` |\n| Hexagon | `r30` | `fr` |\n| Hexagon | `r31` | `lr` |\n\nSome registers cannot be used for input or output operands:\n\n| Architecture | Unsupported register | Reason |\n| ------------ | -------------------- | ------ |\n| All | `sp` | The stack pointer must be restored to its original value at the end of an asm code block. |\n| All | `bp` (x86), `x29` (AArch64), `x8` (RISC-V), `fr` (Hexagon) | The frame pointer cannot be used as an input or output. |\n| ARM | `r7` or `r11` | On ARM the frame pointer can be either `r7` or `r11` depending on the target. The frame pointer cannot be used as an input or output. |\n| ARM | `r6` | `r6` is used internally by LLVM as a base pointer and therefore cannot be used as an input or output. |\n| x86 | `k0` | This is a constant zero register which can't be modified. |\n| x86 | `ip` | This is the program counter, not a real register. |\n| x86 | `mm[0-7]` | MMX registers are not currently supported (but may be in the future). |\n| x86 | `st([0-7])` | x87 registers are not currently supported (but may be in the future). |\n| AArch64 | `xzr` | This is a constant zero register which can't be modified. |\n| ARM | `pc` | This is the program counter, not a real register. |\n| RISC-V | `x0` | This is a constant zero register which can't be modified. |\n| RISC-V | `gp`, `tp` | These registers are reserved and cannot be used as inputs or outputs. |\n| Hexagon | `lr` | This is the link register which cannot be used as an input or output. |\n\nIn some cases LLVM will allocate a \"reserved register\" for `reg` operands even though this register cannot be explicitly specified. Assembly code making use of reserved registers should be careful since `reg` operands may alias with those registers. Reserved registers are:\n- The frame pointer on all architectures.\n- `r6` on ARM.\n\n## Template modifiers\n\nThe placeholders can be augmented by modifiers which are specified after the `:` in the curly braces. These modifiers do not affect register allocation, but change the way operands are formatted when inserted into the template string. Only one modifier is allowed per template placeholder.\n\nThe supported modifiers are a subset of LLVM's (and GCC's) [asm template argument modifiers][llvm-argmod], but do not use the same letter codes.\n\n| Architecture | Register class | Modifier | Example output | LLVM modifier |\n| ------------ | -------------- | -------- | -------------- | ------------- |\n| x86-32 | `reg` | None | `eax` | `k` |\n| x86-64 | `reg` | None | `rax` | `q` |\n| x86-32 | `reg_abcd` | `l` | `al` | `b` |\n| x86-64 | `reg` | `l` | `al` | `b` |\n| x86 | `reg_abcd` | `h` | `ah` | `h` |\n| x86 | `reg` | `x` | `ax` | `w` |\n| x86 | `reg` | `e` | `eax` | `k` |\n| x86-64 | `reg` | `r` | `rax` | `q` |\n| x86 | `reg_byte` | None | `al` / `ah` | None |\n| x86 | `xmm_reg` | None | `xmm0` | `x` |\n| x86 | `ymm_reg` | None | `ymm0` | `t` |\n| x86 | `zmm_reg` | None | `zmm0` | `g` |\n| x86 | `*mm_reg` | `x` | `xmm0` | `x` |\n| x86 | `*mm_reg` | `y` | `ymm0` | `t` |\n| x86 | `*mm_reg` | `z` | `zmm0` | `g` |\n| x86 | `kreg` | None | `k1` | None |\n| AArch64 | `reg` | None | `x0` | `x` |\n| AArch64 | `reg` | `w` | `w0` | `w` |\n| AArch64 | `reg` | `x` | `x0` | `x` |\n| AArch64 | `vreg` | None | `v0` | None |\n| AArch64 | `vreg` | `v` | `v0` | None |\n| AArch64 | `vreg` | `b` | `b0` | `b` |\n| AArch64 | `vreg` | `h` | `h0` | `h` |\n| AArch64 | `vreg` | `s` | `s0` | `s` |\n| AArch64 | `vreg` | `d` | `d0` | `d` |\n| AArch64 | `vreg` | `q` | `q0` | `q` |\n| ARM | `reg` | None | `r0` | None |\n| ARM | `sreg` | None | `s0` | None |\n| ARM | `dreg` | None | `d0` | `P` |\n| ARM | `qreg` | None | `q0` | `q` |\n| ARM | `qreg` | `e` / `f` | `d0` / `d1` | `e` / `f` |\n| NVPTX | `reg16` | None | `rs0` | None |\n| NVPTX | `reg32` | None | `r0` | None |\n| NVPTX | `reg64` | None | `rd0` | None |\n| RISC-V | `reg` | None | `x1` | None |\n| RISC-V | `freg` | None | `f0` | None |\n| Hexagon | `reg` | None | `r0` | None |\n\n> Notes:\n> - on ARM `e` / `f`: this prints the low or high doubleword register name of a NEON quad (128-bit) register.\n> - on x86: our behavior for `reg` with no modifiers differs from what GCC does. GCC will infer the modifier based on the operand value type, while we default to the full register size.\n> - on x86 `xmm_reg`: the `x`, `t` and `g` LLVM modifiers are not yet implemented in LLVM (they are supported by GCC only), but this should be a simple change.\n\nAs stated in the previous section, passing an input value smaller than the register width will result in the upper bits of the register containing undefined values. This is not a problem if the inline asm only accesses the lower bits of the register, which can be done by using a template modifier to use a subregister name in the asm code (e.g. `ax` instead of `rax`). Since this an easy pitfall, the compiler will suggest a template modifier to use where appropriate given the input type. If all references to an operand already have modifiers then the warning is suppressed for that operand.\n\n[llvm-argmod]: http://llvm.org/docs/LangRef.html#asm-template-argument-modifiers\n\n## Options\n\nFlags are used to further influence the behavior of the inline assembly block.\nCurrently the following options are defined:\n- `pure`: The `asm` block has no side effects, and its outputs depend only on its direct inputs (i.e. the values themselves, not what they point to) or values read from memory (unless the `nomem` options is also set). This allows the compiler to execute the `asm` block fewer times than specified in the program (e.g. by hoisting it out of a loop) or even eliminate it entirely if the outputs are not used.\n- `nomem`: The `asm` blocks does not read or write to any memory. This allows the compiler to cache the values of modified global variables in registers across the `asm` block since it knows that they are not read or written to by the `asm`.\n- `readonly`: The `asm` block does not write to any memory. This allows the compiler to cache the values of unmodified global variables in registers across the `asm` block since it knows that they are not written to by the `asm`.\n- `preserves_flags`: The `asm` block does not modify the flags register (defined in the rules below). This allows the compiler to avoid recomputing the condition flags after the `asm` block.\n- `noreturn`: The `asm` block never returns, and its return type is defined as `!` (never). Behavior is undefined if execution falls through past the end of the asm code. A `noreturn` asm block behaves just like a function which doesn't return; notably, local variables in scope are not dropped before it is invoked.\n- `nostack`: The `asm` block does not push data to the stack, or write to the stack red-zone (if supported by the target). If this option is *not* used then the stack pointer is guaranteed to be suitably aligned (according to the target ABI) for a function call.\n- `att_syntax`: This option is only valid on x86, and causes the assembler to use the `.att_syntax prefix` mode of the GNU assembler. Register operands are substituted in with a leading `%`.\n\nThe compiler performs some additional checks on options:\n- The `nomem` and `readonly` options are mutually exclusive: it is a compile-time error to specify both.\n- The `pure` option must be combined with either the `nomem` or `readonly` options, otherwise a compile-time error is emitted.\n- It is a compile-time error to specify `pure` on an asm block with no outputs or only discarded outputs (`_`).\n- It is a compile-time error to specify `noreturn` on an asm block with outputs.\n\n## Rules for inline assembly\n\n- Any registers not specified as inputs will contain an undefined value on entry to the asm block.\n - An \"undefined value\" in the context of inline assembly means that the register can (non-deterministically) have any one of the possible values allowed by the architecture. Notably it is not the same as an LLVM `undef` which can have a different value every time you read it (since such a concept does not exist in assembly code).\n- Any registers not specified as outputs must have the same value upon exiting the asm block as they had on entry, otherwise behavior is undefined.\n - This only applies to registers which can be specified as an input or output. Other registers follow target-specific rules.\n - Note that a `lateout` may be allocated to the same register as an `in`, in which case this rule does not apply. Code should not rely on this however since it depends on the results of register allocation.\n- Behavior is undefined if execution unwinds out of an asm block.\n - This also applies if the assembly code calls a function which then unwinds.\n- The set of memory locations that assembly code is allowed the read and write are the same as those allowed for an FFI function.\n - Refer to the unsafe code guidelines for the exact rules.\n - If the `readonly` option is set, then only memory reads are allowed.\n - If the `nomem` option is set then no reads or writes to memory are allowed.\n - These rules do not apply to memory which is private to the asm code, such as stack space allocated within the asm block.\n- The compiler cannot assume that the instructions in the asm are the ones that will actually end up executed.\n - This effectively means that the compiler must treat the `asm!` as a black box and only take the interface specification into account, not the instructions themselves.\n - Runtime code patching is allowed, via target-specific mechanisms (outside the scope of this RFC).\n- Unless the `nostack` option is set, asm code is allowed to use stack space below the stack pointer.\n - On entry to the asm block the stack pointer is guaranteed to be suitably aligned (according to the target ABI) for a function call.\n - You are responsible for making sure you don't overflow the stack (e.g. use stack probing to ensure you hit a guard page).\n - You should adjust the stack pointer when allocating stack memory as required by the target ABI.\n - The stack pointer must be restored to its original value before leaving the asm block.\n- If the `noreturn` option is set then behavior is undefined if execution falls through to the end of the asm block.\n- If the `pure` option is set then behavior is undefined if the `asm` has side-effects other than its direct outputs. Behavior is also undefined if two executions of the `asm` code with the same inputs result in different outputs.\n - When used with the `nomem` option, \"inputs\" are just the direct inputs of the `asm!`.\n - When used with the `readonly` option, \"inputs\" comprise the direct inputs of the `asm!` and any memory that the `asm!` block is allowed to read.\n- These flags registers must be restored upon exiting the asm block if the `preserves_flags` option is set:\n - x86\n - Status flags in `EFLAGS` (CF, PF, AF, ZF, SF, OF).\n - Floating-point status word (all).\n - Floating-point exception flags in `MXCSR` (PE, UE, OE, ZE, DE, IE).\n - ARM\n - Condition flags in `CPSR` (N, Z, C, V)\n - Saturation flag in `CPSR` (Q)\n - Greater than or equal flags in `CPSR` (GE).\n - Condition flags in `FPSCR` (N, Z, C, V)\n - Saturation flag in `FPSCR` (QC)\n - Floating-point exception flags in `FPSCR` (IDC, IXC, UFC, OFC, DZC, IOC).\n - AArch64\n - Condition flags (`NZCV` register).\n - Floating-point status (`FPSR` register).\n - RISC-V\n - Floating-point exception flags in `fcsr` (`fflags`).\n- On x86, the direction flag (DF in `EFLAGS`) is clear on entry to an asm block and must be clear on exit.\n - Behavior is undefined if the direction flag is set on exiting an asm block.\n- The requirement of restoring the stack pointer and non-output registers to their original value only applies when exiting an `asm!` block.\n - This means that `asm!` blocks that never return (even if not marked `noreturn`) don't need to preserve these registers.\n - When returning to a different `asm!` block than you entered (e.g. for context switching), these registers must contain the value they had upon entering the `asm!` block that you are *exiting*.\n - You cannot exit an `asm!` block that has not been entered. Neither can you exit an `asm!` block that has already been exited.\n - You are responsible for switching any target-specific state (e.g. thread-local storage, stack bounds).\n - The set of memory locations that you may access is the intersection of those allowed by the `asm!` blocks you entered and exited.\n- You cannot assume that an `asm!` block will appear exactly once in the output binary. The compiler is allowed to instantiate multiple copies of the `asm!` block, for example when the function containing it is inlined in multiple places.\n - As a consequence, you should only use [local labels] inside inline assembly code. Defining symbols in assembly code may lead to assembler and/or linker errors due to duplicate symbol definitions.\n\n> **Note**: As a general rule, the flags covered by `preserves_flags` are those which are *not* preserved when performing a function call.\n\n[local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels\n" } , LintCompletion { label : "core_private_diy_float" , description : "# `core_private_diy_float`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "trace_macros" , description : "# `trace_macros`\n\nThe tracking issue for this feature is [#29598].\n\n[#29598]: https://github.com/rust-lang/rust/issues/29598\n\n------------------------\n\nWith `trace_macros` you can trace the expansion of macros in your code.\n\n## Examples\n\n```rust\n#![feature(trace_macros)]\n\nfn main() {\n trace_macros!(true);\n println!(\"Hello, Rust!\");\n trace_macros!(false);\n}\n```\n\nThe `cargo build` output:\n\n```txt\nnote: trace_macro\n --> src/main.rs:5:5\n |\n5 | println!(\"Hello, Rust!\");\n | ^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = note: expanding `println! { \"Hello, Rust!\" }`\n = note: to `print ! ( concat ! ( \"Hello, Rust!\" , \"\\n\" ) )`\n = note: expanding `print! { concat ! ( \"Hello, Rust!\" , \"\\n\" ) }`\n = note: to `$crate :: io :: _print ( format_args ! ( concat ! ( \"Hello, Rust!\" , \"\\n\" ) )\n )`\n\n Finished dev [unoptimized + debuginfo] target(s) in 0.60 secs\n```\n" } , LintCompletion { label : "concat_idents" , description : "# `concat_idents`\n\nThe tracking issue for this feature is: [#29599]\n\n[#29599]: https://github.com/rust-lang/rust/issues/29599\n\n------------------------\n\nThe `concat_idents` feature adds a macro for concatenating multiple identifiers\ninto one identifier.\n\n## Examples\n\n```rust\n#![feature(concat_idents)]\n\nfn main() {\n fn foobar() -> u32 { 23 }\n let f = concat_idents!(foo, bar);\n assert_eq!(f(), 23);\n}\n```" } , LintCompletion { label : "windows_net" , description : "# `windows_net`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "derive_clone_copy" , description : "# `derive_clone_copy`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "libstd_thread_internals" , description : "# `libstd_thread_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "test" , description : "# `test`\n\nThe tracking issue for this feature is: None.\n\n------------------------\n\nThe internals of the `test` crate are unstable, behind the `test` flag. The\nmost widely used part of the `test` crate are benchmark tests, which can test\nthe performance of your code. Let's make our `src/lib.rs` look like this\n(comments elided):\n\n```rust,ignore\n#![feature(test)]\n\nextern crate test;\n\npub fn add_two(a: i32) -> i32 {\n a + 2\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use test::Bencher;\n\n #[test]\n fn it_works() {\n assert_eq!(4, add_two(2));\n }\n\n #[bench]\n fn bench_add_two(b: &mut Bencher) {\n b.iter(|| add_two(2));\n }\n}\n```\n\nNote the `test` feature gate, which enables this unstable feature.\n\nWe've imported the `test` crate, which contains our benchmarking support.\nWe have a new function as well, with the `bench` attribute. Unlike regular\ntests, which take no arguments, benchmark tests take a `&mut Bencher`. This\n`Bencher` provides an `iter` method, which takes a closure. This closure\ncontains the code we'd like to benchmark.\n\nWe can run benchmark tests with `cargo bench`:\n\n```bash\n$ cargo bench\n Compiling adder v0.0.1 (file:///home/steve/tmp/adder)\n Running target/release/adder-91b3e234d4ed382a\n\nrunning 2 tests\ntest tests::it_works ... ignored\ntest tests::bench_add_two ... bench: 1 ns/iter (+/- 0)\n\ntest result: ok. 0 passed; 0 failed; 1 ignored; 1 measured\n```\n\nOur non-benchmark test was ignored. You may have noticed that `cargo bench`\ntakes a bit longer than `cargo test`. This is because Rust runs our benchmark\na number of times, and then takes the average. Because we're doing so little\nwork in this example, we have a `1 ns/iter (+/- 0)`, but this would show\nthe variance if there was one.\n\nAdvice on writing benchmarks:\n\n\n* Move setup code outside the `iter` loop; only put the part you want to measure inside\n* Make the code do \"the same thing\" on each iteration; do not accumulate or change state\n* Make the outer function idempotent too; the benchmark runner is likely to run\n it many times\n* Make the inner `iter` loop short and fast so benchmark runs are fast and the\n calibrator can adjust the run-length at fine resolution\n* Make the code in the `iter` loop do something simple, to assist in pinpointing\n performance improvements (or regressions)\n\n## Gotcha: optimizations\n\nThere's another tricky part to writing benchmarks: benchmarks compiled with\noptimizations activated can be dramatically changed by the optimizer so that\nthe benchmark is no longer benchmarking what one expects. For example, the\ncompiler might recognize that some calculation has no external effects and\nremove it entirely.\n\n```rust,ignore\n#![feature(test)]\n\nextern crate test;\nuse test::Bencher;\n\n#[bench]\nfn bench_xor_1000_ints(b: &mut Bencher) {\n b.iter(|| {\n (0..1000).fold(0, |old, new| old ^ new);\n });\n}\n```\n\ngives the following results\n\n```text\nrunning 1 test\ntest bench_xor_1000_ints ... bench: 0 ns/iter (+/- 0)\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 1 measured\n```\n\nThe benchmarking runner offers two ways to avoid this. Either, the closure that\nthe `iter` method receives can return an arbitrary value which forces the\noptimizer to consider the result used and ensures it cannot remove the\ncomputation entirely. This could be done for the example above by adjusting the\n`b.iter` call to\n\n```rust\n# struct X;\n# impl X { fn iter<T, F>(&self, _: F) where F: FnMut() -> T {} } let b = X;\nb.iter(|| {\n // Note lack of `;` (could also use an explicit `return`).\n (0..1000).fold(0, |old, new| old ^ new)\n});\n```\n\nOr, the other option is to call the generic `test::black_box` function, which\nis an opaque \"black box\" to the optimizer and so forces it to consider any\nargument as used.\n\n```rust\n#![feature(test)]\n\nextern crate test;\n\n# fn main() {\n# struct X;\n# impl X { fn iter<T, F>(&self, _: F) where F: FnMut() -> T {} } let b = X;\nb.iter(|| {\n let n = test::black_box(1000);\n\n (0..n).fold(0, |a, b| a ^ b)\n})\n# }\n```\n\nNeither of these read or modify the value, and are very cheap for small values.\nLarger values can be passed indirectly to reduce overhead (e.g.\n`black_box(&huge_struct)`).\n\nPerforming either of the above changes gives the following benchmarking results\n\n```text\nrunning 1 test\ntest bench_xor_1000_ints ... bench: 131 ns/iter (+/- 3)\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 1 measured\n```\n\nHowever, the optimizer can still modify a testcase in an undesirable manner\neven when using either of the above.\n" } , LintCompletion { label : "sort_internals" , description : "# `sort_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "is_sorted" , description : "# `is_sorted`\n\nThe tracking issue for this feature is: [#53485]\n\n[#53485]: https://github.com/rust-lang/rust/issues/53485\n\n------------------------\n\nAdd the methods `is_sorted`, `is_sorted_by` and `is_sorted_by_key` to `[T]`;\nadd the methods `is_sorted`, `is_sorted_by` and `is_sorted_by_key` to\n`Iterator`.\n" } , LintCompletion { label : "llvm_asm" , description : "# `llvm_asm`\n\nThe tracking issue for this feature is: [#70173]\n\n[#70173]: https://github.com/rust-lang/rust/issues/70173\n\n------------------------\n\nFor extremely low-level manipulations and performance reasons, one\nmight wish to control the CPU directly. Rust supports using inline\nassembly to do this via the `llvm_asm!` macro.\n\n```rust,ignore\nllvm_asm!(assembly template\n : output operands\n : input operands\n : clobbers\n : options\n );\n```\n\nAny use of `llvm_asm` is feature gated (requires `#![feature(llvm_asm)]` on the\ncrate to allow) and of course requires an `unsafe` block.\n\n> **Note**: the examples here are given in x86/x86-64 assembly, but\n> all platforms are supported.\n\n## Assembly template\n\nThe `assembly template` is the only required parameter and must be a\nliteral string (i.e. `\"\"`)\n\n```rust\n#![feature(llvm_asm)]\n\n#[cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))]\nfn foo() {\n unsafe {\n llvm_asm!(\"NOP\");\n }\n}\n\n// Other platforms:\n#[cfg(not(any(target_arch = \"x86\", target_arch = \"x86_64\")))]\nfn foo() { /* ... */ }\n\nfn main() {\n // ...\n foo();\n // ...\n}\n```\n\n(The `feature(llvm_asm)` and `#[cfg]`s are omitted from now on.)\n\nOutput operands, input operands, clobbers and options are all optional\nbut you must add the right number of `:` if you skip them:\n\n```rust\n# #![feature(llvm_asm)]\n# #[cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))]\n# fn main() { unsafe {\nllvm_asm!(\"xor %eax, %eax\"\n :\n :\n : \"eax\"\n );\n# } }\n# #[cfg(not(any(target_arch = \"x86\", target_arch = \"x86_64\")))]\n# fn main() {}\n```\n\nWhitespace also doesn't matter:\n\n```rust\n# #![feature(llvm_asm)]\n# #[cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))]\n# fn main() { unsafe {\nllvm_asm!(\"xor %eax, %eax\" ::: \"eax\");\n# } }\n# #[cfg(not(any(target_arch = \"x86\", target_arch = \"x86_64\")))]\n# fn main() {}\n```\n\n## Operands\n\nInput and output operands follow the same format: `:\n\"constraints1\"(expr1), \"constraints2\"(expr2), ...\"`. Output operand\nexpressions must be mutable place, or not yet assigned:\n\n```rust\n# #![feature(llvm_asm)]\n# #[cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))]\nfn add(a: i32, b: i32) -> i32 {\n let c: i32;\n unsafe {\n llvm_asm!(\"add $2, $0\"\n : \"=r\"(c)\n : \"0\"(a), \"r\"(b)\n );\n }\n c\n}\n# #[cfg(not(any(target_arch = \"x86\", target_arch = \"x86_64\")))]\n# fn add(a: i32, b: i32) -> i32 { a + b }\n\nfn main() {\n assert_eq!(add(3, 14159), 14162)\n}\n```\n\nIf you would like to use real operands in this position, however,\nyou are required to put curly braces `{}` around the register that\nyou want, and you are required to put the specific size of the\noperand. This is useful for very low level programming, where\nwhich register you use is important:\n\n```rust\n# #![feature(llvm_asm)]\n# #[cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))]\n# unsafe fn read_byte_in(port: u16) -> u8 {\nlet result: u8;\nllvm_asm!(\"in %dx, %al\" : \"={al}\"(result) : \"{dx}\"(port));\nresult\n# }\n```\n\n## Clobbers\n\nSome instructions modify registers which might otherwise have held\ndifferent values so we use the clobbers list to indicate to the\ncompiler not to assume any values loaded into those registers will\nstay valid.\n\n```rust\n# #![feature(llvm_asm)]\n# #[cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))]\n# fn main() { unsafe {\n// Put the value 0x200 in eax:\nllvm_asm!(\"mov $$0x200, %eax\" : /* no outputs */ : /* no inputs */ : \"eax\");\n# } }\n# #[cfg(not(any(target_arch = \"x86\", target_arch = \"x86_64\")))]\n# fn main() {}\n```\n\nInput and output registers need not be listed since that information\nis already communicated by the given constraints. Otherwise, any other\nregisters used either implicitly or explicitly should be listed.\n\nIf the assembly changes the condition code register `cc` should be\nspecified as one of the clobbers. Similarly, if the assembly modifies\nmemory, `memory` should also be specified.\n\n## Options\n\nThe last section, `options` is specific to Rust. The format is comma\nseparated literal strings (i.e. `:\"foo\", \"bar\", \"baz\"`). It's used to\nspecify some extra info about the inline assembly:\n\nCurrent valid options are:\n\n1. *volatile* - specifying this is analogous to\n `__asm__ __volatile__ (...)` in gcc/clang.\n2. *alignstack* - certain instructions expect the stack to be\n aligned a certain way (i.e. SSE) and specifying this indicates to\n the compiler to insert its usual stack alignment code\n3. *intel* - use intel syntax instead of the default AT&T.\n\n```rust\n# #![feature(llvm_asm)]\n# #[cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))]\n# fn main() {\nlet result: i32;\nunsafe {\n llvm_asm!(\"mov eax, 2\" : \"={eax}\"(result) : : : \"intel\")\n}\nprintln!(\"eax is currently {}\", result);\n# }\n# #[cfg(not(any(target_arch = \"x86\", target_arch = \"x86_64\")))]\n# fn main() {}\n```\n\n## More Information\n\nThe current implementation of the `llvm_asm!` macro is a direct binding to [LLVM's\ninline assembler expressions][llvm-docs], so be sure to check out [their\ndocumentation as well][llvm-docs] for more information about clobbers,\nconstraints, etc.\n\n[llvm-docs]: http://llvm.org/docs/LangRef.html#inline-assembler-expressions\n\nIf you need more power and don't mind losing some of the niceties of\n`llvm_asm!`, check out [global_asm](global-asm.md).\n" } , LintCompletion { label : "format_args_capture" , description : "# `format_args_capture`\n\nThe tracking issue for this feature is: [#67984]\n\n[#67984]: https://github.com/rust-lang/rust/issues/67984\n\n------------------------\n\nEnables `format_args!` (and macros which use `format_args!` in their implementation, such\nas `format!`, `print!` and `panic!`) to capture variables from the surrounding scope.\nThis avoids the need to pass named parameters when the binding in question\nalready exists in scope.\n\n```rust\n#![feature(format_args_capture)]\n\nlet (person, species, name) = (\"Charlie Brown\", \"dog\", \"Snoopy\");\n\n// captures named argument `person`\nprint!(\"Hello {person}\");\n\n// captures named arguments `species` and `name`\nformat!(\"The {species}'s name is {name}.\");\n```\n\nThis also works for formatting parameters such as width and precision:\n\n```rust\n#![feature(format_args_capture)]\n\nlet precision = 2;\nlet s = format!(\"{:.precision$}\", 1.324223);\n\nassert_eq!(&s, \"1.32\");\n```\n\nA non-exhaustive list of macros which benefit from this functionality include:\n- `format!`\n- `print!` and `println!`\n- `eprint!` and `eprintln!`\n- `write!` and `writeln!`\n- `panic!`\n- `unreachable!`\n- `unimplemented!`\n- `todo!`\n- `assert!` and similar\n- macros in many thirdparty crates, such as `log`\n" } , LintCompletion { label : "set_stdio" , description : "# `set_stdio`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } ] ;
diff --git a/crates/ide/src/completion/patterns.rs b/crates/ide/src/completion/patterns.rs
deleted file mode 100644
index cf6d5947d..000000000
--- a/crates/ide/src/completion/patterns.rs
+++ /dev/null
@@ -1,249 +0,0 @@
1//! Patterns telling us certain facts about current syntax element, they are used in completion context
2
3use syntax::{
4 algo::non_trivia_sibling,
5 ast::{self, LoopBodyOwner},
6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement,
7 SyntaxKind::*,
8 SyntaxNode, SyntaxToken,
9};
10
11#[cfg(test)]
12use crate::completion::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable};
13
14pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool {
15 not_same_range_ancestor(element)
16 .filter(|it| it.kind() == ASSOC_ITEM_LIST)
17 .and_then(|it| it.parent())
18 .filter(|it| it.kind() == TRAIT)
19 .is_some()
20}
21#[test]
22fn test_has_trait_parent() {
23 check_pattern_is_applicable(r"trait A { f<|> }", has_trait_parent);
24}
25
26pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool {
27 not_same_range_ancestor(element)
28 .filter(|it| it.kind() == ASSOC_ITEM_LIST)
29 .and_then(|it| it.parent())
30 .filter(|it| it.kind() == IMPL)
31 .is_some()
32}
33#[test]
34fn test_has_impl_parent() {
35 check_pattern_is_applicable(r"impl A { f<|> }", has_impl_parent);
36}
37
38pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool {
39 // Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`,
40 // where we only check the first parent with different text range.
41 element
42 .ancestors()
43 .find(|it| it.kind() == IMPL)
44 .map(|it| ast::Impl::cast(it).unwrap())
45 .map(|it| it.trait_().is_some())
46 .unwrap_or(false)
47}
48#[test]
49fn test_inside_impl_trait_block() {
50 check_pattern_is_applicable(r"impl Foo for Bar { f<|> }", inside_impl_trait_block);
51 check_pattern_is_applicable(r"impl Foo for Bar { fn f<|> }", inside_impl_trait_block);
52 check_pattern_is_not_applicable(r"impl A { f<|> }", inside_impl_trait_block);
53 check_pattern_is_not_applicable(r"impl A { fn f<|> }", inside_impl_trait_block);
54}
55
56pub(crate) fn has_field_list_parent(element: SyntaxElement) -> bool {
57 not_same_range_ancestor(element).filter(|it| it.kind() == RECORD_FIELD_LIST).is_some()
58}
59#[test]
60fn test_has_field_list_parent() {
61 check_pattern_is_applicable(r"struct Foo { f<|> }", has_field_list_parent);
62 check_pattern_is_applicable(r"struct Foo { f<|> pub f: i32}", has_field_list_parent);
63}
64
65pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool {
66 not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some()
67}
68#[test]
69fn test_has_block_expr_parent() {
70 check_pattern_is_applicable(r"fn my_fn() { let a = 2; f<|> }", has_block_expr_parent);
71}
72
73pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool {
74 element.ancestors().find(|it| it.kind() == IDENT_PAT).is_some()
75}
76#[test]
77fn test_has_bind_pat_parent() {
78 check_pattern_is_applicable(r"fn my_fn(m<|>) {}", has_bind_pat_parent);
79 check_pattern_is_applicable(r"fn my_fn() { let m<|> }", has_bind_pat_parent);
80}
81
82pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool {
83 not_same_range_ancestor(element)
84 .filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR)
85 .is_some()
86}
87#[test]
88fn test_has_ref_parent() {
89 check_pattern_is_applicable(r"fn my_fn(&m<|>) {}", has_ref_parent);
90 check_pattern_is_applicable(r"fn my() { let &m<|> }", has_ref_parent);
91}
92
93pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool {
94 let ancestor = not_same_range_ancestor(element);
95 if !ancestor.is_some() {
96 return true;
97 }
98 ancestor.filter(|it| it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST).is_some()
99}
100#[test]
101fn test_has_item_list_or_source_file_parent() {
102 check_pattern_is_applicable(r"i<|>", has_item_list_or_source_file_parent);
103 check_pattern_is_applicable(r"mod foo { f<|> }", has_item_list_or_source_file_parent);
104}
105
106pub(crate) fn is_match_arm(element: SyntaxElement) -> bool {
107 not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some()
108 && previous_sibling_or_ancestor_sibling(element)
109 .and_then(|it| it.into_token())
110 .filter(|it| it.kind() == FAT_ARROW)
111 .is_some()
112}
113#[test]
114fn test_is_match_arm() {
115 check_pattern_is_applicable(r"fn my_fn() { match () { () => m<|> } }", is_match_arm);
116}
117
118pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool {
119 element
120 .into_token()
121 .and_then(|it| previous_non_trivia_token(it))
122 .filter(|it| it.kind() == UNSAFE_KW)
123 .is_some()
124}
125#[test]
126fn test_unsafe_is_prev() {
127 check_pattern_is_applicable(r"unsafe i<|>", unsafe_is_prev);
128}
129
130pub(crate) fn if_is_prev(element: SyntaxElement) -> bool {
131 element
132 .into_token()
133 .and_then(|it| previous_non_trivia_token(it))
134 .filter(|it| it.kind() == IF_KW)
135 .is_some()
136}
137
138pub(crate) fn fn_is_prev(element: SyntaxElement) -> bool {
139 element
140 .into_token()
141 .and_then(|it| previous_non_trivia_token(it))
142 .filter(|it| it.kind() == FN_KW)
143 .is_some()
144}
145#[test]
146fn test_fn_is_prev() {
147 check_pattern_is_applicable(r"fn l<|>", fn_is_prev);
148}
149
150/// Check if the token previous to the previous one is `for`.
151/// For example, `for _ i<|>` => true.
152pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool {
153 element
154 .into_token()
155 .and_then(|it| previous_non_trivia_token(it))
156 .and_then(|it| previous_non_trivia_token(it))
157 .filter(|it| it.kind() == FOR_KW)
158 .is_some()
159}
160#[test]
161fn test_for_is_prev2() {
162 check_pattern_is_applicable(r"for i i<|>", for_is_prev2);
163}
164
165#[test]
166fn test_if_is_prev() {
167 check_pattern_is_applicable(r"if l<|>", if_is_prev);
168}
169
170pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool {
171 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT).is_some()
172}
173#[test]
174fn test_has_trait_as_prev_sibling() {
175 check_pattern_is_applicable(r"trait A w<|> {}", has_trait_as_prev_sibling);
176}
177
178pub(crate) fn has_impl_as_prev_sibling(element: SyntaxElement) -> bool {
179 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == IMPL).is_some()
180}
181#[test]
182fn test_has_impl_as_prev_sibling() {
183 check_pattern_is_applicable(r"impl A w<|> {}", has_impl_as_prev_sibling);
184}
185
186pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
187 let leaf = match element {
188 NodeOrToken::Node(node) => node,
189 NodeOrToken::Token(token) => token.parent(),
190 };
191 for node in leaf.ancestors() {
192 if node.kind() == FN || node.kind() == CLOSURE_EXPR {
193 break;
194 }
195 let loop_body = match_ast! {
196 match node {
197 ast::ForExpr(it) => it.loop_body(),
198 ast::WhileExpr(it) => it.loop_body(),
199 ast::LoopExpr(it) => it.loop_body(),
200 _ => None,
201 }
202 };
203 if let Some(body) = loop_body {
204 if body.syntax().text_range().contains_range(leaf.text_range()) {
205 return true;
206 }
207 }
208 }
209 false
210}
211
212fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> {
213 element
214 .ancestors()
215 .take_while(|it| it.text_range() == element.text_range())
216 .last()
217 .and_then(|it| it.parent())
218}
219
220fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
221 let mut token = token.prev_token();
222 while let Some(inner) = token.clone() {
223 if !inner.kind().is_trivia() {
224 return Some(inner);
225 } else {
226 token = inner.prev_token();
227 }
228 }
229 None
230}
231
232fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option<SyntaxElement> {
233 let token_sibling = non_trivia_sibling(element.clone(), Direction::Prev);
234 if let Some(sibling) = token_sibling {
235 Some(sibling)
236 } else {
237 // if not trying to find first ancestor which has such a sibling
238 let node = match element {
239 NodeOrToken::Node(node) => node,
240 NodeOrToken::Token(token) => token.parent(),
241 };
242 let range = node.text_range();
243 let top_node = node.ancestors().take_while(|it| it.text_range() == range).last()?;
244 let prev_sibling_node = top_node.ancestors().find(|it| {
245 non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some()
246 })?;
247 non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)
248 }
249}
diff --git a/crates/ide/src/completion/presentation.rs b/crates/ide/src/completion/presentation.rs
deleted file mode 100644
index a5172b87e..000000000
--- a/crates/ide/src/completion/presentation.rs
+++ /dev/null
@@ -1,1346 +0,0 @@
1//! This modules takes care of rendering various definitions as completion items.
2//! It also handles scoring (sorting) completions.
3
4use hir::{HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type};
5use itertools::Itertools;
6use syntax::ast::NameOwner;
7use test_utils::mark;
8
9use crate::{
10 completion::{
11 completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind,
12 CompletionKind, Completions,
13 },
14 display::{const_label, function_declaration, macro_label, type_label},
15 CompletionScore, RootDatabase,
16};
17
18impl Completions {
19 pub(crate) fn add_field(&mut self, ctx: &CompletionContext, field: hir::Field, ty: &Type) {
20 let is_deprecated = is_deprecated(field, ctx.db);
21 let name = field.name(ctx.db);
22 let mut completion_item =
23 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
24 .kind(CompletionItemKind::Field)
25 .detail(ty.display(ctx.db).to_string())
26 .set_documentation(field.docs(ctx.db))
27 .set_deprecated(is_deprecated);
28
29 if let Some(score) = compute_score(ctx, &ty, &name.to_string()) {
30 completion_item = completion_item.set_score(score);
31 }
32
33 completion_item.add_to(self);
34 }
35
36 pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) {
37 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), field.to_string())
38 .kind(CompletionItemKind::Field)
39 .detail(ty.display(ctx.db).to_string())
40 .add_to(self);
41 }
42
43 pub(crate) fn add_resolution(
44 &mut self,
45 ctx: &CompletionContext,
46 local_name: String,
47 resolution: &ScopeDef,
48 ) {
49 use hir::ModuleDef::*;
50
51 let completion_kind = match resolution {
52 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType,
53 _ => CompletionKind::Reference,
54 };
55
56 let kind = match resolution {
57 ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module,
58 ScopeDef::ModuleDef(Function(func)) => {
59 return self.add_function(ctx, *func, Some(local_name));
60 }
61 ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct,
62 // FIXME: add CompletionItemKind::Union
63 ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct,
64 ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum,
65
66 ScopeDef::ModuleDef(EnumVariant(var)) => {
67 return self.add_enum_variant(ctx, *var, Some(local_name));
68 }
69 ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const,
70 ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static,
71 ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait,
72 ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::TypeAlias,
73 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
74 ScopeDef::GenericParam(..) => CompletionItemKind::TypeParam,
75 ScopeDef::Local(..) => CompletionItemKind::Binding,
76 // (does this need its own kind?)
77 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => CompletionItemKind::TypeParam,
78 ScopeDef::MacroDef(mac) => {
79 return self.add_macro(ctx, Some(local_name), *mac);
80 }
81 ScopeDef::Unknown => {
82 return self.add(
83 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), local_name)
84 .kind(CompletionItemKind::UnresolvedReference),
85 );
86 }
87 };
88
89 let docs = match resolution {
90 ScopeDef::ModuleDef(Module(it)) => it.docs(ctx.db),
91 ScopeDef::ModuleDef(Adt(it)) => it.docs(ctx.db),
92 ScopeDef::ModuleDef(EnumVariant(it)) => it.docs(ctx.db),
93 ScopeDef::ModuleDef(Const(it)) => it.docs(ctx.db),
94 ScopeDef::ModuleDef(Static(it)) => it.docs(ctx.db),
95 ScopeDef::ModuleDef(Trait(it)) => it.docs(ctx.db),
96 ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(ctx.db),
97 _ => None,
98 };
99
100 let mut completion_item =
101 CompletionItem::new(completion_kind, ctx.source_range(), local_name.clone());
102 if let ScopeDef::Local(local) = resolution {
103 let ty = local.ty(ctx.db);
104 if !ty.is_unknown() {
105 completion_item = completion_item.detail(ty.display(ctx.db).to_string());
106 }
107 };
108
109 if let ScopeDef::Local(local) = resolution {
110 if let Some(score) = compute_score(ctx, &local.ty(ctx.db), &local_name) {
111 completion_item = completion_item.set_score(score);
112 }
113 }
114
115 // Add `<>` for generic types
116 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
117 if let Some(cap) = ctx.config.snippet_cap {
118 let has_non_default_type_params = match resolution {
119 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
120 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
121 _ => false,
122 };
123 if has_non_default_type_params {
124 mark::hit!(inserts_angle_brackets_for_generics);
125 completion_item = completion_item
126 .lookup_by(local_name.clone())
127 .label(format!("{}<…>", local_name))
128 .insert_snippet(cap, format!("{}<$0>", local_name));
129 }
130 }
131 }
132
133 completion_item.kind(kind).set_documentation(docs).add_to(self)
134 }
135
136 pub(crate) fn add_macro(
137 &mut self,
138 ctx: &CompletionContext,
139 name: Option<String>,
140 macro_: hir::MacroDef,
141 ) {
142 // FIXME: Currently proc-macro do not have ast-node,
143 // such that it does not have source
144 if macro_.is_proc_macro() {
145 return;
146 }
147
148 let name = match name {
149 Some(it) => it,
150 None => return,
151 };
152
153 let ast_node = macro_.source(ctx.db).value;
154 let detail = macro_label(&ast_node);
155
156 let docs = macro_.docs(ctx.db);
157
158 let mut builder = CompletionItem::new(
159 CompletionKind::Reference,
160 ctx.source_range(),
161 &format!("{}!", name),
162 )
163 .kind(CompletionItemKind::Macro)
164 .set_documentation(docs.clone())
165 .set_deprecated(is_deprecated(macro_, ctx.db))
166 .detail(detail);
167
168 let needs_bang = ctx.use_item_syntax.is_none() && !ctx.is_macro_call;
169 builder = match ctx.config.snippet_cap {
170 Some(cap) if needs_bang => {
171 let docs = docs.as_ref().map_or("", |s| s.as_str());
172 let (bra, ket) = guess_macro_braces(&name, docs);
173 builder
174 .insert_snippet(cap, format!("{}!{}$0{}", name, bra, ket))
175 .label(format!("{}!{}…{}", name, bra, ket))
176 .lookup_by(format!("{}!", name))
177 }
178 None if needs_bang => builder.insert_text(format!("{}!", name)),
179 _ => {
180 mark::hit!(dont_insert_macro_call_parens_unncessary);
181 builder.insert_text(name)
182 }
183 };
184
185 self.add(builder);
186 }
187
188 pub(crate) fn add_function(
189 &mut self,
190 ctx: &CompletionContext,
191 func: hir::Function,
192 local_name: Option<String>,
193 ) {
194 fn add_arg(arg: &str, ty: &Type, ctx: &CompletionContext) -> String {
195 if let Some(derefed_ty) = ty.remove_ref() {
196 for (name, local) in ctx.locals.iter() {
197 if name == arg && local.ty(ctx.db) == derefed_ty {
198 return (if ty.is_mutable_reference() { "&mut " } else { "&" }).to_string()
199 + &arg.to_string();
200 }
201 }
202 }
203 arg.to_string()
204 };
205 let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string());
206 let ast_node = func.source(ctx.db).value;
207
208 let mut builder =
209 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone())
210 .kind(if func.self_param(ctx.db).is_some() {
211 CompletionItemKind::Method
212 } else {
213 CompletionItemKind::Function
214 })
215 .set_documentation(func.docs(ctx.db))
216 .set_deprecated(is_deprecated(func, ctx.db))
217 .detail(function_declaration(&ast_node));
218
219 let params_ty = func.params(ctx.db);
220 let params = ast_node
221 .param_list()
222 .into_iter()
223 .flat_map(|it| it.params())
224 .zip(params_ty)
225 .flat_map(|(it, param_ty)| {
226 if let Some(pat) = it.pat() {
227 let name = pat.to_string();
228 let arg = name.trim_start_matches("mut ").trim_start_matches('_');
229 return Some(add_arg(arg, param_ty.ty(), ctx));
230 }
231 None
232 })
233 .collect();
234
235 builder = builder.add_call_parens(ctx, name, Params::Named(params));
236
237 self.add(builder)
238 }
239
240 pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
241 let ast_node = constant.source(ctx.db).value;
242 let name = match ast_node.name() {
243 Some(name) => name,
244 _ => return,
245 };
246 let detail = const_label(&ast_node);
247
248 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
249 .kind(CompletionItemKind::Const)
250 .set_documentation(constant.docs(ctx.db))
251 .set_deprecated(is_deprecated(constant, ctx.db))
252 .detail(detail)
253 .add_to(self);
254 }
255
256 pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) {
257 let type_def = type_alias.source(ctx.db).value;
258 let name = match type_def.name() {
259 Some(name) => name,
260 _ => return,
261 };
262 let detail = type_label(&type_def);
263
264 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
265 .kind(CompletionItemKind::TypeAlias)
266 .set_documentation(type_alias.docs(ctx.db))
267 .set_deprecated(is_deprecated(type_alias, ctx.db))
268 .detail(detail)
269 .add_to(self);
270 }
271
272 pub(crate) fn add_qualified_enum_variant(
273 &mut self,
274 ctx: &CompletionContext,
275 variant: hir::EnumVariant,
276 path: ModPath,
277 ) {
278 self.add_enum_variant_impl(ctx, variant, None, Some(path))
279 }
280
281 pub(crate) fn add_enum_variant(
282 &mut self,
283 ctx: &CompletionContext,
284 variant: hir::EnumVariant,
285 local_name: Option<String>,
286 ) {
287 self.add_enum_variant_impl(ctx, variant, local_name, None)
288 }
289
290 fn add_enum_variant_impl(
291 &mut self,
292 ctx: &CompletionContext,
293 variant: hir::EnumVariant,
294 local_name: Option<String>,
295 path: Option<ModPath>,
296 ) {
297 let is_deprecated = is_deprecated(variant, ctx.db);
298 let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string());
299 let qualified_name = match &path {
300 Some(it) => it.to_string(),
301 None => name.to_string(),
302 };
303 let detail_types = variant
304 .fields(ctx.db)
305 .into_iter()
306 .map(|field| (field.name(ctx.db), field.signature_ty(ctx.db)));
307 let variant_kind = variant.kind(ctx.db);
308 let detail = match variant_kind {
309 StructKind::Tuple | StructKind::Unit => format!(
310 "({})",
311 detail_types.map(|(_, t)| t.display(ctx.db).to_string()).format(", ")
312 ),
313 StructKind::Record => format!(
314 "{{ {} }}",
315 detail_types
316 .map(|(n, t)| format!("{}: {}", n, t.display(ctx.db).to_string()))
317 .format(", ")
318 ),
319 };
320 let mut res = CompletionItem::new(
321 CompletionKind::Reference,
322 ctx.source_range(),
323 qualified_name.clone(),
324 )
325 .kind(CompletionItemKind::EnumVariant)
326 .set_documentation(variant.docs(ctx.db))
327 .set_deprecated(is_deprecated)
328 .detail(detail);
329
330 if path.is_some() {
331 res = res.lookup_by(name);
332 }
333
334 if variant_kind == StructKind::Tuple {
335 mark::hit!(inserts_parens_for_tuple_enums);
336 let params = Params::Anonymous(variant.fields(ctx.db).len());
337 res = res.add_call_parens(ctx, qualified_name, params)
338 }
339
340 res.add_to(self);
341 }
342}
343
344pub(crate) fn compute_score(
345 ctx: &CompletionContext,
346 ty: &Type,
347 name: &str,
348) -> Option<CompletionScore> {
349 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
350 mark::hit!(record_field_type_match);
351 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
352 (struct_field.name(ctx.db).to_string(), struct_field.signature_ty(ctx.db))
353 } else if let Some(active_parameter) = &ctx.active_parameter {
354 mark::hit!(active_param_type_match);
355 (active_parameter.name.clone(), active_parameter.ty.clone())
356 } else {
357 return None;
358 };
359
360 // Compute score
361 // For the same type
362 if &active_type != ty {
363 return None;
364 }
365
366 let mut res = CompletionScore::TypeMatch;
367
368 // If same type + same name then go top position
369 if active_name == name {
370 res = CompletionScore::TypeAndNameMatch
371 }
372
373 Some(res)
374}
375
376enum Params {
377 Named(Vec<String>),
378 Anonymous(usize),
379}
380
381impl Params {
382 fn len(&self) -> usize {
383 match self {
384 Params::Named(xs) => xs.len(),
385 Params::Anonymous(len) => *len,
386 }
387 }
388
389 fn is_empty(&self) -> bool {
390 self.len() == 0
391 }
392}
393
394impl Builder {
395 fn add_call_parens(mut self, ctx: &CompletionContext, name: String, params: Params) -> Builder {
396 if !ctx.config.add_call_parenthesis {
397 return self;
398 }
399 if ctx.use_item_syntax.is_some() {
400 mark::hit!(no_parens_in_use_item);
401 return self;
402 }
403 if ctx.is_pattern_call {
404 mark::hit!(dont_duplicate_pattern_parens);
405 return self;
406 }
407 if ctx.is_call {
408 return self;
409 }
410
411 // Don't add parentheses if the expected type is some function reference.
412 if let Some(ty) = &ctx.expected_type {
413 if ty.is_fn() {
414 mark::hit!(no_call_parens_if_fn_ptr_needed);
415 return self;
416 }
417 }
418
419 let cap = match ctx.config.snippet_cap {
420 Some(it) => it,
421 None => return self,
422 };
423 // If not an import, add parenthesis automatically.
424 mark::hit!(inserts_parens_for_function_calls);
425
426 let (snippet, label) = if params.is_empty() {
427 (format!("{}()$0", name), format!("{}()", name))
428 } else {
429 self = self.trigger_call_info();
430 let snippet = match (ctx.config.add_call_argument_snippets, params) {
431 (true, Params::Named(params)) => {
432 let function_params_snippet =
433 params.iter().enumerate().format_with(", ", |(index, param_name), f| {
434 f(&format_args!("${{{}:{}}}", index + 1, param_name))
435 });
436 format!("{}({})$0", name, function_params_snippet)
437 }
438 _ => {
439 mark::hit!(suppress_arg_snippets);
440 format!("{}($0)", name)
441 }
442 };
443
444 (snippet, format!("{}(…)", name))
445 };
446 self.lookup_by(name).label(label).insert_snippet(cap, snippet)
447 }
448}
449
450fn is_deprecated(node: impl HasAttrs, db: &RootDatabase) -> bool {
451 node.attrs(db).by_key("deprecated").exists()
452}
453
454fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static str) {
455 let mut votes = [0, 0, 0];
456 for (idx, s) in docs.match_indices(&macro_name) {
457 let (before, after) = (&docs[..idx], &docs[idx + s.len()..]);
458 // Ensure to match the full word
459 if after.starts_with('!')
460 && !before.ends_with(|c: char| c == '_' || c.is_ascii_alphanumeric())
461 {
462 // It may have spaces before the braces like `foo! {}`
463 match after[1..].chars().find(|&c| !c.is_whitespace()) {
464 Some('{') => votes[0] += 1,
465 Some('[') => votes[1] += 1,
466 Some('(') => votes[2] += 1,
467 _ => {}
468 }
469 }
470 }
471
472 // Insert a space before `{}`.
473 // We prefer the last one when some votes equal.
474 let (_vote, (bra, ket)) = votes
475 .iter()
476 .zip(&[(" {", "}"), ("[", "]"), ("(", ")")])
477 .max_by_key(|&(&vote, _)| vote)
478 .unwrap();
479 (*bra, *ket)
480}
481
482#[cfg(test)]
483mod tests {
484 use std::cmp::Reverse;
485
486 use expect_test::{expect, Expect};
487 use test_utils::mark;
488
489 use crate::{
490 completion::{
491 test_utils::{
492 check_edit, check_edit_with_config, do_completion, get_all_completion_items,
493 },
494 CompletionConfig, CompletionKind,
495 },
496 CompletionScore,
497 };
498
499 fn check(ra_fixture: &str, expect: Expect) {
500 let actual = do_completion(ra_fixture, CompletionKind::Reference);
501 expect.assert_debug_eq(&actual);
502 }
503
504 fn check_scores(ra_fixture: &str, expect: Expect) {
505 fn display_score(score: Option<CompletionScore>) -> &'static str {
506 match score {
507 Some(CompletionScore::TypeMatch) => "[type]",
508 Some(CompletionScore::TypeAndNameMatch) => "[type+name]",
509 None => "[]".into(),
510 }
511 }
512
513 let mut completions = get_all_completion_items(CompletionConfig::default(), ra_fixture);
514 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string()));
515 let actual = completions
516 .into_iter()
517 .filter(|it| it.completion_kind == CompletionKind::Reference)
518 .map(|it| {
519 let tag = it.kind().unwrap().tag();
520 let score = display_score(it.score());
521 format!("{} {} {}\n", tag, it.label(), score)
522 })
523 .collect::<String>();
524 expect.assert_eq(&actual);
525 }
526
527 #[test]
528 fn enum_detail_includes_record_fields() {
529 check(
530 r#"
531enum Foo { Foo { x: i32, y: i32 } }
532
533fn main() { Foo::Fo<|> }
534"#,
535 expect![[r#"
536 [
537 CompletionItem {
538 label: "Foo",
539 source_range: 54..56,
540 delete: 54..56,
541 insert: "Foo",
542 kind: EnumVariant,
543 detail: "{ x: i32, y: i32 }",
544 },
545 ]
546 "#]],
547 );
548 }
549
550 #[test]
551 fn enum_detail_doesnt_include_tuple_fields() {
552 check(
553 r#"
554enum Foo { Foo (i32, i32) }
555
556fn main() { Foo::Fo<|> }
557"#,
558 expect![[r#"
559 [
560 CompletionItem {
561 label: "Foo(…)",
562 source_range: 46..48,
563 delete: 46..48,
564 insert: "Foo($0)",
565 kind: EnumVariant,
566 lookup: "Foo",
567 detail: "(i32, i32)",
568 trigger_call_info: true,
569 },
570 ]
571 "#]],
572 );
573 }
574
575 #[test]
576 fn enum_detail_just_parentheses_for_unit() {
577 check(
578 r#"
579enum Foo { Foo }
580
581fn main() { Foo::Fo<|> }
582"#,
583 expect![[r#"
584 [
585 CompletionItem {
586 label: "Foo",
587 source_range: 35..37,
588 delete: 35..37,
589 insert: "Foo",
590 kind: EnumVariant,
591 detail: "()",
592 },
593 ]
594 "#]],
595 );
596 }
597
598 #[test]
599 fn sets_deprecated_flag_in_completion_items() {
600 check(
601 r#"
602#[deprecated]
603fn something_deprecated() {}
604#[deprecated(since = "1.0.0")]
605fn something_else_deprecated() {}
606
607fn main() { som<|> }
608"#,
609 expect![[r#"
610 [
611 CompletionItem {
612 label: "main()",
613 source_range: 121..124,
614 delete: 121..124,
615 insert: "main()$0",
616 kind: Function,
617 lookup: "main",
618 detail: "fn main()",
619 },
620 CompletionItem {
621 label: "something_deprecated()",
622 source_range: 121..124,
623 delete: 121..124,
624 insert: "something_deprecated()$0",
625 kind: Function,
626 lookup: "something_deprecated",
627 detail: "fn something_deprecated()",
628 deprecated: true,
629 },
630 CompletionItem {
631 label: "something_else_deprecated()",
632 source_range: 121..124,
633 delete: 121..124,
634 insert: "something_else_deprecated()$0",
635 kind: Function,
636 lookup: "something_else_deprecated",
637 detail: "fn something_else_deprecated()",
638 deprecated: true,
639 },
640 ]
641 "#]],
642 );
643
644 check(
645 r#"
646struct A { #[deprecated] the_field: u32 }
647fn foo() { A { the<|> } }
648"#,
649 expect![[r#"
650 [
651 CompletionItem {
652 label: "the_field",
653 source_range: 57..60,
654 delete: 57..60,
655 insert: "the_field",
656 kind: Field,
657 detail: "u32",
658 deprecated: true,
659 },
660 ]
661 "#]],
662 );
663 }
664
665 #[test]
666 fn renders_docs() {
667 check(
668 r#"
669struct S {
670 /// Field docs
671 foo:
672}
673impl S {
674 /// Method docs
675 fn bar(self) { self.<|> }
676}"#,
677 expect![[r#"
678 [
679 CompletionItem {
680 label: "bar()",
681 source_range: 94..94,
682 delete: 94..94,
683 insert: "bar()$0",
684 kind: Method,
685 lookup: "bar",
686 detail: "fn bar(self)",
687 documentation: Documentation(
688 "Method docs",
689 ),
690 },
691 CompletionItem {
692 label: "foo",
693 source_range: 94..94,
694 delete: 94..94,
695 insert: "foo",
696 kind: Field,
697 detail: "{unknown}",
698 documentation: Documentation(
699 "Field docs",
700 ),
701 },
702 ]
703 "#]],
704 );
705
706 check(
707 r#"
708use self::my<|>;
709
710/// mod docs
711mod my { }
712
713/// enum docs
714enum E {
715 /// variant docs
716 V
717}
718use self::E::*;
719"#,
720 expect![[r#"
721 [
722 CompletionItem {
723 label: "E",
724 source_range: 10..12,
725 delete: 10..12,
726 insert: "E",
727 kind: Enum,
728 documentation: Documentation(
729 "enum docs",
730 ),
731 },
732 CompletionItem {
733 label: "V",
734 source_range: 10..12,
735 delete: 10..12,
736 insert: "V",
737 kind: EnumVariant,
738 detail: "()",
739 documentation: Documentation(
740 "variant docs",
741 ),
742 },
743 CompletionItem {
744 label: "my",
745 source_range: 10..12,
746 delete: 10..12,
747 insert: "my",
748 kind: Module,
749 documentation: Documentation(
750 "mod docs",
751 ),
752 },
753 ]
754 "#]],
755 )
756 }
757
758 #[test]
759 fn dont_render_attrs() {
760 check(
761 r#"
762struct S;
763impl S {
764 #[inline]
765 fn the_method(&self) { }
766}
767fn foo(s: S) { s.<|> }
768"#,
769 expect![[r#"
770 [
771 CompletionItem {
772 label: "the_method()",
773 source_range: 81..81,
774 delete: 81..81,
775 insert: "the_method()$0",
776 kind: Method,
777 lookup: "the_method",
778 detail: "fn the_method(&self)",
779 },
780 ]
781 "#]],
782 )
783 }
784
785 #[test]
786 fn inserts_parens_for_function_calls() {
787 mark::check!(inserts_parens_for_function_calls);
788 check_edit(
789 "no_args",
790 r#"
791fn no_args() {}
792fn main() { no_<|> }
793"#,
794 r#"
795fn no_args() {}
796fn main() { no_args()$0 }
797"#,
798 );
799
800 check_edit(
801 "with_args",
802 r#"
803fn with_args(x: i32, y: String) {}
804fn main() { with_<|> }
805"#,
806 r#"
807fn with_args(x: i32, y: String) {}
808fn main() { with_args(${1:x}, ${2:y})$0 }
809"#,
810 );
811
812 check_edit(
813 "foo",
814 r#"
815struct S;
816impl S {
817 fn foo(&self) {}
818}
819fn bar(s: &S) { s.f<|> }
820"#,
821 r#"
822struct S;
823impl S {
824 fn foo(&self) {}
825}
826fn bar(s: &S) { s.foo()$0 }
827"#,
828 );
829
830 check_edit(
831 "foo",
832 r#"
833struct S {}
834impl S {
835 fn foo(&self, x: i32) {}
836}
837fn bar(s: &S) {
838 s.f<|>
839}
840"#,
841 r#"
842struct S {}
843impl S {
844 fn foo(&self, x: i32) {}
845}
846fn bar(s: &S) {
847 s.foo(${1:x})$0
848}
849"#,
850 );
851 }
852
853 #[test]
854 fn suppress_arg_snippets() {
855 mark::check!(suppress_arg_snippets);
856 check_edit_with_config(
857 CompletionConfig { add_call_argument_snippets: false, ..CompletionConfig::default() },
858 "with_args",
859 r#"
860fn with_args(x: i32, y: String) {}
861fn main() { with_<|> }
862"#,
863 r#"
864fn with_args(x: i32, y: String) {}
865fn main() { with_args($0) }
866"#,
867 );
868 }
869
870 #[test]
871 fn strips_underscores_from_args() {
872 check_edit(
873 "foo",
874 r#"
875fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
876fn main() { f<|> }
877"#,
878 r#"
879fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
880fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
881"#,
882 );
883 }
884
885 #[test]
886 fn insert_ref_when_matching_local_in_scope() {
887 check_edit(
888 "ref_arg",
889 r#"
890struct Foo {}
891fn ref_arg(x: &Foo) {}
892fn main() {
893 let x = Foo {};
894 ref_ar<|>
895}
896"#,
897 r#"
898struct Foo {}
899fn ref_arg(x: &Foo) {}
900fn main() {
901 let x = Foo {};
902 ref_arg(${1:&x})$0
903}
904"#,
905 );
906 }
907
908 #[test]
909 fn insert_mut_ref_when_matching_local_in_scope() {
910 check_edit(
911 "ref_arg",
912 r#"
913struct Foo {}
914fn ref_arg(x: &mut Foo) {}
915fn main() {
916 let x = Foo {};
917 ref_ar<|>
918}
919"#,
920 r#"
921struct Foo {}
922fn ref_arg(x: &mut Foo) {}
923fn main() {
924 let x = Foo {};
925 ref_arg(${1:&mut x})$0
926}
927"#,
928 );
929 }
930
931 #[test]
932 fn insert_ref_when_matching_local_in_scope_for_method() {
933 check_edit(
934 "apply_foo",
935 r#"
936struct Foo {}
937struct Bar {}
938impl Bar {
939 fn apply_foo(&self, x: &Foo) {}
940}
941
942fn main() {
943 let x = Foo {};
944 let y = Bar {};
945 y.<|>
946}
947"#,
948 r#"
949struct Foo {}
950struct Bar {}
951impl Bar {
952 fn apply_foo(&self, x: &Foo) {}
953}
954
955fn main() {
956 let x = Foo {};
957 let y = Bar {};
958 y.apply_foo(${1:&x})$0
959}
960"#,
961 );
962 }
963
964 #[test]
965 fn trim_mut_keyword_in_func_completion() {
966 check_edit(
967 "take_mutably",
968 r#"
969fn take_mutably(mut x: &i32) {}
970
971fn main() {
972 take_m<|>
973}
974"#,
975 r#"
976fn take_mutably(mut x: &i32) {}
977
978fn main() {
979 take_mutably(${1:x})$0
980}
981"#,
982 );
983 }
984
985 #[test]
986 fn inserts_parens_for_tuple_enums() {
987 mark::check!(inserts_parens_for_tuple_enums);
988 check_edit(
989 "Some",
990 r#"
991enum Option<T> { Some(T), None }
992use Option::*;
993fn main() -> Option<i32> {
994 Som<|>
995}
996"#,
997 r#"
998enum Option<T> { Some(T), None }
999use Option::*;
1000fn main() -> Option<i32> {
1001 Some($0)
1002}
1003"#,
1004 );
1005 check_edit(
1006 "Some",
1007 r#"
1008enum Option<T> { Some(T), None }
1009use Option::*;
1010fn main(value: Option<i32>) {
1011 match value {
1012 Som<|>
1013 }
1014}
1015"#,
1016 r#"
1017enum Option<T> { Some(T), None }
1018use Option::*;
1019fn main(value: Option<i32>) {
1020 match value {
1021 Some($0)
1022 }
1023}
1024"#,
1025 );
1026 }
1027
1028 #[test]
1029 fn dont_duplicate_pattern_parens() {
1030 mark::check!(dont_duplicate_pattern_parens);
1031 check_edit(
1032 "Var",
1033 r#"
1034enum E { Var(i32) }
1035fn main() {
1036 match E::Var(92) {
1037 E::<|>(92) => (),
1038 }
1039}
1040"#,
1041 r#"
1042enum E { Var(i32) }
1043fn main() {
1044 match E::Var(92) {
1045 E::Var(92) => (),
1046 }
1047}
1048"#,
1049 );
1050 }
1051
1052 #[test]
1053 fn no_call_parens_if_fn_ptr_needed() {
1054 mark::check!(no_call_parens_if_fn_ptr_needed);
1055 check_edit(
1056 "foo",
1057 r#"
1058fn foo(foo: u8, bar: u8) {}
1059struct ManualVtable { f: fn(u8, u8) }
1060
1061fn main() -> ManualVtable {
1062 ManualVtable { f: f<|> }
1063}
1064"#,
1065 r#"
1066fn foo(foo: u8, bar: u8) {}
1067struct ManualVtable { f: fn(u8, u8) }
1068
1069fn main() -> ManualVtable {
1070 ManualVtable { f: foo }
1071}
1072"#,
1073 );
1074 }
1075
1076 #[test]
1077 fn no_parens_in_use_item() {
1078 mark::check!(no_parens_in_use_item);
1079 check_edit(
1080 "foo",
1081 r#"
1082mod m { pub fn foo() {} }
1083use crate::m::f<|>;
1084"#,
1085 r#"
1086mod m { pub fn foo() {} }
1087use crate::m::foo;
1088"#,
1089 );
1090 }
1091
1092 #[test]
1093 fn no_parens_in_call() {
1094 check_edit(
1095 "foo",
1096 r#"
1097fn foo(x: i32) {}
1098fn main() { f<|>(); }
1099"#,
1100 r#"
1101fn foo(x: i32) {}
1102fn main() { foo(); }
1103"#,
1104 );
1105 check_edit(
1106 "foo",
1107 r#"
1108struct Foo;
1109impl Foo { fn foo(&self){} }
1110fn f(foo: &Foo) { foo.f<|>(); }
1111"#,
1112 r#"
1113struct Foo;
1114impl Foo { fn foo(&self){} }
1115fn f(foo: &Foo) { foo.foo(); }
1116"#,
1117 );
1118 }
1119
1120 #[test]
1121 fn inserts_angle_brackets_for_generics() {
1122 mark::check!(inserts_angle_brackets_for_generics);
1123 check_edit(
1124 "Vec",
1125 r#"
1126struct Vec<T> {}
1127fn foo(xs: Ve<|>)
1128"#,
1129 r#"
1130struct Vec<T> {}
1131fn foo(xs: Vec<$0>)
1132"#,
1133 );
1134 check_edit(
1135 "Vec",
1136 r#"
1137type Vec<T> = (T,);
1138fn foo(xs: Ve<|>)
1139"#,
1140 r#"
1141type Vec<T> = (T,);
1142fn foo(xs: Vec<$0>)
1143"#,
1144 );
1145 check_edit(
1146 "Vec",
1147 r#"
1148struct Vec<T = i128> {}
1149fn foo(xs: Ve<|>)
1150"#,
1151 r#"
1152struct Vec<T = i128> {}
1153fn foo(xs: Vec)
1154"#,
1155 );
1156 check_edit(
1157 "Vec",
1158 r#"
1159struct Vec<T> {}
1160fn foo(xs: Ve<|><i128>)
1161"#,
1162 r#"
1163struct Vec<T> {}
1164fn foo(xs: Vec<i128>)
1165"#,
1166 );
1167 }
1168
1169 #[test]
1170 fn dont_insert_macro_call_parens_unncessary() {
1171 mark::check!(dont_insert_macro_call_parens_unncessary);
1172 check_edit(
1173 "frobnicate!",
1174 r#"
1175//- /main.rs crate:main deps:foo
1176use foo::<|>;
1177//- /foo/lib.rs crate:foo
1178#[macro_export]
1179macro_rules frobnicate { () => () }
1180"#,
1181 r#"
1182use foo::frobnicate;
1183"#,
1184 );
1185
1186 check_edit(
1187 "frobnicate!",
1188 r#"
1189macro_rules frobnicate { () => () }
1190fn main() { frob<|>!(); }
1191"#,
1192 r#"
1193macro_rules frobnicate { () => () }
1194fn main() { frobnicate!(); }
1195"#,
1196 );
1197 }
1198
1199 #[test]
1200 fn active_param_score() {
1201 mark::check!(active_param_type_match);
1202 check_scores(
1203 r#"
1204struct S { foo: i64, bar: u32, baz: u32 }
1205fn test(bar: u32) { }
1206fn foo(s: S) { test(s.<|>) }
1207"#,
1208 expect![[r#"
1209 fd bar [type+name]
1210 fd baz [type]
1211 fd foo []
1212 "#]],
1213 );
1214 }
1215
1216 #[test]
1217 fn record_field_scores() {
1218 mark::check!(record_field_type_match);
1219 check_scores(
1220 r#"
1221struct A { foo: i64, bar: u32, baz: u32 }
1222struct B { x: (), y: f32, bar: u32 }
1223fn foo(a: A) { B { bar: a.<|> }; }
1224"#,
1225 expect![[r#"
1226 fd bar [type+name]
1227 fd baz [type]
1228 fd foo []
1229 "#]],
1230 )
1231 }
1232
1233 #[test]
1234 fn record_field_and_call_scores() {
1235 check_scores(
1236 r#"
1237struct A { foo: i64, bar: u32, baz: u32 }
1238struct B { x: (), y: f32, bar: u32 }
1239fn f(foo: i64) { }
1240fn foo(a: A) { B { bar: f(a.<|>) }; }
1241"#,
1242 expect![[r#"
1243 fd foo [type+name]
1244 fd bar []
1245 fd baz []
1246 "#]],
1247 );
1248 check_scores(
1249 r#"
1250struct A { foo: i64, bar: u32, baz: u32 }
1251struct B { x: (), y: f32, bar: u32 }
1252fn f(foo: i64) { }
1253fn foo(a: A) { f(B { bar: a.<|> }); }
1254"#,
1255 expect![[r#"
1256 fd bar [type+name]
1257 fd baz [type]
1258 fd foo []
1259 "#]],
1260 );
1261 }
1262
1263 #[test]
1264 fn prioritize_exact_ref_match() {
1265 check_scores(
1266 r#"
1267struct WorldSnapshot { _f: () };
1268fn go(world: &WorldSnapshot) { go(w<|>) }
1269"#,
1270 expect![[r#"
1271 bn world [type+name]
1272 st WorldSnapshot []
1273 fn go(…) []
1274 "#]],
1275 );
1276 }
1277
1278 #[test]
1279 fn too_many_arguments() {
1280 mark::check!(too_many_arguments);
1281 check_scores(
1282 r#"
1283struct Foo;
1284fn f(foo: &Foo) { f(foo, w<|>) }
1285"#,
1286 expect![[r#"
1287 st Foo []
1288 fn f(…) []
1289 bn foo []
1290 "#]],
1291 );
1292 }
1293
1294 #[test]
1295 fn guesses_macro_braces() {
1296 check_edit(
1297 "vec!",
1298 r#"
1299/// Creates a [`Vec`] containing the arguments.
1300///
1301/// ```
1302/// let v = vec![1, 2, 3];
1303/// assert_eq!(v[0], 1);
1304/// assert_eq!(v[1], 2);
1305/// assert_eq!(v[2], 3);
1306/// ```
1307macro_rules! vec { () => {} }
1308
1309fn fn main() { v<|> }
1310"#,
1311 r#"
1312/// Creates a [`Vec`] containing the arguments.
1313///
1314/// ```
1315/// let v = vec![1, 2, 3];
1316/// assert_eq!(v[0], 1);
1317/// assert_eq!(v[1], 2);
1318/// assert_eq!(v[2], 3);
1319/// ```
1320macro_rules! vec { () => {} }
1321
1322fn fn main() { vec![$0] }
1323"#,
1324 );
1325
1326 check_edit(
1327 "foo!",
1328 r#"
1329/// Foo
1330///
1331/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
1332/// call as `let _=foo! { hello world };`
1333macro_rules! foo { () => {} }
1334fn main() { <|> }
1335"#,
1336 r#"
1337/// Foo
1338///
1339/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
1340/// call as `let _=foo! { hello world };`
1341macro_rules! foo { () => {} }
1342fn main() { foo! {$0} }
1343"#,
1344 )
1345 }
1346}
diff --git a/crates/ide/src/completion/test_utils.rs b/crates/ide/src/completion/test_utils.rs
deleted file mode 100644
index dabbef888..000000000
--- a/crates/ide/src/completion/test_utils.rs
+++ /dev/null
@@ -1,125 +0,0 @@
1//! Runs completion for testing purposes.
2
3use hir::Semantics;
4use itertools::Itertools;
5use stdx::{format_to, trim_indent};
6use syntax::{AstNode, NodeOrToken, SyntaxElement};
7use test_utils::assert_eq_text;
8
9use crate::{
10 completion::{completion_item::CompletionKind, CompletionConfig},
11 fixture, CompletionItem,
12};
13
14pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
15 do_completion_with_config(CompletionConfig::default(), code, kind)
16}
17
18pub(crate) fn do_completion_with_config(
19 config: CompletionConfig,
20 code: &str,
21 kind: CompletionKind,
22) -> Vec<CompletionItem> {
23 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(config, code)
24 .into_iter()
25 .filter(|c| c.completion_kind == kind)
26 .collect();
27 kind_completions.sort_by(|l, r| l.label().cmp(r.label()));
28 kind_completions
29}
30
31pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String {
32 completion_list_with_config(CompletionConfig::default(), code, kind)
33}
34
35pub(crate) fn completion_list_with_config(
36 config: CompletionConfig,
37 code: &str,
38 kind: CompletionKind,
39) -> String {
40 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(config, code)
41 .into_iter()
42 .filter(|c| c.completion_kind == kind)
43 .collect();
44 kind_completions.sort_by_key(|c| c.label().to_owned());
45 let label_width = kind_completions
46 .iter()
47 .map(|it| monospace_width(it.label()))
48 .max()
49 .unwrap_or_default()
50 .min(16);
51 kind_completions
52 .into_iter()
53 .map(|it| {
54 let tag = it.kind().unwrap().tag();
55 let var_name = format!("{} {}", tag, it.label());
56 let mut buf = var_name;
57 if let Some(detail) = it.detail() {
58 let width = label_width.saturating_sub(monospace_width(it.label()));
59 format_to!(buf, "{:width$} {}", "", detail, width = width);
60 }
61 format_to!(buf, "\n");
62 buf
63 })
64 .collect()
65}
66
67fn monospace_width(s: &str) -> usize {
68 s.chars().count()
69}
70
71pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
72 check_edit_with_config(CompletionConfig::default(), what, ra_fixture_before, ra_fixture_after)
73}
74
75pub(crate) fn check_edit_with_config(
76 config: CompletionConfig,
77 what: &str,
78 ra_fixture_before: &str,
79 ra_fixture_after: &str,
80) {
81 let ra_fixture_after = trim_indent(ra_fixture_after);
82 let (analysis, position) = fixture::position(ra_fixture_before);
83 let completions: Vec<CompletionItem> =
84 analysis.completions(&config, position).unwrap().unwrap().into();
85 let (completion,) = completions
86 .iter()
87 .filter(|it| it.lookup() == what)
88 .collect_tuple()
89 .unwrap_or_else(|| panic!("can't find {:?} completion in {:#?}", what, completions));
90 let mut actual = analysis.file_text(position.file_id).unwrap().to_string();
91 completion.text_edit().apply(&mut actual);
92 assert_eq_text!(&ra_fixture_after, &actual)
93}
94
95pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) {
96 let (analysis, pos) = fixture::position(code);
97 analysis
98 .with_db(|db| {
99 let sema = Semantics::new(db);
100 let original_file = sema.parse(pos.file_id);
101 let token = original_file.syntax().token_at_offset(pos.offset).left_biased().unwrap();
102 assert!(check(NodeOrToken::Token(token)));
103 })
104 .unwrap();
105}
106
107pub(crate) fn check_pattern_is_not_applicable(code: &str, check: fn(SyntaxElement) -> bool) {
108 let (analysis, pos) = fixture::position(code);
109 analysis
110 .with_db(|db| {
111 let sema = Semantics::new(db);
112 let original_file = sema.parse(pos.file_id);
113 let token = original_file.syntax().token_at_offset(pos.offset).left_biased().unwrap();
114 assert!(!check(NodeOrToken::Token(token)));
115 })
116 .unwrap();
117}
118
119pub(crate) fn get_all_completion_items(
120 config: CompletionConfig,
121 code: &str,
122) -> Vec<CompletionItem> {
123 let (analysis, position) = fixture::position(code);
124 analysis.completions(&config, position).unwrap().unwrap().into()
125}
diff --git a/crates/ide/src/display.rs b/crates/ide/src/display.rs
index 2484dbbf1..0650915c5 100644
--- a/crates/ide/src/display.rs
+++ b/crates/ide/src/display.rs
@@ -4,87 +4,8 @@
4mod navigation_target; 4mod navigation_target;
5mod short_label; 5mod short_label;
6 6
7use syntax::{
8 ast::{self, AstNode, AttrsOwner, GenericParamsOwner, NameOwner},
9 SyntaxKind::{ATTR, COMMENT},
10};
11
12use ast::VisibilityOwner;
13use stdx::format_to;
14
15pub use navigation_target::NavigationTarget; 7pub use navigation_target::NavigationTarget;
16pub(crate) use navigation_target::{ToNav, TryToNav}; 8pub(crate) use navigation_target::{ToNav, TryToNav};
17pub(crate) use short_label::ShortLabel; 9pub(crate) use short_label::ShortLabel;
18 10
19pub(crate) fn function_declaration(node: &ast::Fn) -> String { 11pub(crate) use syntax::display::{function_declaration, macro_label};
20 let mut buf = String::new();
21 if let Some(vis) = node.visibility() {
22 format_to!(buf, "{} ", vis);
23 }
24 if node.async_token().is_some() {
25 format_to!(buf, "async ");
26 }
27 if node.const_token().is_some() {
28 format_to!(buf, "const ");
29 }
30 if node.unsafe_token().is_some() {
31 format_to!(buf, "unsafe ");
32 }
33 if let Some(abi) = node.abi() {
34 // Keyword `extern` is included in the string.
35 format_to!(buf, "{} ", abi);
36 }
37 if let Some(name) = node.name() {
38 format_to!(buf, "fn {}", name)
39 }
40 if let Some(type_params) = node.generic_param_list() {
41 format_to!(buf, "{}", type_params);
42 }
43 if let Some(param_list) = node.param_list() {
44 let params: Vec<String> = param_list
45 .self_param()
46 .into_iter()
47 .map(|self_param| self_param.to_string())
48 .chain(param_list.params().map(|param| param.to_string()))
49 .collect();
50 // Useful to inline parameters
51 format_to!(buf, "({})", params.join(", "));
52 }
53 if let Some(ret_type) = node.ret_type() {
54 if ret_type.ty().is_some() {
55 format_to!(buf, " {}", ret_type);
56 }
57 }
58 if let Some(where_clause) = node.where_clause() {
59 format_to!(buf, "\n{}", where_clause);
60 }
61 buf
62}
63
64pub(crate) fn const_label(node: &ast::Const) -> String {
65 let label: String = node
66 .syntax()
67 .children_with_tokens()
68 .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
69 .map(|node| node.to_string())
70 .collect();
71
72 label.trim().to_owned()
73}
74
75pub(crate) fn type_label(node: &ast::TypeAlias) -> String {
76 let label: String = node
77 .syntax()
78 .children_with_tokens()
79 .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
80 .map(|node| node.to_string())
81 .collect();
82
83 label.trim().to_owned()
84}
85
86pub(crate) fn macro_label(node: &ast::MacroCall) -> String {
87 let name = node.name().map(|name| name.syntax().text().to_string()).unwrap_or_default();
88 let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" };
89 format!("{}macro_rules! {}", vis, name)
90}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index aaf9b3b4b..cecfae4c7 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -23,8 +23,6 @@ mod prime_caches;
23mod display; 23mod display;
24 24
25mod call_hierarchy; 25mod call_hierarchy;
26mod call_info;
27mod completion;
28mod diagnostics; 26mod diagnostics;
29mod expand_macro; 27mod expand_macro;
30mod extend_selection; 28mod extend_selection;
@@ -65,10 +63,6 @@ use crate::display::ToNav;
65 63
66pub use crate::{ 64pub use crate::{
67 call_hierarchy::CallItem, 65 call_hierarchy::CallItem,
68 call_info::CallInfo,
69 completion::{
70 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat,
71 },
72 diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity}, 66 diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity},
73 display::NavigationTarget, 67 display::NavigationTarget,
74 expand_macro::ExpandedMacro, 68 expand_macro::ExpandedMacro,
@@ -86,6 +80,10 @@ pub use crate::{
86 Highlight, HighlightModifier, HighlightModifiers, HighlightTag, HighlightedRange, 80 Highlight, HighlightModifier, HighlightModifiers, HighlightTag, HighlightedRange,
87 }, 81 },
88}; 82};
83pub use call_info::CallInfo;
84pub use completion::{
85 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat,
86};
89 87
90pub use assists::{ 88pub use assists::{
91 utils::MergeBehaviour, Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist, 89 utils::MergeBehaviour, Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist,
diff --git a/crates/ide/src/syntax_highlighting/injection.rs b/crates/ide/src/syntax_highlighting/injection.rs
index 43f4e6fea..acd91b26c 100644
--- a/crates/ide/src/syntax_highlighting/injection.rs
+++ b/crates/ide/src/syntax_highlighting/injection.rs
@@ -3,14 +3,12 @@
3use std::{collections::BTreeMap, convert::TryFrom}; 3use std::{collections::BTreeMap, convert::TryFrom};
4 4
5use ast::{HasQuotes, HasStringValue}; 5use ast::{HasQuotes, HasStringValue};
6use call_info::ActiveParameter;
6use hir::Semantics; 7use hir::Semantics;
7use itertools::Itertools; 8use itertools::Itertools;
8use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize}; 9use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize};
9 10
10use crate::{ 11use crate::{Analysis, Highlight, HighlightModifier, HighlightTag, HighlightedRange, RootDatabase};
11 call_info::ActiveParameter, Analysis, Highlight, HighlightModifier, HighlightTag,
12 HighlightedRange, RootDatabase,
13};
14 12
15use super::HighlightedRangeStack; 13use super::HighlightedRangeStack;
16 14