aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
authorZac Pullar-Strecker <[email protected]>2020-07-31 03:12:44 +0100
committerZac Pullar-Strecker <[email protected]>2020-07-31 03:12:44 +0100
commitf05d7b41a719d848844b054a16477b29d0f063c6 (patch)
tree0a8a0946e8aef2ce64d4c13d0035ba41cce2daf3 /crates/ra_ide
parent73ff610e41959e3e7c78a2b4b25b086883132956 (diff)
parent6b7cb8b5ab539fc4333ce34bc29bf77c976f232a (diff)
Merge remote-tracking branch 'upstream/master' into 503-hover-doc-links
Hasn't fixed tests yet.
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/Cargo.toml4
-rw-r--r--crates/ra_ide/src/call_hierarchy.rs97
-rw-r--r--crates/ra_ide/src/call_info.rs789
-rw-r--r--crates/ra_ide/src/completion.rs18
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs1263
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs994
-rw-r--r--crates/ra_ide/src/completion/complete_fn_param.rs161
-rw-r--r--crates/ra_ide/src/completion/complete_keyword.rs562
-rw-r--r--crates/ra_ide/src/completion/complete_macro_in_item_position.rs137
-rw-r--r--crates/ra_ide/src/completion/complete_pattern.rs126
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs635
-rw-r--r--crates/ra_ide/src/completion/complete_qualified_path.rs1506
-rw-r--r--crates/ra_ide/src/completion/complete_record.rs546
-rw-r--r--crates/ra_ide/src/completion/complete_snippet.rs106
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs498
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs1615
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs59
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs26
-rw-r--r--crates/ra_ide/src/completion/patterns.rs16
-rw-r--r--crates/ra_ide/src/completion/presentation.rs1675
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs72
-rw-r--r--crates/ra_ide/src/diagnostics.rs913
-rw-r--r--crates/ra_ide/src/display.rs96
-rw-r--r--crates/ra_ide/src/display/function_signature.rs334
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs207
-rw-r--r--crates/ra_ide/src/display/short_label.rs36
-rw-r--r--crates/ra_ide/src/display/structure.rs438
-rw-r--r--crates/ra_ide/src/expand_macro.rs241
-rw-r--r--crates/ra_ide/src/extend_selection.rs8
-rw-r--r--crates/ra_ide/src/file_structure.rs431
-rw-r--r--crates/ra_ide/src/folding_ranges.rs194
-rw-r--r--crates/ra_ide/src/goto_definition.rs1168
-rw-r--r--crates/ra_ide/src/goto_implementation.rs211
-rw-r--r--crates/ra_ide/src/goto_type_definition.rs132
-rw-r--r--crates/ra_ide/src/hover.rs3454
-rw-r--r--crates/ra_ide/src/inlay_hints.rs1062
-rw-r--r--crates/ra_ide/src/lib.rs142
-rw-r--r--crates/ra_ide/src/markup.rs38
-rw-r--r--crates/ra_ide/src/mock_analysis.rs40
-rw-r--r--crates/ra_ide/src/references.rs47
-rw-r--r--crates/ra_ide/src/references/rename.rs1101
-rw-r--r--crates/ra_ide/src/runnables.rs1137
-rw-r--r--crates/ra_ide/src/snapshots/highlight_doctest.html101
-rw-r--r--crates/ra_ide/src/snapshots/highlight_strings.html95
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html117
-rw-r--r--crates/ra_ide/src/ssr.rs53
-rw-r--r--crates/ra_ide/src/status.rs15
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs114
-rw-r--r--crates/ra_ide/src/syntax_highlighting/html.rs15
-rw-r--r--crates/ra_ide/src/syntax_highlighting/injection.rs2
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs4
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs32
-rw-r--r--crates/ra_ide/src/syntax_tree.rs14
-rw-r--r--crates/ra_ide/src/typing.rs1
-rw-r--r--crates/ra_ide/src/typing/on_enter.rs67
-rw-r--r--crates/ra_ide/test_data/highlight_doctest.html102
-rw-r--r--crates/ra_ide/test_data/highlight_injection.html (renamed from crates/ra_ide/src/snapshots/highlight_injection.html)25
-rw-r--r--crates/ra_ide/test_data/highlight_strings.html96
-rw-r--r--crates/ra_ide/test_data/highlight_unsafe.html (renamed from crates/ra_ide/src/snapshots/highlight_unsafe.html)33
-rw-r--r--crates/ra_ide/test_data/highlighting.html128
-rw-r--r--crates/ra_ide/test_data/rainbow_highlighting.html (renamed from crates/ra_ide/src/snapshots/rainbow_highlighting.html)25
61 files changed, 9765 insertions, 13609 deletions
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
index d0eb018d5..4e2ba6d61 100644
--- a/crates/ra_ide/Cargo.toml
+++ b/crates/ra_ide/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2018"
3name = "ra_ide" 3name = "ra_ide"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
@@ -22,6 +23,7 @@ maplit = "*"
22lazy_static = "*" 23lazy_static = "*"
23pulldown-cmark-to-cmark = "4.0.2" 24pulldown-cmark-to-cmark = "4.0.2"
24pulldown-cmark = "0.7.0" 25pulldown-cmark = "0.7.0"
26oorandom = "11.1.2"
25 27
26stdx = { path = "../stdx" } 28stdx = { path = "../stdx" }
27 29
@@ -44,4 +46,4 @@ ra_parser = { path = "../ra_parser" }
44hir = { path = "../ra_hir", package = "ra_hir" } 46hir = { path = "../ra_hir", package = "ra_hir" }
45 47
46[dev-dependencies] 48[dev-dependencies]
47insta = "0.16.0" 49expect = { path = "../expect" }
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs
index 1e3a31602..1fcaf4a32 100644
--- a/crates/ra_ide/src/call_hierarchy.rs
+++ b/crates/ra_ide/src/call_hierarchy.rs
@@ -39,10 +39,11 @@ pub(crate) fn call_hierarchy(
39 39
40pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { 40pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
41 let sema = Semantics::new(db); 41 let sema = Semantics::new(db);
42
42 // 1. Find all refs 43 // 1. Find all refs
43 // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply. 44 // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply.
44 // 3. Add ranges relative to the start of the fndef. 45 // 3. Add ranges relative to the start of the fndef.
45 let refs = references::find_all_refs(db, position, None)?; 46 let refs = references::find_all_refs(&sema, position, None)?;
46 47
47 let mut calls = CallLocations::default(); 48 let mut calls = CallLocations::default();
48 49
@@ -58,7 +59,7 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
58 if let Some(nav) = syntax.ancestors().find_map(|node| { 59 if let Some(nav) = syntax.ancestors().find_map(|node| {
59 match_ast! { 60 match_ast! {
60 match node { 61 match node {
61 ast::FnDef(it) => { 62 ast::Fn(it) => {
62 let def = sema.to_def(&it)?; 63 let def = sema.to_def(&it)?;
63 Some(def.to_nav(sema.db)) 64 Some(def.to_nav(sema.db))
64 }, 65 },
@@ -94,9 +95,9 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
94 if let Some(func_target) = match &call_node { 95 if let Some(func_target) = match &call_node {
95 FnCallNode::CallExpr(expr) => { 96 FnCallNode::CallExpr(expr) => {
96 //FIXME: Type::as_callable is broken 97 //FIXME: Type::as_callable is broken
97 let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?; 98 let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(db)?;
98 match callable_def { 99 match callable.kind() {
99 hir::CallableDef::FunctionId(it) => { 100 hir::CallableKind::Function(it) => {
100 let fn_def: hir::Function = it.into(); 101 let fn_def: hir::Function = it.into();
101 let nav = fn_def.to_nav(db); 102 let nav = fn_def.to_nav(db);
102 Some(nav) 103 Some(nav)
@@ -108,10 +109,6 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
108 let function = sema.resolve_method_call(&expr)?; 109 let function = sema.resolve_method_call(&expr)?;
109 Some(function.to_nav(db)) 110 Some(function.to_nav(db))
110 } 111 }
111 FnCallNode::MacroCallExpr(macro_call) => {
112 let macro_def = sema.resolve_macro_call(&macro_call)?;
113 Some(macro_def.to_nav(db))
114 }
115 } { 112 } {
116 Some((func_target, name_ref.syntax().text_range())) 113 Some((func_target, name_ref.syntax().text_range()))
117 } else { 114 } else {
@@ -157,7 +154,8 @@ mod tests {
157 let nav = navs.pop().unwrap(); 154 let nav = navs.pop().unwrap();
158 nav.assert_match(expected); 155 nav.assert_match(expected);
159 156
160 let item_pos = FilePosition { file_id: nav.file_id(), offset: nav.range().start() }; 157 let item_pos =
158 FilePosition { file_id: nav.file_id, offset: nav.focus_or_full_range().start() };
161 let incoming_calls = analysis.incoming_calls(item_pos).unwrap().unwrap(); 159 let incoming_calls = analysis.incoming_calls(item_pos).unwrap().unwrap();
162 assert_eq!(incoming_calls.len(), expected_incoming.len()); 160 assert_eq!(incoming_calls.len(), expected_incoming.len());
163 161
@@ -183,8 +181,8 @@ fn caller() {
183 call<|>ee(); 181 call<|>ee();
184} 182}
185"#, 183"#,
186 "callee FN_DEF FileId(1) 0..14 3..9", 184 "callee FN FileId(1) 0..14 3..9",
187 &["caller FN_DEF FileId(1) 15..44 18..24 : [33..39]"], 185 &["caller FN FileId(1) 15..44 18..24 : [33..39]"],
188 &[], 186 &[],
189 ); 187 );
190 } 188 }
@@ -199,8 +197,8 @@ fn caller() {
199 callee(); 197 callee();
200} 198}
201"#, 199"#,
202 "callee FN_DEF FileId(1) 0..14 3..9", 200 "callee FN FileId(1) 0..14 3..9",
203 &["caller FN_DEF FileId(1) 15..44 18..24 : [33..39]"], 201 &["caller FN FileId(1) 15..44 18..24 : [33..39]"],
204 &[], 202 &[],
205 ); 203 );
206 } 204 }
@@ -216,8 +214,8 @@ fn caller() {
216 callee(); 214 callee();
217} 215}
218"#, 216"#,
219 "callee FN_DEF FileId(1) 0..14 3..9", 217 "callee FN FileId(1) 0..14 3..9",
220 &["caller FN_DEF FileId(1) 15..58 18..24 : [33..39, 47..53]"], 218 &["caller FN FileId(1) 15..58 18..24 : [33..39, 47..53]"],
221 &[], 219 &[],
222 ); 220 );
223 } 221 }
@@ -236,10 +234,10 @@ fn caller2() {
236 callee(); 234 callee();
237} 235}
238"#, 236"#,
239 "callee FN_DEF FileId(1) 0..14 3..9", 237 "callee FN FileId(1) 0..14 3..9",
240 &[ 238 &[
241 "caller1 FN_DEF FileId(1) 15..45 18..25 : [34..40]", 239 "caller1 FN FileId(1) 15..45 18..25 : [34..40]",
242 "caller2 FN_DEF FileId(1) 47..77 50..57 : [66..72]", 240 "caller2 FN FileId(1) 47..77 50..57 : [66..72]",
243 ], 241 ],
244 &[], 242 &[],
245 ); 243 );
@@ -265,10 +263,10 @@ mod tests {
265 } 263 }
266} 264}
267"#, 265"#,
268 "callee FN_DEF FileId(1) 0..14 3..9", 266 "callee FN FileId(1) 0..14 3..9",
269 &[ 267 &[
270 "caller1 FN_DEF FileId(1) 15..45 18..25 : [34..40]", 268 "caller1 FN FileId(1) 15..45 18..25 : [34..40]",
271 "test_caller FN_DEF FileId(1) 95..149 110..121 : [134..140]", 269 "test_caller FN FileId(1) 95..149 110..121 : [134..140]",
272 ], 270 ],
273 &[], 271 &[],
274 ); 272 );
@@ -289,8 +287,8 @@ fn caller() {
289//- /foo/mod.rs 287//- /foo/mod.rs
290pub fn callee() {} 288pub fn callee() {}
291"#, 289"#,
292 "callee FN_DEF FileId(2) 0..18 7..13", 290 "callee FN FileId(2) 0..18 7..13",
293 &["caller FN_DEF FileId(1) 27..56 30..36 : [45..51]"], 291 &["caller FN FileId(1) 27..56 30..36 : [45..51]"],
294 &[], 292 &[],
295 ); 293 );
296 } 294 }
@@ -306,9 +304,9 @@ fn call<|>er() {
306 callee(); 304 callee();
307} 305}
308"#, 306"#,
309 "caller FN_DEF FileId(1) 15..58 18..24", 307 "caller FN FileId(1) 15..58 18..24",
310 &[], 308 &[],
311 &["callee FN_DEF FileId(1) 0..14 3..9 : [33..39, 47..53]"], 309 &["callee FN FileId(1) 0..14 3..9 : [33..39, 47..53]"],
312 ); 310 );
313 } 311 }
314 312
@@ -327,9 +325,9 @@ fn call<|>er() {
327//- /foo/mod.rs 325//- /foo/mod.rs
328pub fn callee() {} 326pub fn callee() {}
329"#, 327"#,
330 "caller FN_DEF FileId(1) 27..56 30..36", 328 "caller FN FileId(1) 27..56 30..36",
331 &[], 329 &[],
332 &["callee FN_DEF FileId(2) 0..18 7..13 : [45..51]"], 330 &["callee FN FileId(2) 0..18 7..13 : [45..51]"],
333 ); 331 );
334 } 332 }
335 333
@@ -350,9 +348,46 @@ fn caller3() {
350 348
351} 349}
352"#, 350"#,
353 "caller2 FN_DEF FileId(1) 33..64 36..43", 351 "caller2 FN FileId(1) 33..64 36..43",
354 &["caller1 FN_DEF FileId(1) 0..31 3..10 : [19..26]"], 352 &["caller1 FN FileId(1) 0..31 3..10 : [19..26]"],
355 &["caller3 FN_DEF FileId(1) 66..83 69..76 : [52..59]"], 353 &["caller3 FN FileId(1) 66..83 69..76 : [52..59]"],
354 );
355 }
356
357 #[test]
358 fn test_call_hierarchy_issue_5103() {
359 check_hierarchy(
360 r#"
361fn a() {
362 b()
363}
364
365fn b() {}
366
367fn main() {
368 a<|>()
369}
370"#,
371 "a FN FileId(1) 0..18 3..4",
372 &["main FN FileId(1) 31..52 34..38 : [47..48]"],
373 &["b FN FileId(1) 20..29 23..24 : [13..14]"],
374 );
375
376 check_hierarchy(
377 r#"
378fn a() {
379 b<|>()
380}
381
382fn b() {}
383
384fn main() {
385 a()
386}
387"#,
388 "b FN FileId(1) 20..29 23..24",
389 &["a FN FileId(1) 0..18 3..4 : [13..14]"],
390 &[],
356 ); 391 );
357 } 392 }
358} 393}
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
index a6bdf1c9d..ff602202f 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -1,13 +1,43 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2use hir::Semantics; 2use either::Either;
3use hir::{Docs, HirDisplay, Semantics, Type};
3use ra_ide_db::RootDatabase; 4use ra_ide_db::RootDatabase;
4use ra_syntax::{ 5use ra_syntax::{
5 ast::{self, ArgListOwner}, 6 ast::{self, ArgListOwner},
6 match_ast, AstNode, SyntaxNode, SyntaxToken, 7 match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize,
7}; 8};
9use stdx::format_to;
8use test_utils::mark; 10use test_utils::mark;
9 11
10use crate::{CallInfo, FilePosition, FunctionSignature}; 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}
11 41
12/// Computes parameter information for the given call expression. 42/// Computes parameter information for the given call expression.
13pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> { 43pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
@@ -16,106 +46,135 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
16 let file = file.syntax(); 46 let file = file.syntax();
17 let token = file.token_at_offset(position.offset).next()?; 47 let token = file.token_at_offset(position.offset).next()?;
18 let token = sema.descend_into_macros(token); 48 let token = sema.descend_into_macros(token);
19 call_info_for_token(&sema, token)
20}
21 49
22#[derive(Debug)] 50 let (callable, active_parameter) = call_info_impl(&sema, token)?;
23pub(crate) struct ActiveParameter {
24 /// FIXME: should be `Type` and `Name
25 pub(crate) ty: String,
26 pub(crate) name: String,
27}
28 51
29impl ActiveParameter { 52 let mut res =
30 pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option<Self> { 53 CallInfo { doc: None, signature: String::new(), parameters: vec![], active_parameter };
31 call_info(db, position)?.into_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 => (),
32 } 74 }
33 75
34 pub(crate) fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> { 76 res.signature.push('(');
35 call_info_for_token(sema, token)?.into_active_parameter() 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 }
36 } 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)
37} 106}
38 107
39fn call_info_for_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<CallInfo> { 108fn call_info_impl(
109 sema: &Semantics<RootDatabase>,
110 token: SyntaxToken,
111) -> Option<(hir::Callable, Option<usize>)> {
40 // Find the calling expression and it's NameRef 112 // Find the calling expression and it's NameRef
41 let calling_node = FnCallNode::with_node(&token.parent())?; 113 let calling_node = FnCallNode::with_node(&token.parent())?;
42 114
43 let (mut call_info, has_self) = match &calling_node { 115 let callable = match &calling_node {
44 FnCallNode::CallExpr(call) => { 116 FnCallNode::CallExpr(call) => sema.type_of_expr(&call.expr()?)?.as_callable(sema.db)?,
45 //FIXME: Type::as_callable is broken 117 FnCallNode::MethodCallExpr(call) => sema.resolve_method_call_as_callable(call)?,
46 let callable_def = sema.type_of_expr(&call.expr()?)?.as_callable()?; 118 };
47 match callable_def { 119 let active_param = if let Some(arg_list) = calling_node.arg_list() {
48 hir::CallableDef::FunctionId(it) => { 120 // Number of arguments specified at the call site
49 let fn_def = it.into(); 121 let num_args_at_callsite = arg_list.args().count();
50 (CallInfo::with_fn(sema.db, fn_def), fn_def.has_self_param(sema.db)) 122
51 } 123 let arg_list_range = arg_list.syntax().text_range();
52 hir::CallableDef::StructId(it) => { 124 if !arg_list_range.contains_inclusive(token.text_range().start()) {
53 (CallInfo::with_struct(sema.db, it.into())?, false) 125 mark::hit!(call_info_bad_offset);
54 } 126 return None;
55 hir::CallableDef::EnumVariantId(it) => {
56 (CallInfo::with_enum_variant(sema.db, it.into())?, false)
57 }
58 }
59 }
60 FnCallNode::MethodCallExpr(method_call) => {
61 let function = sema.resolve_method_call(&method_call)?;
62 (CallInfo::with_fn(sema.db, function), function.has_self_param(sema.db))
63 }
64 FnCallNode::MacroCallExpr(macro_call) => {
65 let macro_def = sema.resolve_macro_call(&macro_call)?;
66 (CallInfo::with_macro(sema.db, macro_def)?, false)
67 } 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
68 }; 139 };
140 Some((callable, active_param))
141}
69 142
70 // If we have a calling expression let's find which argument we are on 143#[derive(Debug)]
71 let num_params = call_info.parameters().len(); 144pub(crate) struct ActiveParameter {
145 pub(crate) ty: Type,
146 pub(crate) name: String,
147}
72 148
73 match num_params { 149impl ActiveParameter {
74 0 => (), 150 pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option<Self> {
75 1 => { 151 let sema = Semantics::new(db);
76 if !has_self { 152 let file = sema.parse(position.file_id);
77 call_info.active_parameter = Some(0); 153 let file = file.syntax();
78 } 154 let token = file.token_at_offset(position.offset).next()?;
79 } 155 let token = sema.descend_into_macros(token);
80 _ => { 156 Self::at_token(&sema, token)
81 if let Some(arg_list) = calling_node.arg_list() { 157 }
82 // Number of arguments specified at the call site
83 let num_args_at_callsite = arg_list.args().count();
84
85 let arg_list_range = arg_list.syntax().text_range();
86 if !arg_list_range.contains_inclusive(token.text_range().start()) {
87 mark::hit!(call_info_bad_offset);
88 return None;
89 }
90 158
91 let mut param = std::cmp::min( 159 pub(crate) fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> {
92 num_args_at_callsite, 160 let (signature, active_parameter) = call_info_impl(&sema, token)?;
93 arg_list
94 .args()
95 .take_while(|arg| {
96 arg.syntax().text_range().end() < token.text_range().start()
97 })
98 .count(),
99 );
100
101 // If we are in a method account for `self`
102 if has_self {
103 param += 1;
104 }
105 161
106 call_info.active_parameter = Some(param); 162 let idx = active_parameter?;
107 } 163 let mut params = signature.params(sema.db);
164 if !(idx < params.len()) {
165 mark::hit!(too_many_arguments);
166 return None;
108 } 167 }
168 let (pat, ty) = params.swap_remove(idx);
169 let name = pat?.to_string();
170 Some(ActiveParameter { ty, name })
109 } 171 }
110
111 Some(call_info)
112} 172}
113 173
114#[derive(Debug)] 174#[derive(Debug)]
115pub(crate) enum FnCallNode { 175pub(crate) enum FnCallNode {
116 CallExpr(ast::CallExpr), 176 CallExpr(ast::CallExpr),
117 MethodCallExpr(ast::MethodCallExpr), 177 MethodCallExpr(ast::MethodCallExpr),
118 MacroCallExpr(ast::MacroCall),
119} 178}
120 179
121impl FnCallNode { 180impl FnCallNode {
@@ -131,7 +190,6 @@ impl FnCallNode {
131 } 190 }
132 Some(FnCallNode::MethodCallExpr(it)) 191 Some(FnCallNode::MethodCallExpr(it))
133 }, 192 },
134 ast::MacroCall(it) => Some(FnCallNode::MacroCallExpr(it)),
135 _ => None, 193 _ => None,
136 } 194 }
137 } 195 }
@@ -143,7 +201,6 @@ impl FnCallNode {
143 match node { 201 match node {
144 ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)), 202 ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
145 ast::MethodCallExpr(it) => Some(FnCallNode::MethodCallExpr(it)), 203 ast::MethodCallExpr(it) => Some(FnCallNode::MethodCallExpr(it)),
146 ast::MacroCall(it) => Some(FnCallNode::MacroCallExpr(it)),
147 _ => None, 204 _ => None,
148 } 205 }
149 } 206 }
@@ -159,8 +216,6 @@ impl FnCallNode {
159 FnCallNode::MethodCallExpr(call_expr) => { 216 FnCallNode::MethodCallExpr(call_expr) => {
160 call_expr.syntax().children().filter_map(ast::NameRef::cast).next() 217 call_expr.syntax().children().filter_map(ast::NameRef::cast).next()
161 } 218 }
162
163 FnCallNode::MacroCallExpr(call_expr) => call_expr.path()?.segment()?.name_ref(),
164 } 219 }
165 } 220 }
166 221
@@ -168,214 +223,209 @@ impl FnCallNode {
168 match self { 223 match self {
169 FnCallNode::CallExpr(expr) => expr.arg_list(), 224 FnCallNode::CallExpr(expr) => expr.arg_list(),
170 FnCallNode::MethodCallExpr(expr) => expr.arg_list(), 225 FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
171 FnCallNode::MacroCallExpr(_) => None,
172 } 226 }
173 } 227 }
174} 228}
175 229
176impl CallInfo {
177 fn into_active_parameter(self) -> Option<ActiveParameter> {
178 let idx = self.active_parameter?;
179 let ty = self.signature.parameter_types.get(idx)?.clone();
180 let name = self.signature.parameter_names.get(idx)?.clone();
181 let res = ActiveParameter { ty, name };
182 Some(res)
183 }
184
185 fn with_fn(db: &RootDatabase, function: hir::Function) -> Self {
186 let signature = FunctionSignature::from_hir(db, function);
187
188 CallInfo { signature, active_parameter: None }
189 }
190
191 fn with_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
192 let signature = FunctionSignature::from_struct(db, st)?;
193
194 Some(CallInfo { signature, active_parameter: None })
195 }
196
197 fn with_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
198 let signature = FunctionSignature::from_enum_variant(db, variant)?;
199
200 Some(CallInfo { signature, active_parameter: None })
201 }
202
203 fn with_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
204 let signature = FunctionSignature::from_macro(db, macro_def)?;
205
206 Some(CallInfo { signature, active_parameter: None })
207 }
208
209 fn parameters(&self) -> &[String] {
210 &self.signature.parameters
211 }
212}
213
214#[cfg(test)] 230#[cfg(test)]
215mod tests { 231mod tests {
232 use expect::{expect, Expect};
216 use test_utils::mark; 233 use test_utils::mark;
217 234
218 use crate::mock_analysis::analysis_and_position; 235 use crate::mock_analysis::analysis_and_position;
219 236
220 use super::*; 237 fn check(ra_fixture: &str, expect: Expect) {
221 238 let (analysis, position) = analysis_and_position(ra_fixture);
222 // These are only used when testing 239 let call_info = analysis.call_info(position).unwrap();
223 impl CallInfo { 240 let actual = match call_info {
224 fn doc(&self) -> Option<hir::Documentation> { 241 Some(call_info) => {
225 self.signature.doc.clone() 242 let docs = match &call_info.doc {
226 } 243 None => "".to_string(),
227 244 Some(docs) => format!("{}\n------\n", docs.as_str()),
228 fn label(&self) -> String { 245 };
229 self.signature.to_string() 246 let params = call_info
230 } 247 .parameter_labels()
231 } 248 .enumerate()
232 249 .map(|(i, param)| {
233 fn call_info_helper(text: &str) -> Option<CallInfo> { 250 if Some(i) == call_info.active_parameter {
234 let (analysis, position) = analysis_and_position(text); 251 format!("<{}>", param)
235 analysis.call_info(position).unwrap() 252 } else {
236 } 253 param.to_string()
237 254 }
238 fn call_info(text: &str) -> CallInfo { 255 })
239 let info = call_info_helper(text); 256 .collect::<Vec<_>>()
240 assert!(info.is_some()); 257 .join(", ");
241 info.unwrap() 258 format!("{}{}\n({})\n", docs, call_info.signature, params)
242 } 259 }
243 260 None => String::new(),
244 fn no_call_info(text: &str) { 261 };
245 let info = call_info_helper(text); 262 expect.assert_eq(&actual);
246 assert!(info.is_none());
247 } 263 }
248 264
249 #[test] 265 #[test]
250 fn test_fn_signature_two_args_firstx() { 266 fn test_fn_signature_two_args() {
251 let info = call_info( 267 check(
252 r#"fn foo(x: u32, y: u32) -> u32 {x + y} 268 r#"
253fn bar() { foo(<|>3, ); }"#, 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 "#]],
254 ); 276 );
255 277 check(
256 assert_eq!(info.parameters(), ["x: u32", "y: u32"]); 278 r#"
257 assert_eq!(info.active_parameter, Some(0)); 279fn foo(x: u32, y: u32) -> u32 {x + y}
258 } 280fn bar() { foo(3<|>, ); }
259 281"#,
260 #[test] 282 expect![[r#"
261 fn test_fn_signature_two_args_second() { 283 fn foo(x: u32, y: u32) -> u32
262 let info = call_info( 284 (<x: u32>, y: u32)
263 r#"fn foo(x: u32, y: u32) -> u32 {x + y} 285 "#]],
264fn bar() { foo(3, <|>); }"#, 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 "#]],
265 ); 306 );
266
267 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
268 assert_eq!(info.active_parameter, Some(1));
269 } 307 }
270 308
271 #[test] 309 #[test]
272 fn test_fn_signature_two_args_empty() { 310 fn test_fn_signature_two_args_empty() {
273 let info = call_info( 311 check(
274 r#"fn foo(x: u32, y: u32) -> u32 {x + y} 312 r#"
275fn bar() { foo(<|>); }"#, 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 "#]],
276 ); 320 );
277
278 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
279 assert_eq!(info.active_parameter, Some(0));
280 } 321 }
281 322
282 #[test] 323 #[test]
283 fn test_fn_signature_two_args_first_generics() { 324 fn test_fn_signature_two_args_first_generics() {
284 let info = call_info( 325 check(
285 r#"fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 where T: Copy + Display, U: Debug {x + y}
286fn bar() { foo(<|>3, ); }"#,
287 );
288
289 assert_eq!(info.parameters(), ["x: T", "y: U"]);
290 assert_eq!(
291 info.label(),
292 r#" 326 r#"
293fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 327fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
294where T: Copy + Display, 328 where T: Copy + Display, U: Debug
295 U: Debug 329{ x + y }
296 "# 330
297 .trim() 331fn bar() { foo(<|>3, ); }
332"#,
333 expect![[r#"
334 fn foo(x: i32, y: {unknown}) -> u32
335 (<x: i32>, y: {unknown})
336 "#]],
298 ); 337 );
299 assert_eq!(info.active_parameter, Some(0));
300 } 338 }
301 339
302 #[test] 340 #[test]
303 fn test_fn_signature_no_params() { 341 fn test_fn_signature_no_params() {
304 let info = call_info( 342 check(
305 r#"fn foo<T>() -> T where T: Copy + Display {}
306fn bar() { foo(<|>); }"#,
307 );
308
309 assert!(info.parameters().is_empty());
310 assert_eq!(
311 info.label(),
312 r#" 343 r#"
313fn foo<T>() -> T 344fn foo<T>() -> T where T: Copy + Display {}
314where T: Copy + Display 345fn bar() { foo(<|>); }
315 "# 346"#,
316 .trim() 347 expect![[r#"
348 fn foo() -> {unknown}
349 ()
350 "#]],
317 ); 351 );
318 assert!(info.active_parameter.is_none());
319 } 352 }
320 353
321 #[test] 354 #[test]
322 fn test_fn_signature_for_impl() { 355 fn test_fn_signature_for_impl() {
323 let info = call_info( 356 check(
324 r#"struct F; impl F { pub fn new() { F{}} } 357 r#"
325fn bar() {let _ : F = F::new(<|>);}"#, 358struct F;
359impl F { pub fn new() { } }
360fn bar() {
361 let _ : F = F::new(<|>);
362}
363"#,
364 expect![[r#"
365 fn new()
366 ()
367 "#]],
326 ); 368 );
327
328 assert!(info.parameters().is_empty());
329 assert_eq!(info.active_parameter, None);
330 } 369 }
331 370
332 #[test] 371 #[test]
333 fn test_fn_signature_for_method_self() { 372 fn test_fn_signature_for_method_self() {
334 let info = call_info( 373 check(
335 r#"struct F; 374 r#"
336impl F { 375struct S;
337 pub fn new() -> F{ 376impl S { pub fn do_it(&self) {} }
338 F{}
339 }
340
341 pub fn do_it(&self) {}
342}
343 377
344fn bar() { 378fn bar() {
345 let f : F = F::new(); 379 let s: S = S;
346 f.do_it(<|>); 380 s.do_it(<|>);
347}"#, 381}
382"#,
383 expect![[r#"
384 fn do_it(&self)
385 ()
386 "#]],
348 ); 387 );
349
350 assert_eq!(info.parameters(), ["&self"]);
351 assert_eq!(info.active_parameter, None);
352 } 388 }
353 389
354 #[test] 390 #[test]
355 fn test_fn_signature_for_method_with_arg() { 391 fn test_fn_signature_for_method_with_arg() {
356 let info = call_info( 392 check(
357 r#"struct F; 393 r#"
358impl F { 394struct S;
359 pub fn new() -> F{ 395impl S {
360 F{} 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 );
361 } 406 }
362 407
363 pub fn do_it(&self, x: i32) {} 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) {}
364} 415}
365 416
366fn bar() { 417fn main() { S::foo(<|>); }
367 let f : F = F::new(); 418"#,
368 f.do_it(<|>); 419 expect![[r#"
369}"#, 420 fn foo(self: &S, x: i32)
421 (<self: &S>, x: i32)
422 "#]],
370 ); 423 );
371
372 assert_eq!(info.parameters(), ["&self", "x: i32"]);
373 assert_eq!(info.active_parameter, Some(1));
374 } 424 }
375 425
376 #[test] 426 #[test]
377 fn test_fn_signature_with_docs_simple() { 427 fn test_fn_signature_with_docs_simple() {
378 let info = call_info( 428 check(
379 r#" 429 r#"
380/// test 430/// test
381// non-doc-comment 431// non-doc-comment
@@ -387,17 +437,18 @@ fn bar() {
387 let _ = foo(<|>); 437 let _ = foo(<|>);
388} 438}
389"#, 439"#,
440 expect![[r#"
441 test
442 ------
443 fn foo(j: u32) -> u32
444 (<j: u32>)
445 "#]],
390 ); 446 );
391
392 assert_eq!(info.parameters(), ["j: u32"]);
393 assert_eq!(info.active_parameter, Some(0));
394 assert_eq!(info.label(), "fn foo(j: u32) -> u32");
395 assert_eq!(info.doc().map(|it| it.into()), Some("test".to_string()));
396 } 447 }
397 448
398 #[test] 449 #[test]
399 fn test_fn_signature_with_docs() { 450 fn test_fn_signature_with_docs() {
400 let info = call_info( 451 check(
401 r#" 452 r#"
402/// Adds one to the number given. 453/// Adds one to the number given.
403/// 454///
@@ -415,31 +466,26 @@ pub fn add_one(x: i32) -> i32 {
415pub fn do() { 466pub fn do() {
416 add_one(<|> 467 add_one(<|>
417}"#, 468}"#,
418 ); 469 expect![[r##"
419 470 Adds one to the number given.
420 assert_eq!(info.parameters(), ["x: i32"]);
421 assert_eq!(info.active_parameter, Some(0));
422 assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
423 assert_eq!(
424 info.doc().map(|it| it.into()),
425 Some(
426 r#"Adds one to the number given.
427 471
428# Examples 472 # Examples
429 473
430``` 474 ```
431let five = 5; 475 let five = 5;
432 476
433assert_eq!(6, my_crate::add_one(5)); 477 assert_eq!(6, my_crate::add_one(5));
434```"# 478 ```
435 .to_string() 479 ------
436 ) 480 fn add_one(x: i32) -> i32
481 (<x: i32>)
482 "##]],
437 ); 483 );
438 } 484 }
439 485
440 #[test] 486 #[test]
441 fn test_fn_signature_with_docs_impl() { 487 fn test_fn_signature_with_docs_impl() {
442 let info = call_info( 488 check(
443 r#" 489 r#"
444struct addr; 490struct addr;
445impl addr { 491impl addr {
@@ -460,32 +506,28 @@ impl addr {
460pub fn do_it() { 506pub fn do_it() {
461 addr {}; 507 addr {};
462 addr::add_one(<|>); 508 addr::add_one(<|>);
463}"#, 509}
464 ); 510"#,
465 511 expect![[r##"
466 assert_eq!(info.parameters(), ["x: i32"]); 512 Adds one to the number given.
467 assert_eq!(info.active_parameter, Some(0));
468 assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
469 assert_eq!(
470 info.doc().map(|it| it.into()),
471 Some(
472 r#"Adds one to the number given.
473 513
474# Examples 514 # Examples
475 515
476``` 516 ```
477let five = 5; 517 let five = 5;
478 518
479assert_eq!(6, my_crate::add_one(5)); 519 assert_eq!(6, my_crate::add_one(5));
480```"# 520 ```
481 .to_string() 521 ------
482 ) 522 fn add_one(x: i32) -> i32
523 (<x: i32>)
524 "##]],
483 ); 525 );
484 } 526 }
485 527
486 #[test] 528 #[test]
487 fn test_fn_signature_with_docs_from_actix() { 529 fn test_fn_signature_with_docs_from_actix() {
488 let info = call_info( 530 check(
489 r#" 531 r#"
490struct WriteHandler<E>; 532struct WriteHandler<E>;
491 533
@@ -509,101 +551,89 @@ impl<E> WriteHandler<E> {
509pub fn foo(mut r: WriteHandler<()>) { 551pub fn foo(mut r: WriteHandler<()>) {
510 r.finished(<|>); 552 r.finished(<|>);
511} 553}
512
513"#, 554"#,
514 ); 555 expect![[r#"
515 556 Method is called when writer finishes.
516 assert_eq!(info.label(), "fn finished(&mut self, ctx: &mut Self::Context)".to_string()); 557
517 assert_eq!(info.parameters(), ["&mut self", "ctx: &mut Self::Context"]); 558 By default this method stops actor's `Context`.
518 assert_eq!(info.active_parameter, Some(1)); 559 ------
519 assert_eq!( 560 fn finished(&mut self, ctx: &mut {unknown})
520 info.doc().map(|it| it.into()), 561 (<ctx: &mut {unknown}>)
521 Some( 562 "#]],
522 r#"Method is called when writer finishes.
523
524By default this method stops actor's `Context`."#
525 .to_string()
526 )
527 ); 563 );
528 } 564 }
529 565
530 #[test] 566 #[test]
531 fn call_info_bad_offset() { 567 fn call_info_bad_offset() {
532 mark::check!(call_info_bad_offset); 568 mark::check!(call_info_bad_offset);
533 let (analysis, position) = analysis_and_position( 569 check(
534 r#"fn foo(x: u32, y: u32) -> u32 {x + y} 570 r#"
535 fn bar() { foo <|> (3, ); }"#, 571fn foo(x: u32, y: u32) -> u32 {x + y}
572fn bar() { foo <|> (3, ); }
573"#,
574 expect![[""]],
536 ); 575 );
537 let call_info = analysis.call_info(position).unwrap();
538 assert!(call_info.is_none());
539 } 576 }
540 577
541 #[test] 578 #[test]
542 fn test_nested_method_in_lamba() { 579 fn test_nested_method_in_lambda() {
543 let info = call_info( 580 check(
544 r#"struct Foo; 581 r#"
545 582struct Foo;
546impl Foo { 583impl Foo { fn bar(&self, _: u32) { } }
547 fn bar(&self, _: u32) { }
548}
549 584
550fn bar(_: u32) { } 585fn bar(_: u32) { }
551 586
552fn main() { 587fn main() {
553 let foo = Foo; 588 let foo = Foo;
554 std::thread::spawn(move || foo.bar(<|>)); 589 std::thread::spawn(move || foo.bar(<|>));
555}"#, 590}
591"#,
592 expect![[r#"
593 fn bar(&self, _: u32)
594 (<_: u32>)
595 "#]],
556 ); 596 );
557
558 assert_eq!(info.parameters(), ["&self", "_: u32"]);
559 assert_eq!(info.active_parameter, Some(1));
560 assert_eq!(info.label(), "fn bar(&self, _: u32)");
561 } 597 }
562 598
563 #[test] 599 #[test]
564 fn works_for_tuple_structs() { 600 fn works_for_tuple_structs() {
565 let info = call_info( 601 check(
566 r#" 602 r#"
567/// A cool tuple struct 603/// A cool tuple struct
568struct TS(u32, i32); 604struct S(u32, i32);
569fn main() { 605fn main() {
570 let s = TS(0, <|>); 606 let s = S(0, <|>);
571}"#, 607}
608"#,
609 expect![[r#"
610 A cool tuple struct
611 ------
612 struct S(u32, i32)
613 (u32, <i32>)
614 "#]],
572 ); 615 );
573
574 assert_eq!(info.label(), "struct TS(u32, i32) -> TS");
575 assert_eq!(info.doc().map(|it| it.into()), Some("A cool tuple struct".to_string()));
576 assert_eq!(info.active_parameter, Some(1));
577 } 616 }
578 617
579 #[test] 618 #[test]
580 fn generic_struct() { 619 fn generic_struct() {
581 let info = call_info( 620 check(
582 r#" 621 r#"
583struct TS<T>(T); 622struct S<T>(T);
584fn main() { 623fn main() {
585 let s = TS(<|>); 624 let s = S(<|>);
586}"#, 625}
587 ); 626"#,
588 627 expect![[r#"
589 assert_eq!(info.label(), "struct TS<T>(T) -> TS"); 628 struct S({unknown})
590 assert_eq!(info.active_parameter, Some(0)); 629 (<{unknown}>)
591 } 630 "#]],
592
593 #[test]
594 fn cant_call_named_structs() {
595 no_call_info(
596 r#"
597struct TS { x: u32, y: i32 }
598fn main() {
599 let s = TS(<|>);
600}"#,
601 ); 631 );
602 } 632 }
603 633
604 #[test] 634 #[test]
605 fn works_for_enum_variants() { 635 fn works_for_enum_variants() {
606 let info = call_info( 636 check(
607 r#" 637 r#"
608enum E { 638enum E {
609 /// A Variant 639 /// A Variant
@@ -617,17 +647,32 @@ enum E {
617fn main() { 647fn main() {
618 let a = E::A(<|>); 648 let a = E::A(<|>);
619} 649}
620 "#, 650"#,
651 expect![[r#"
652 A Variant
653 ------
654 enum E::A(i32)
655 (<i32>)
656 "#]],
621 ); 657 );
658 }
622 659
623 assert_eq!(info.label(), "E::A(0: i32)"); 660 #[test]
624 assert_eq!(info.doc().map(|it| it.into()), Some("A Variant".to_string())); 661 fn cant_call_struct_record() {
625 assert_eq!(info.active_parameter, Some(0)); 662 check(
663 r#"
664struct S { x: u32, y: i32 }
665fn main() {
666 let s = S(<|>);
667}
668"#,
669 expect![[""]],
670 );
626 } 671 }
627 672
628 #[test] 673 #[test]
629 fn cant_call_enum_records() { 674 fn cant_call_enum_record() {
630 no_call_info( 675 check(
631 r#" 676 r#"
632enum E { 677enum E {
633 /// A Variant 678 /// A Variant
@@ -641,47 +686,57 @@ enum E {
641fn main() { 686fn main() {
642 let a = E::C(<|>); 687 let a = E::C(<|>);
643} 688}
644 "#, 689"#,
690 expect![[""]],
645 ); 691 );
646 } 692 }
647 693
648 #[test] 694 #[test]
649 fn fn_signature_for_macro() { 695 fn fn_signature_for_call_in_macro() {
650 let info = call_info( 696 check(
651 r#" 697 r#"
652/// empty macro 698macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
653macro_rules! foo { 699fn foo() { }
654 () => {} 700id! {
701 fn bar() { foo(<|>); }
655} 702}
703"#,
704 expect![[r#"
705 fn foo()
706 ()
707 "#]],
708 );
709 }
656 710
657fn f() { 711 #[test]
658 foo!(<|>); 712 fn call_info_for_lambdas() {
713 check(
714 r#"
715struct S;
716fn foo(s: S) -> i32 { 92 }
717fn main() {
718 (|s| foo(s))(<|>)
659} 719}
660 "#, 720 "#,
661 ); 721 expect![[r#"
662 722 (S) -> i32
663 assert_eq!(info.label(), "foo!()"); 723 (<S>)
664 assert_eq!(info.doc().map(|it| it.into()), Some("empty macro".to_string())); 724 "#]],
725 )
665 } 726 }
666 727
667 #[test] 728 #[test]
668 fn fn_signature_for_call_in_macro() { 729 fn call_info_for_fn_ptr() {
669 let info = call_info( 730 check(
670 r#" 731 r#"
671 macro_rules! id { 732fn main(f: fn(i32, f64) -> char) {
672 ($($tt:tt)*) => { $($tt)* } 733 f(0, <|>)
673 } 734}
674 fn foo() { 735 "#,
675 736 expect![[r#"
676 } 737 (i32, f64) -> char
677 id! { 738 (i32, <f64>)
678 fn bar() { 739 "#]],
679 foo(<|>); 740 )
680 }
681 }
682 "#,
683 );
684
685 assert_eq!(info.label(), "fn foo()");
686 } 741 }
687} 742}
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index e1fcf379d..68ac05e4c 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -2,6 +2,9 @@ mod completion_config;
2mod completion_item; 2mod completion_item;
3mod completion_context; 3mod completion_context;
4mod presentation; 4mod presentation;
5mod patterns;
6#[cfg(test)]
7mod test_utils;
5 8
6mod complete_attribute; 9mod complete_attribute;
7mod complete_dot; 10mod complete_dot;
@@ -15,9 +18,6 @@ mod complete_unqualified_path;
15mod complete_postfix; 18mod complete_postfix;
16mod complete_macro_in_item_position; 19mod complete_macro_in_item_position;
17mod complete_trait_impl; 20mod complete_trait_impl;
18mod patterns;
19#[cfg(test)]
20mod test_utils;
21 21
22use ra_ide_db::RootDatabase; 22use ra_ide_db::RootDatabase;
23 23
@@ -63,11 +63,11 @@ pub use crate::completion::{
63// There also snippet completions: 63// There also snippet completions:
64// 64//
65// .Expressions 65// .Expressions
66// - `pd` -> `println!("{:?}")` 66// - `pd` -> `eprintln!(" = {:?}", );`
67// - `ppd` -> `println!("{:#?}")` 67// - `ppd` -> `eprintln!(" = {:#?}", );`
68// 68//
69// .Items 69// .Items
70// - `tfn` -> `#[test] fn f(){}` 70// - `tfn` -> `#[test] fn feature(){}`
71// - `tmod` -> 71// - `tmod` ->
72// ```rust 72// ```rust
73// #[cfg(test)] 73// #[cfg(test)]
@@ -75,7 +75,7 @@ pub use crate::completion::{
75// use super::*; 75// use super::*;
76// 76//
77// #[test] 77// #[test]
78// fn test_fn() {} 78// fn test_name() {}
79// } 79// }
80// ``` 80// ```
81 81
@@ -137,8 +137,8 @@ mod tests {
137 documentation: &'a str, 137 documentation: &'a str,
138 } 138 }
139 139
140 fn check_detail_and_documentation(fixture: &str, expected: DetailAndDocumentation) { 140 fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
141 let (analysis, position) = analysis_and_position(fixture); 141 let (analysis, position) = analysis_and_position(ra_fixture);
142 let config = CompletionConfig::default(); 142 let config = CompletionConfig::default();
143 let completions = analysis.completions(&config, position).unwrap().unwrap(); 143 let completions = analysis.completions(&config, position).unwrap().unwrap();
144 for item in completions { 144 for item in completions {
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs
index 6beeca457..2faaae974 100644
--- a/crates/ra_ide/src/completion/complete_attribute.rs
+++ b/crates/ra_ide/src/completion/complete_attribute.rs
@@ -13,14 +13,18 @@ use crate::completion::{
13 13
14pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 14pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
15 let attribute = ctx.attribute_under_caret.as_ref()?; 15 let attribute = ctx.attribute_under_caret.as_ref()?;
16 16 match (attribute.path(), attribute.token_tree()) {
17 match (attribute.path(), attribute.input()) { 17 (Some(path), Some(token_tree)) if path.to_string() == "derive" => {
18 (Some(path), Some(ast::AttrInput::TokenTree(token_tree)))
19 if path.to_string() == "derive" =>
20 {
21 complete_derive(acc, ctx, token_tree) 18 complete_derive(acc, ctx, token_tree)
22 } 19 }
23 (_, Some(ast::AttrInput::TokenTree(_token_tree))) => {} 20 (Some(path), Some(token_tree))
21 if ["allow", "warn", "deny", "forbid"]
22 .iter()
23 .any(|lint_level| lint_level == &path.to_string()) =>
24 {
25 complete_lint(acc, ctx, token_tree)
26 }
27 (_, Some(_token_tree)) => {}
24 _ => complete_attribute_start(acc, ctx, attribute), 28 _ => complete_attribute_start(acc, ctx, attribute),
25 } 29 }
26 Some(()) 30 Some(())
@@ -46,7 +50,7 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr
46 _ => {} 50 _ => {}
47 } 51 }
48 52
49 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.should_be_inner { 53 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner {
50 acc.add(item); 54 acc.add(item);
51 } 55 }
52 } 56 }
@@ -56,163 +60,76 @@ struct AttrCompletion {
56 label: &'static str, 60 label: &'static str,
57 lookup: Option<&'static str>, 61 lookup: Option<&'static str>,
58 snippet: Option<&'static str>, 62 snippet: Option<&'static str>,
59 should_be_inner: bool, 63 prefer_inner: bool,
64}
65
66impl AttrCompletion {
67 const fn prefer_inner(self) -> AttrCompletion {
68 AttrCompletion { prefer_inner: true, ..self }
69 }
70}
71
72const fn attr(
73 label: &'static str,
74 lookup: Option<&'static str>,
75 snippet: Option<&'static str>,
76) -> AttrCompletion {
77 AttrCompletion { label, lookup, snippet, prefer_inner: false }
60} 78}
61 79
62const ATTRIBUTES: &[AttrCompletion] = &[ 80const ATTRIBUTES: &[AttrCompletion] = &[
63 AttrCompletion { 81 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
64 label: "allow(…)", 82 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
65 snippet: Some("allow(${0:lint})"), 83 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
66 should_be_inner: false, 84 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
67 lookup: Some("allow"), 85 attr(r#"deprecated = "…""#, Some("deprecated"), Some(r#"deprecated = "${0:reason}""#)),
68 }, 86 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
69 AttrCompletion { 87 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
70 label: "cfg_attr(…)", 88 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
71 snippet: Some("cfg_attr(${1:predicate}, ${0:attr})"), 89 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
72 should_be_inner: false,
73 lookup: Some("cfg_attr"),
74 },
75 AttrCompletion {
76 label: "cfg(…)",
77 snippet: Some("cfg(${0:predicate})"),
78 should_be_inner: false,
79 lookup: Some("cfg"),
80 },
81 AttrCompletion {
82 label: "deny(…)",
83 snippet: Some("deny(${0:lint})"),
84 should_be_inner: false,
85 lookup: Some("deny"),
86 },
87 AttrCompletion {
88 label: r#"deprecated = "…""#,
89 snippet: Some(r#"deprecated = "${0:reason}""#),
90 should_be_inner: false,
91 lookup: Some("deprecated"),
92 },
93 AttrCompletion {
94 label: "derive(…)",
95 snippet: Some(r#"derive(${0:Debug})"#),
96 should_be_inner: false,
97 lookup: Some("derive"),
98 },
99 AttrCompletion {
100 label: r#"doc = "…""#,
101 snippet: Some(r#"doc = "${0:docs}""#),
102 should_be_inner: false,
103 lookup: Some("doc"),
104 },
105 AttrCompletion {
106 label: "feature(…)",
107 snippet: Some("feature(${0:flag})"),
108 should_be_inner: true,
109 lookup: Some("feature"),
110 },
111 AttrCompletion {
112 label: "forbid(…)",
113 snippet: Some("forbid(${0:lint})"),
114 should_be_inner: false,
115 lookup: Some("forbid"),
116 },
117 // FIXME: resolve through macro resolution? 90 // FIXME: resolve through macro resolution?
118 AttrCompletion { 91 attr("global_allocator", None, None).prefer_inner(),
119 label: "global_allocator", 92 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
120 snippet: None, 93 attr("inline(…)", Some("inline"), Some("inline(${0:lint})")),
121 should_be_inner: true, 94 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
122 lookup: None, 95 attr("link", None, None),
123 }, 96 attr("macro_export", None, None),
124 AttrCompletion { 97 attr("macro_use", None, None),
125 label: "ignore(…)", 98 attr(r#"must_use = "…""#, Some("must_use"), Some(r#"must_use = "${0:reason}""#)),
126 snippet: Some("ignore(${0:lint})"), 99 attr("no_mangle", None, None),
127 should_be_inner: false, 100 attr("no_std", None, None).prefer_inner(),
128 lookup: Some("ignore"), 101 attr("non_exhaustive", None, None),
129 }, 102 attr("panic_handler", None, None).prefer_inner(),
130 AttrCompletion { 103 attr("path = \"…\"", Some("path"), Some("path =\"${0:path}\"")),
131 label: "inline(…)", 104 attr("proc_macro", None, None),
132 snippet: Some("inline(${0:lint})"), 105 attr("proc_macro_attribute", None, None),
133 should_be_inner: false, 106 attr("proc_macro_derive(…)", Some("proc_macro_derive"), Some("proc_macro_derive(${0:Trait})")),
134 lookup: Some("inline"), 107 attr("recursion_limit = …", Some("recursion_limit"), Some("recursion_limit = ${0:128}"))
135 }, 108 .prefer_inner(),
136 AttrCompletion { 109 attr("repr(…)", Some("repr"), Some("repr(${0:C})")),
137 label: r#"link_name = "…""#, 110 attr(
138 snippet: Some(r#"link_name = "${0:symbol_name}""#), 111 "should_panic(…)",
139 should_be_inner: false, 112 Some("should_panic"),
140 lookup: Some("link_name"), 113 Some(r#"should_panic(expected = "${0:reason}")"#),
141 }, 114 ),
142 AttrCompletion { label: "link", snippet: None, should_be_inner: false, lookup: None }, 115 attr(
143 AttrCompletion { label: "macro_export", snippet: None, should_be_inner: false, lookup: None }, 116 r#"target_feature = "…""#,
144 AttrCompletion { label: "macro_use", snippet: None, should_be_inner: false, lookup: None }, 117 Some("target_feature"),
145 AttrCompletion { 118 Some("target_feature = \"${0:feature}\""),
146 label: r#"must_use = "…""#, 119 ),
147 snippet: Some(r#"must_use = "${0:reason}""#), 120 attr("test", None, None),
148 should_be_inner: false, 121 attr("used", None, None),
149 lookup: Some("must_use"), 122 attr("warn(…)", Some("warn"), Some("warn(${0:lint})")),
150 }, 123 attr(
151 AttrCompletion { label: "no_mangle", snippet: None, should_be_inner: false, lookup: None }, 124 r#"windows_subsystem = "…""#,
152 AttrCompletion { label: "no_std", snippet: None, should_be_inner: true, lookup: None }, 125 Some("windows_subsystem"),
153 AttrCompletion { label: "non_exhaustive", snippet: None, should_be_inner: false, lookup: None }, 126 Some(r#"windows_subsystem = "${0:subsystem}""#),
154 AttrCompletion { label: "panic_handler", snippet: None, should_be_inner: true, lookup: None }, 127 )
155 AttrCompletion { 128 .prefer_inner(),
156 label: "path = \"…\"",
157 snippet: Some("path =\"${0:path}\""),
158 should_be_inner: false,
159 lookup: Some("path"),
160 },
161 AttrCompletion { label: "proc_macro", snippet: None, should_be_inner: false, lookup: None },
162 AttrCompletion {
163 label: "proc_macro_attribute",
164 snippet: None,
165 should_be_inner: false,
166 lookup: None,
167 },
168 AttrCompletion {
169 label: "proc_macro_derive(…)",
170 snippet: Some("proc_macro_derive(${0:Trait})"),
171 should_be_inner: false,
172 lookup: Some("proc_macro_derive"),
173 },
174 AttrCompletion {
175 label: "recursion_limit = …",
176 snippet: Some("recursion_limit = ${0:128}"),
177 should_be_inner: true,
178 lookup: Some("recursion_limit"),
179 },
180 AttrCompletion {
181 label: "repr(…)",
182 snippet: Some("repr(${0:C})"),
183 should_be_inner: false,
184 lookup: Some("repr"),
185 },
186 AttrCompletion {
187 label: "should_panic(…)",
188 snippet: Some(r#"should_panic(expected = "${0:reason}")"#),
189 should_be_inner: false,
190 lookup: Some("should_panic"),
191 },
192 AttrCompletion {
193 label: r#"target_feature = "…""#,
194 snippet: Some("target_feature = \"${0:feature}\""),
195 should_be_inner: false,
196 lookup: Some("target_feature"),
197 },
198 AttrCompletion { label: "test", snippet: None, should_be_inner: false, lookup: None },
199 AttrCompletion { label: "used", snippet: None, should_be_inner: false, lookup: None },
200 AttrCompletion {
201 label: "warn(…)",
202 snippet: Some("warn(${0:lint})"),
203 should_be_inner: false,
204 lookup: Some("warn"),
205 },
206 AttrCompletion {
207 label: r#"windows_subsystem = "…""#,
208 snippet: Some(r#"windows_subsystem = "${0:subsystem}""#),
209 should_be_inner: true,
210 lookup: Some("windows_subsystem"),
211 },
212]; 129];
213 130
214fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) { 131fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
215 if let Ok(existing_derives) = parse_derive_input(derive_input) { 132 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) {
216 for derive_completion in DEFAULT_DERIVE_COMPLETIONS 133 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
217 .into_iter() 134 .into_iter()
218 .filter(|completion| !existing_derives.contains(completion.label)) 135 .filter(|completion| !existing_derives.contains(completion.label))
@@ -245,7 +162,26 @@ fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input:
245 } 162 }
246} 163}
247 164
248fn parse_derive_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> { 165fn complete_lint(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
166 if let Ok(existing_lints) = parse_comma_sep_input(derive_input) {
167 for lint_completion in DEFAULT_LINT_COMPLETIONS
168 .into_iter()
169 .filter(|completion| !existing_lints.contains(completion.label))
170 {
171 acc.add(
172 CompletionItem::new(
173 CompletionKind::Attribute,
174 ctx.source_range(),
175 lint_completion.label,
176 )
177 .kind(CompletionItemKind::Attribute)
178 .detail(lint_completion.description),
179 );
180 }
181 }
182}
183
184fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
249 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) { 185 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
250 (Some(left_paren), Some(right_paren)) 186 (Some(left_paren), Some(right_paren))
251 if left_paren.kind() == SyntaxKind::L_PAREN 187 if left_paren.kind() == SyntaxKind::L_PAREN
@@ -282,7 +218,7 @@ fn parse_derive_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>,
282 218
283fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> { 219fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
284 let mut result = FxHashSet::default(); 220 let mut result = FxHashSet::default();
285 ctx.scope().process_all_names(&mut |name, scope_def| { 221 ctx.scope.process_all_names(&mut |name, scope_def| {
286 if let hir::ScopeDef::MacroDef(mac) = scope_def { 222 if let hir::ScopeDef::MacroDef(mac) = scope_def {
287 if mac.is_derive_macro() { 223 if mac.is_derive_macro() {
288 result.insert(name.to_string()); 224 result.insert(name.to_string());
@@ -299,6 +235,7 @@ struct DeriveCompletion {
299 235
300/// Standard Rust derives and the information about their dependencies 236/// Standard Rust derives and the information about their dependencies
301/// (the dependencies are needed so that the main derive don't break the compilation when added) 237/// (the dependencies are needed so that the main derive don't break the compilation when added)
238#[rustfmt::skip]
302const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[ 239const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
303 DeriveCompletion { label: "Clone", dependencies: &[] }, 240 DeriveCompletion { label: "Clone", dependencies: &[] },
304 DeriveCompletion { label: "Copy", dependencies: &["Clone"] }, 241 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
@@ -311,679 +248,397 @@ const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
311 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] }, 248 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
312]; 249];
313 250
251struct LintCompletion {
252 label: &'static str,
253 description: &'static str,
254}
255
256#[rustfmt::skip]
257const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
258 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"# },
259 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
260 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
261 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
262 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
263 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
264 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
265 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
266 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
267 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
268 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
269 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
270 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
271 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
272 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
273 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
274 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
275 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
276 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
277 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
278 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
279 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
280 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
281 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
282 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
283 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
284 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
285 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
286 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
287 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
288 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
289 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
290 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
291 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
292 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
293 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
294 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
295 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
296 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
297 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
298 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
299 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
300 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
301 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
302 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
303 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
304 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
305 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
306 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
307 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
308 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
309 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
310 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
311 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
312 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
313 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
314 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
315 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
316 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
317 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
318 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
319 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
320 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
321 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
322 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
323 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
324 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
325 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
326 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
327 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
328 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
329 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
330 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
331 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
332 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
333 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
334 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
335 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
336 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
337 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
338 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
339 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
340 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
341 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
342 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
343 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
344 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
345 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
346 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
347 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
348 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
349 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
350 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
351 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
352 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
353 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
354 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
355 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
356 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
357 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
358 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
359 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
360 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
361 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
362 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"# },
363 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
364 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
365 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
366 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
367 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
368 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
369 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
370 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
371 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
372 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
373];
374
314#[cfg(test)] 375#[cfg(test)]
315mod tests { 376mod tests {
316 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 377 use expect::{expect, Expect};
317 use insta::assert_debug_snapshot;
318 378
319 fn do_attr_completion(code: &str) -> Vec<CompletionItem> { 379 use crate::completion::{test_utils::completion_list, CompletionKind};
320 do_completion(code, CompletionKind::Attribute) 380
381 fn check(ra_fixture: &str, expect: Expect) {
382 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
383 expect.assert_eq(&actual);
321 } 384 }
322 385
323 #[test] 386 #[test]
324 fn empty_derive_completion() { 387 fn empty_derive_completion() {
325 assert_debug_snapshot!( 388 check(
326 do_attr_completion( 389 r#"
327 r" 390#[derive(<|>)]
328 #[derive(<|>)] 391struct Test {}
329 struct Test {} 392 "#,
330 ", 393 expect![[r#"
331 ), 394 at Clone
332 @r###" 395 at Copy, Clone
333 [ 396 at Debug
334 CompletionItem { 397 at Default
335 label: "Clone", 398 at Eq, PartialEq
336 source_range: 9..9, 399 at Hash
337 delete: 9..9, 400 at Ord, PartialOrd, Eq, PartialEq
338 insert: "Clone", 401 at PartialEq
339 kind: Attribute, 402 at PartialOrd, PartialEq
340 }, 403 "#]],
341 CompletionItem {
342 label: "Copy, Clone",
343 source_range: 9..9,
344 delete: 9..9,
345 insert: "Copy, Clone",
346 kind: Attribute,
347 },
348 CompletionItem {
349 label: "Debug",
350 source_range: 9..9,
351 delete: 9..9,
352 insert: "Debug",
353 kind: Attribute,
354 },
355 CompletionItem {
356 label: "Default",
357 source_range: 9..9,
358 delete: 9..9,
359 insert: "Default",
360 kind: Attribute,
361 },
362 CompletionItem {
363 label: "Eq, PartialEq",
364 source_range: 9..9,
365 delete: 9..9,
366 insert: "Eq, PartialEq",
367 kind: Attribute,
368 },
369 CompletionItem {
370 label: "Hash",
371 source_range: 9..9,
372 delete: 9..9,
373 insert: "Hash",
374 kind: Attribute,
375 },
376 CompletionItem {
377 label: "Ord, PartialOrd, Eq, PartialEq",
378 source_range: 9..9,
379 delete: 9..9,
380 insert: "Ord, PartialOrd, Eq, PartialEq",
381 kind: Attribute,
382 },
383 CompletionItem {
384 label: "PartialEq",
385 source_range: 9..9,
386 delete: 9..9,
387 insert: "PartialEq",
388 kind: Attribute,
389 },
390 CompletionItem {
391 label: "PartialOrd, PartialEq",
392 source_range: 9..9,
393 delete: 9..9,
394 insert: "PartialOrd, PartialEq",
395 kind: Attribute,
396 },
397 ]
398 "###
399 ); 404 );
400 } 405 }
401 406
402 #[test] 407 #[test]
408 fn empty_lint_completion() {
409 check(
410 r#"#[allow(<|>)]"#,
411 expect![[r#"
412 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
413 at ambiguous_associated_items ambiguous associated items
414 at anonymous_parameters detects anonymous parameters
415 at arithmetic_overflow arithmetic operation overflows
416 at array_into_iter detects calling `into_iter` on arrays
417 at asm_sub_register using only a subset of a register for inline asm inputs
418 at bare_trait_objects suggest using `dyn Trait` for trait objects
419 at bindings_with_variant_name detects pattern bindings with the same name as one of the matched variants
420 at box_pointers use of owned (Box type) heap memory
421 at cenum_impl_drop_cast a C-like enum implementing Drop is cast
422 at clashing_extern_declarations detects when an extern fn has been declared with the same name but different types
423 at coherence_leak_check distinct impls distinguished only by the leak-check code
424 at conflicting_repr_hints conflicts between `#[repr(..)]` hints that were previously accepted and used in practice
425 at confusable_idents detects visually confusable pairs between identifiers
426 at const_err constant evaluation detected erroneous expression
427 at dead_code detect unused, unexported items
428 at deprecated detects use of deprecated items
429 at deprecated_in_future detects use of items that will be deprecated in a future version
430 at elided_lifetimes_in_paths hidden lifetime parameters in types are deprecated
431 at ellipsis_inclusive_range_patterns `...` range patterns are deprecated
432 at explicit_outlives_requirements outlives requirements can be inferred
433 at exported_private_dependencies public interface leaks type from a private dependency
434 at ill_formed_attribute_input ill-formed attribute inputs that were previously accepted and used in practice
435 at illegal_floating_point_literal_pattern floating-point literals cannot be used in patterns
436 at improper_ctypes proper use of libc types in foreign modules
437 at improper_ctypes_definitions proper use of libc types in foreign item definitions
438 at incomplete_features incomplete features that may function improperly in some or all cases
439 at incomplete_include trailing content in included file
440 at indirect_structural_match pattern with const indirectly referencing non-structural-match type
441 at inline_no_sanitize detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`
442 at intra_doc_link_resolution_failure failures in resolving intra-doc link targets
443 at invalid_codeblock_attributes codeblock attribute looks a lot like a known one
444 at invalid_type_param_default type parameter default erroneously allowed in invalid location
445 at invalid_value an invalid value is being created (such as a NULL reference)
446 at irrefutable_let_patterns detects irrefutable patterns in if-let and while-let statements
447 at keyword_idents detects edition keywords being used as an identifier
448 at late_bound_lifetime_arguments detects generic lifetime arguments in path segments with late bound lifetime parameters
449 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
450 at macro_use_extern_crate the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system
451 at meta_variable_misuse possible meta-variable misuse at macro definition
452 at missing_copy_implementations detects potentially-forgotten implementations of `Copy`
453 at missing_crate_level_docs detects crates with no crate-level documentation
454 at missing_debug_implementations detects missing implementations of Debug
455 at missing_doc_code_examples detects publicly-exported items without code samples in their documentation
456 at missing_docs detects missing documentation for public members
457 at missing_fragment_specifier detects missing fragment specifiers in unused `macro_rules!` patterns
458 at mixed_script_confusables detects Unicode scripts whose mixed script confusables codepoints are solely used
459 at mutable_borrow_reservation_conflict reservation of a two-phased borrow conflicts with other shared borrows
460 at mutable_transmutes mutating transmuted &mut T from &T may cause undefined behavior
461 at no_mangle_const_items const items will not have their symbols exported
462 at no_mangle_generic_items generic items must be mangled
463 at non_ascii_idents detects non-ASCII identifiers
464 at non_camel_case_types types, variants, traits and type parameters should have camel case names
465 at non_shorthand_field_patterns using `Struct { x: x }` instead of `Struct { x }` in a pattern
466 at non_snake_case variables, methods, functions, lifetime parameters and modules should have snake case names
467 at non_upper_case_globals static constants should have uppercase identifiers
468 at order_dependent_trait_objects trait-object types were treated as different depending on marker-trait order
469 at overflowing_literals literal out of range for its type
470 at overlapping_patterns detects overlapping patterns
471 at path_statements path statements with no effect
472 at patterns_in_fns_without_body patterns in functions without body were erroneously allowed
473 at private_doc_tests detects code samples in docs of private items not documented by rustdoc
474 at private_in_public detect private items in public interfaces not caught by the old implementation
475 at proc_macro_derive_resolution_fallback detects proc macro derives using inaccessible names from parent modules
476 at pub_use_of_private_extern_crate detect public re-exports of private extern crates
477 at redundant_semicolons detects unnecessary trailing semicolons
478 at renamed_and_removed_lints lints that have been renamed or removed
479 at safe_packed_borrows safe borrows of fields of packed structs were erroneously allowed
480 at single_use_lifetimes detects lifetime parameters that are only used once
481 at soft_unstable a feature gate that doesn't break dependent crates
482 at stable_features stable features found in `#[feature]` directive
483 at trivial_bounds these bounds don't depend on an type parameters
484 at trivial_casts detects trivial casts which could be removed
485 at trivial_numeric_casts detects trivial casts of numeric types which could be removed
486 at type_alias_bounds bounds in type aliases are not enforced
487 at tyvar_behind_raw_pointer raw pointer to an inference variable
488 at unaligned_references detects unaligned references to fields of packed structs
489 at uncommon_codepoints detects uncommon Unicode codepoints in identifiers
490 at unconditional_panic operation will cause a panic at runtime
491 at unconditional_recursion functions that cannot return without calling themselves
492 at unknown_crate_types unknown crate type found in `#[crate_type]` directive
493 at unknown_lints unrecognized lint attribute
494 at unnameable_test_items detects an item that cannot be named being marked as `#[test_case]`
495 at unreachable_code detects unreachable code paths
496 at unreachable_patterns detects unreachable patterns
497 at unreachable_pub `pub` items not reachable from crate root
498 at unsafe_code usage of `unsafe` code
499 at unsafe_op_in_unsafe_fn unsafe operations in unsafe functions without an explicit unsafe block are deprecated
500 at unstable_features enabling unstable features (deprecated. do not use)
501 at unstable_name_collisions detects name collision with an existing but unstable method
502 at unused_allocation detects unnecessary allocations that can be eliminated
503 at unused_assignments detect assignments that will never be read
504 at unused_attributes detects attributes that were not used by the compiler
505 at unused_braces unnecessary braces around an expression
506 at unused_comparisons comparisons made useless by limits of the types involved
507 at unused_crate_dependencies crate dependencies that are never used
508 at unused_doc_comments detects doc comments that aren't used by rustdoc
509 at unused_extern_crates extern crates that are never used
510 at unused_features unused features found in crate-level `#[feature]` directives
511 at unused_import_braces unnecessary braces around an imported item
512 at unused_imports imports that are never used
513 at unused_labels detects labels that are never used
514 at unused_lifetimes detects lifetime parameters that are never used
515 at unused_macros detects macros that were not used
516 at unused_must_use unused result of a type flagged as `#[must_use]`
517 at unused_mut detect mut variables which don't need to be mutable
518 at unused_parens `if`, `match`, `while` and `return` do not need parentheses
519 at unused_qualifications detects unnecessarily qualified names
520 at unused_results unused result of an expression in a statement
521 at unused_unsafe unnecessary use of an `unsafe` block
522 at unused_variables detect variables which are not used in any way
523 at variant_size_differences detects enums with widely varying variant sizes
524 at warnings mass-change the level for lints which produce warnings
525 at where_clauses_object_safety checks the object safety of where clauses
526 at while_true suggest using `loop { }` instead of `while true { }`
527 "#]],
528 )
529 }
530
531 #[test]
403 fn no_completion_for_incorrect_derive() { 532 fn no_completion_for_incorrect_derive() {
404 assert_debug_snapshot!( 533 check(
405 do_attr_completion( 534 r#"
406 r" 535#[derive{<|>)]
407 #[derive{<|>)] 536struct Test {}
408 struct Test {} 537"#,
409 ", 538 expect![[r#""#]],
410 ), 539 )
411 @"[]"
412 );
413 } 540 }
414 541
415 #[test] 542 #[test]
416 fn derive_with_input_completion() { 543 fn derive_with_input_completion() {
417 assert_debug_snapshot!( 544 check(
418 do_attr_completion( 545 r#"
419 r" 546#[derive(serde::Serialize, PartialEq, <|>)]
420 #[derive(serde::Serialize, PartialEq, <|>)] 547struct Test {}
421 struct Test {} 548"#,
422 ", 549 expect![[r#"
423 ), 550 at Clone
424 @r###" 551 at Copy, Clone
425 [ 552 at Debug
426 CompletionItem { 553 at Default
427 label: "Clone", 554 at Eq
428 source_range: 38..38, 555 at Hash
429 delete: 38..38, 556 at Ord, PartialOrd, Eq
430 insert: "Clone", 557 at PartialOrd
431 kind: Attribute, 558 "#]],
432 }, 559 )
433 CompletionItem {
434 label: "Copy, Clone",
435 source_range: 38..38,
436 delete: 38..38,
437 insert: "Copy, Clone",
438 kind: Attribute,
439 },
440 CompletionItem {
441 label: "Debug",
442 source_range: 38..38,
443 delete: 38..38,
444 insert: "Debug",
445 kind: Attribute,
446 },
447 CompletionItem {
448 label: "Default",
449 source_range: 38..38,
450 delete: 38..38,
451 insert: "Default",
452 kind: Attribute,
453 },
454 CompletionItem {
455 label: "Eq",
456 source_range: 38..38,
457 delete: 38..38,
458 insert: "Eq",
459 kind: Attribute,
460 },
461 CompletionItem {
462 label: "Hash",
463 source_range: 38..38,
464 delete: 38..38,
465 insert: "Hash",
466 kind: Attribute,
467 },
468 CompletionItem {
469 label: "Ord, PartialOrd, Eq",
470 source_range: 38..38,
471 delete: 38..38,
472 insert: "Ord, PartialOrd, Eq",
473 kind: Attribute,
474 },
475 CompletionItem {
476 label: "PartialOrd",
477 source_range: 38..38,
478 delete: 38..38,
479 insert: "PartialOrd",
480 kind: Attribute,
481 },
482 ]
483 "###
484 );
485 } 560 }
486 561
487 #[test] 562 #[test]
488 fn test_attribute_completion() { 563 fn test_attribute_completion() {
489 assert_debug_snapshot!( 564 check(
490 do_attr_completion( 565 r#"#[<|>]"#,
491 r" 566 expect![[r#"
492 #[<|>] 567 at allow(…)
493 ", 568 at cfg(…)
494 ), 569 at cfg_attr(…)
495 @r###" 570 at deny(…)
496 [ 571 at deprecated = "…"
497 CompletionItem { 572 at derive(…)
498 label: "allow(…)", 573 at doc = "…"
499 source_range: 2..2, 574 at forbid(…)
500 delete: 2..2, 575 at ignore = "…"
501 insert: "allow(${0:lint})", 576 at inline(…)
502 kind: Attribute, 577 at link
503 lookup: "allow", 578 at link_name = "…"
504 }, 579 at macro_export
505 CompletionItem { 580 at macro_use
506 label: "cfg(…)", 581 at must_use = "…"
507 source_range: 2..2, 582 at no_mangle
508 delete: 2..2, 583 at non_exhaustive
509 insert: "cfg(${0:predicate})", 584 at path = "…"
510 kind: Attribute, 585 at proc_macro
511 lookup: "cfg", 586 at proc_macro_attribute
512 }, 587 at proc_macro_derive(…)
513 CompletionItem { 588 at repr(…)
514 label: "cfg_attr(…)", 589 at should_panic(…)
515 source_range: 2..2, 590 at target_feature = "…"
516 delete: 2..2, 591 at test
517 insert: "cfg_attr(${1:predicate}, ${0:attr})", 592 at used
518 kind: Attribute, 593 at warn(…)
519 lookup: "cfg_attr", 594 "#]],
520 }, 595 )
521 CompletionItem {
522 label: "deny(…)",
523 source_range: 2..2,
524 delete: 2..2,
525 insert: "deny(${0:lint})",
526 kind: Attribute,
527 lookup: "deny",
528 },
529 CompletionItem {
530 label: "deprecated = \"…\"",
531 source_range: 2..2,
532 delete: 2..2,
533 insert: "deprecated = \"${0:reason}\"",
534 kind: Attribute,
535 lookup: "deprecated",
536 },
537 CompletionItem {
538 label: "derive(…)",
539 source_range: 2..2,
540 delete: 2..2,
541 insert: "derive(${0:Debug})",
542 kind: Attribute,
543 lookup: "derive",
544 },
545 CompletionItem {
546 label: "doc = \"…\"",
547 source_range: 2..2,
548 delete: 2..2,
549 insert: "doc = \"${0:docs}\"",
550 kind: Attribute,
551 lookup: "doc",
552 },
553 CompletionItem {
554 label: "forbid(…)",
555 source_range: 2..2,
556 delete: 2..2,
557 insert: "forbid(${0:lint})",
558 kind: Attribute,
559 lookup: "forbid",
560 },
561 CompletionItem {
562 label: "ignore(…)",
563 source_range: 2..2,
564 delete: 2..2,
565 insert: "ignore(${0:lint})",
566 kind: Attribute,
567 lookup: "ignore",
568 },
569 CompletionItem {
570 label: "inline(…)",
571 source_range: 2..2,
572 delete: 2..2,
573 insert: "inline(${0:lint})",
574 kind: Attribute,
575 lookup: "inline",
576 },
577 CompletionItem {
578 label: "link",
579 source_range: 2..2,
580 delete: 2..2,
581 insert: "link",
582 kind: Attribute,
583 },
584 CompletionItem {
585 label: "link_name = \"…\"",
586 source_range: 2..2,
587 delete: 2..2,
588 insert: "link_name = \"${0:symbol_name}\"",
589 kind: Attribute,
590 lookup: "link_name",
591 },
592 CompletionItem {
593 label: "macro_export",
594 source_range: 2..2,
595 delete: 2..2,
596 insert: "macro_export",
597 kind: Attribute,
598 },
599 CompletionItem {
600 label: "macro_use",
601 source_range: 2..2,
602 delete: 2..2,
603 insert: "macro_use",
604 kind: Attribute,
605 },
606 CompletionItem {
607 label: "must_use = \"…\"",
608 source_range: 2..2,
609 delete: 2..2,
610 insert: "must_use = \"${0:reason}\"",
611 kind: Attribute,
612 lookup: "must_use",
613 },
614 CompletionItem {
615 label: "no_mangle",
616 source_range: 2..2,
617 delete: 2..2,
618 insert: "no_mangle",
619 kind: Attribute,
620 },
621 CompletionItem {
622 label: "non_exhaustive",
623 source_range: 2..2,
624 delete: 2..2,
625 insert: "non_exhaustive",
626 kind: Attribute,
627 },
628 CompletionItem {
629 label: "path = \"…\"",
630 source_range: 2..2,
631 delete: 2..2,
632 insert: "path =\"${0:path}\"",
633 kind: Attribute,
634 lookup: "path",
635 },
636 CompletionItem {
637 label: "proc_macro",
638 source_range: 2..2,
639 delete: 2..2,
640 insert: "proc_macro",
641 kind: Attribute,
642 },
643 CompletionItem {
644 label: "proc_macro_attribute",
645 source_range: 2..2,
646 delete: 2..2,
647 insert: "proc_macro_attribute",
648 kind: Attribute,
649 },
650 CompletionItem {
651 label: "proc_macro_derive(…)",
652 source_range: 2..2,
653 delete: 2..2,
654 insert: "proc_macro_derive(${0:Trait})",
655 kind: Attribute,
656 lookup: "proc_macro_derive",
657 },
658 CompletionItem {
659 label: "repr(…)",
660 source_range: 2..2,
661 delete: 2..2,
662 insert: "repr(${0:C})",
663 kind: Attribute,
664 lookup: "repr",
665 },
666 CompletionItem {
667 label: "should_panic(…)",
668 source_range: 2..2,
669 delete: 2..2,
670 insert: "should_panic(expected = \"${0:reason}\")",
671 kind: Attribute,
672 lookup: "should_panic",
673 },
674 CompletionItem {
675 label: "target_feature = \"…\"",
676 source_range: 2..2,
677 delete: 2..2,
678 insert: "target_feature = \"${0:feature}\"",
679 kind: Attribute,
680 lookup: "target_feature",
681 },
682 CompletionItem {
683 label: "test",
684 source_range: 2..2,
685 delete: 2..2,
686 insert: "test",
687 kind: Attribute,
688 },
689 CompletionItem {
690 label: "used",
691 source_range: 2..2,
692 delete: 2..2,
693 insert: "used",
694 kind: Attribute,
695 },
696 CompletionItem {
697 label: "warn(…)",
698 source_range: 2..2,
699 delete: 2..2,
700 insert: "warn(${0:lint})",
701 kind: Attribute,
702 lookup: "warn",
703 },
704 ]
705 "###
706 );
707 } 596 }
708 597
709 #[test] 598 #[test]
710 fn test_attribute_completion_inside_nested_attr() { 599 fn test_attribute_completion_inside_nested_attr() {
711 assert_debug_snapshot!( 600 check(r#"#[cfg(<|>)]"#, expect![[]])
712 do_attr_completion(
713 r"
714 #[allow(<|>)]
715 ",
716 ),
717 @r###"
718 []
719 "###
720 );
721 } 601 }
722 602
723 #[test] 603 #[test]
724 fn test_inner_attribute_completion() { 604 fn test_inner_attribute_completion() {
725 assert_debug_snapshot!( 605 check(
726 do_attr_completion( 606 r"#![<|>]",
727 r" 607 expect![[r#"
728 #![<|>] 608 at allow(…)
729 ", 609 at cfg(…)
730 ), 610 at cfg_attr(…)
731 @r###" 611 at deny(…)
732 [ 612 at deprecated = "…"
733 CompletionItem { 613 at derive(…)
734 label: "allow(…)", 614 at doc = "…"
735 source_range: 3..3, 615 at feature(…)
736 delete: 3..3, 616 at forbid(…)
737 insert: "allow(${0:lint})", 617 at global_allocator
738 kind: Attribute, 618 at ignore = "…"
739 lookup: "allow", 619 at inline(…)
740 }, 620 at link
741 CompletionItem { 621 at link_name = "…"
742 label: "cfg(…)", 622 at macro_export
743 source_range: 3..3, 623 at macro_use
744 delete: 3..3, 624 at must_use = "…"
745 insert: "cfg(${0:predicate})", 625 at no_mangle
746 kind: Attribute, 626 at no_std
747 lookup: "cfg", 627 at non_exhaustive
748 }, 628 at panic_handler
749 CompletionItem { 629 at path = "…"
750 label: "cfg_attr(…)", 630 at proc_macro
751 source_range: 3..3, 631 at proc_macro_attribute
752 delete: 3..3, 632 at proc_macro_derive(…)
753 insert: "cfg_attr(${1:predicate}, ${0:attr})", 633 at recursion_limit = …
754 kind: Attribute, 634 at repr(…)
755 lookup: "cfg_attr", 635 at should_panic(…)
756 }, 636 at target_feature = "…"
757 CompletionItem { 637 at test
758 label: "deny(…)", 638 at used
759 source_range: 3..3, 639 at warn(…)
760 delete: 3..3, 640 at windows_subsystem = "…"
761 insert: "deny(${0:lint})", 641 "#]],
762 kind: Attribute,
763 lookup: "deny",
764 },
765 CompletionItem {
766 label: "deprecated = \"…\"",
767 source_range: 3..3,
768 delete: 3..3,
769 insert: "deprecated = \"${0:reason}\"",
770 kind: Attribute,
771 lookup: "deprecated",
772 },
773 CompletionItem {
774 label: "derive(…)",
775 source_range: 3..3,
776 delete: 3..3,
777 insert: "derive(${0:Debug})",
778 kind: Attribute,
779 lookup: "derive",
780 },
781 CompletionItem {
782 label: "doc = \"…\"",
783 source_range: 3..3,
784 delete: 3..3,
785 insert: "doc = \"${0:docs}\"",
786 kind: Attribute,
787 lookup: "doc",
788 },
789 CompletionItem {
790 label: "feature(…)",
791 source_range: 3..3,
792 delete: 3..3,
793 insert: "feature(${0:flag})",
794 kind: Attribute,
795 lookup: "feature",
796 },
797 CompletionItem {
798 label: "forbid(…)",
799 source_range: 3..3,
800 delete: 3..3,
801 insert: "forbid(${0:lint})",
802 kind: Attribute,
803 lookup: "forbid",
804 },
805 CompletionItem {
806 label: "global_allocator",
807 source_range: 3..3,
808 delete: 3..3,
809 insert: "global_allocator",
810 kind: Attribute,
811 },
812 CompletionItem {
813 label: "ignore(…)",
814 source_range: 3..3,
815 delete: 3..3,
816 insert: "ignore(${0:lint})",
817 kind: Attribute,
818 lookup: "ignore",
819 },
820 CompletionItem {
821 label: "inline(…)",
822 source_range: 3..3,
823 delete: 3..3,
824 insert: "inline(${0:lint})",
825 kind: Attribute,
826 lookup: "inline",
827 },
828 CompletionItem {
829 label: "link",
830 source_range: 3..3,
831 delete: 3..3,
832 insert: "link",
833 kind: Attribute,
834 },
835 CompletionItem {
836 label: "link_name = \"…\"",
837 source_range: 3..3,
838 delete: 3..3,
839 insert: "link_name = \"${0:symbol_name}\"",
840 kind: Attribute,
841 lookup: "link_name",
842 },
843 CompletionItem {
844 label: "macro_export",
845 source_range: 3..3,
846 delete: 3..3,
847 insert: "macro_export",
848 kind: Attribute,
849 },
850 CompletionItem {
851 label: "macro_use",
852 source_range: 3..3,
853 delete: 3..3,
854 insert: "macro_use",
855 kind: Attribute,
856 },
857 CompletionItem {
858 label: "must_use = \"…\"",
859 source_range: 3..3,
860 delete: 3..3,
861 insert: "must_use = \"${0:reason}\"",
862 kind: Attribute,
863 lookup: "must_use",
864 },
865 CompletionItem {
866 label: "no_mangle",
867 source_range: 3..3,
868 delete: 3..3,
869 insert: "no_mangle",
870 kind: Attribute,
871 },
872 CompletionItem {
873 label: "no_std",
874 source_range: 3..3,
875 delete: 3..3,
876 insert: "no_std",
877 kind: Attribute,
878 },
879 CompletionItem {
880 label: "non_exhaustive",
881 source_range: 3..3,
882 delete: 3..3,
883 insert: "non_exhaustive",
884 kind: Attribute,
885 },
886 CompletionItem {
887 label: "panic_handler",
888 source_range: 3..3,
889 delete: 3..3,
890 insert: "panic_handler",
891 kind: Attribute,
892 },
893 CompletionItem {
894 label: "path = \"…\"",
895 source_range: 3..3,
896 delete: 3..3,
897 insert: "path =\"${0:path}\"",
898 kind: Attribute,
899 lookup: "path",
900 },
901 CompletionItem {
902 label: "proc_macro",
903 source_range: 3..3,
904 delete: 3..3,
905 insert: "proc_macro",
906 kind: Attribute,
907 },
908 CompletionItem {
909 label: "proc_macro_attribute",
910 source_range: 3..3,
911 delete: 3..3,
912 insert: "proc_macro_attribute",
913 kind: Attribute,
914 },
915 CompletionItem {
916 label: "proc_macro_derive(…)",
917 source_range: 3..3,
918 delete: 3..3,
919 insert: "proc_macro_derive(${0:Trait})",
920 kind: Attribute,
921 lookup: "proc_macro_derive",
922 },
923 CompletionItem {
924 label: "recursion_limit = …",
925 source_range: 3..3,
926 delete: 3..3,
927 insert: "recursion_limit = ${0:128}",
928 kind: Attribute,
929 lookup: "recursion_limit",
930 },
931 CompletionItem {
932 label: "repr(…)",
933 source_range: 3..3,
934 delete: 3..3,
935 insert: "repr(${0:C})",
936 kind: Attribute,
937 lookup: "repr",
938 },
939 CompletionItem {
940 label: "should_panic(…)",
941 source_range: 3..3,
942 delete: 3..3,
943 insert: "should_panic(expected = \"${0:reason}\")",
944 kind: Attribute,
945 lookup: "should_panic",
946 },
947 CompletionItem {
948 label: "target_feature = \"…\"",
949 source_range: 3..3,
950 delete: 3..3,
951 insert: "target_feature = \"${0:feature}\"",
952 kind: Attribute,
953 lookup: "target_feature",
954 },
955 CompletionItem {
956 label: "test",
957 source_range: 3..3,
958 delete: 3..3,
959 insert: "test",
960 kind: Attribute,
961 },
962 CompletionItem {
963 label: "used",
964 source_range: 3..3,
965 delete: 3..3,
966 insert: "used",
967 kind: Attribute,
968 },
969 CompletionItem {
970 label: "warn(…)",
971 source_range: 3..3,
972 delete: 3..3,
973 insert: "warn(${0:lint})",
974 kind: Attribute,
975 lookup: "warn",
976 },
977 CompletionItem {
978 label: "windows_subsystem = \"…\"",
979 source_range: 3..3,
980 delete: 3..3,
981 insert: "windows_subsystem = \"${0:subsystem}\"",
982 kind: Attribute,
983 lookup: "windows_subsystem",
984 },
985 ]
986 "###
987 ); 642 );
988 } 643 }
989} 644}
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index ee4e24fca..532665285 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -1,17 +1,12 @@
1//! FIXME: write short doc here 1//! Completes references after dot (fields and method calls).
2 2
3use hir::{HasVisibility, Type}; 3use hir::{HasVisibility, Type};
4
5use crate::{
6 completion::{
7 completion_context::CompletionContext,
8 completion_item::{CompletionKind, Completions},
9 },
10 CompletionItem,
11};
12use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use test_utils::mark;
13 6
14/// Complete dot accesses, i.e. fields or methods (and .await syntax). 7use crate::completion::{completion_context::CompletionContext, completion_item::Completions};
8
9/// Complete dot accesses, i.e. fields or methods.
15pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 10pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
16 let dot_receiver = match &ctx.dot_receiver { 11 let dot_receiver = match &ctx.dot_receiver {
17 Some(expr) => expr, 12 Some(expr) => expr,
@@ -23,24 +18,18 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
23 _ => return, 18 _ => return,
24 }; 19 };
25 20
26 if !ctx.is_call { 21 if ctx.is_call {
22 mark::hit!(test_no_struct_field_completion_for_method_call);
23 } else {
27 complete_fields(acc, ctx, &receiver_ty); 24 complete_fields(acc, ctx, &receiver_ty);
28 } 25 }
29 complete_methods(acc, ctx, &receiver_ty); 26 complete_methods(acc, ctx, &receiver_ty);
30
31 // Suggest .await syntax for types that implement Future trait
32 if receiver_ty.impls_future(ctx.db) {
33 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
34 .detail("expr.await")
35 .insert_text("await")
36 .add_to(acc);
37 }
38} 27}
39 28
40fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 29fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
41 for receiver in receiver.autoderef(ctx.db) { 30 for receiver in receiver.autoderef(ctx.db) {
42 for (field, ty) in receiver.fields(ctx.db) { 31 for (field, ty) in receiver.fields(ctx.db) {
43 if ctx.scope().module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { 32 if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
44 // Skip private field. FIXME: If the definition location of the 33 // Skip private field. FIXME: If the definition location of the
45 // field is editable, we should show the completion 34 // field is editable, we should show the completion
46 continue; 35 continue;
@@ -57,10 +46,10 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty
57fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 46fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
58 if let Some(krate) = ctx.krate { 47 if let Some(krate) = ctx.krate {
59 let mut seen_methods = FxHashSet::default(); 48 let mut seen_methods = FxHashSet::default();
60 let traits_in_scope = ctx.scope().traits_in_scope(); 49 let traits_in_scope = ctx.scope.traits_in_scope();
61 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { 50 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
62 if func.has_self_param(ctx.db) 51 if func.has_self_param(ctx.db)
63 && ctx.scope().module().map_or(true, |m| func.is_visible_from(ctx.db, m)) 52 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
64 && seen_methods.insert(func.name(ctx.db)) 53 && seen_methods.insert(func.name(ctx.db))
65 { 54 {
66 acc.add_function(ctx, func, None); 55 acc.add_function(ctx, func, None);
@@ -72,801 +61,356 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T
72 61
73#[cfg(test)] 62#[cfg(test)]
74mod tests { 63mod tests {
75 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 64 use expect::{expect, Expect};
76 use insta::assert_debug_snapshot; 65 use test_utils::mark;
66
67 use crate::completion::{test_utils::completion_list, CompletionKind};
77 68
78 fn do_ref_completion(code: &str) -> Vec<CompletionItem> { 69 fn check(ra_fixture: &str, expect: Expect) {
79 do_completion(code, CompletionKind::Reference) 70 let actual = completion_list(ra_fixture, CompletionKind::Reference);
71 expect.assert_eq(&actual);
80 } 72 }
81 73
82 #[test] 74 #[test]
83 fn test_struct_field_completion() { 75 fn test_struct_field_and_method_completion() {
84 assert_debug_snapshot!( 76 check(
85 do_ref_completion( 77 r#"
86 r" 78struct S { foo: u32 }
87 struct A { the_field: u32 } 79impl S {
88 fn foo(a: A) { 80 fn bar(&self) {}
89 a.<|> 81}
90 } 82fn foo(s: S) { s.<|> }
91 ", 83"#,
92 ), 84 expect![[r#"
93 @r###" 85 me bar() fn bar(&self)
94 [ 86 fd foo u32
95 CompletionItem { 87 "#]],
96 label: "the_field",
97 source_range: 45..45,
98 delete: 45..45,
99 insert: "the_field",
100 kind: Field,
101 detail: "u32",
102 },
103 ]
104 "###
105 ); 88 );
106 } 89 }
107 90
108 #[test] 91 #[test]
109 fn test_struct_field_completion_self() { 92 fn test_struct_field_completion_self() {
110 assert_debug_snapshot!( 93 check(
111 do_ref_completion( 94 r#"
112 r" 95struct S { the_field: (u32,) }
113 struct A { 96impl S {
114 /// This is the_field 97 fn foo(self) { self.<|> }
115 the_field: (u32,) 98}
116 } 99"#,
117 impl A { 100 expect![[r#"
118 fn foo(self) { 101 me foo() fn foo(self)
119 self.<|> 102 fd the_field (u32,)
120 } 103 "#]],
121 } 104 )
122 ",
123 ),
124 @r###"
125 [
126 CompletionItem {
127 label: "foo()",
128 source_range: 102..102,
129 delete: 102..102,
130 insert: "foo()$0",
131 kind: Method,
132 lookup: "foo",
133 detail: "fn foo(self)",
134 },
135 CompletionItem {
136 label: "the_field",
137 source_range: 102..102,
138 delete: 102..102,
139 insert: "the_field",
140 kind: Field,
141 detail: "(u32,)",
142 documentation: Documentation(
143 "This is the_field",
144 ),
145 },
146 ]
147 "###
148 );
149 } 105 }
150 106
151 #[test] 107 #[test]
152 fn test_struct_field_completion_autoderef() { 108 fn test_struct_field_completion_autoderef() {
153 assert_debug_snapshot!( 109 check(
154 do_ref_completion( 110 r#"
155 r" 111struct A { the_field: (u32, i32) }
156 struct A { the_field: (u32, i32) } 112impl A {
157 impl A { 113 fn foo(&self) { self.<|> }
158 fn foo(&self) { 114}
159 self.<|> 115"#,
160 } 116 expect![[r#"
161 } 117 me foo() fn foo(&self)
162 ", 118 fd the_field (u32, i32)
163 ), 119 "#]],
164 @r###" 120 )
165 [
166 CompletionItem {
167 label: "foo()",
168 source_range: 77..77,
169 delete: 77..77,
170 insert: "foo()$0",
171 kind: Method,
172 lookup: "foo",
173 detail: "fn foo(&self)",
174 },
175 CompletionItem {
176 label: "the_field",
177 source_range: 77..77,
178 delete: 77..77,
179 insert: "the_field",
180 kind: Field,
181 detail: "(u32, i32)",
182 },
183 ]
184 "###
185 );
186 } 121 }
187 122
188 #[test] 123 #[test]
189 fn test_no_struct_field_completion_for_method_call() { 124 fn test_no_struct_field_completion_for_method_call() {
190 assert_debug_snapshot!( 125 mark::check!(test_no_struct_field_completion_for_method_call);
191 do_ref_completion( 126 check(
192 r" 127 r#"
193 struct A { the_field: u32 } 128struct A { the_field: u32 }
194 fn foo(a: A) { 129fn foo(a: A) { a.<|>() }
195 a.<|>() 130"#,
196 } 131 expect![[""]],
197 ",
198 ),
199 @"[]"
200 ); 132 );
201 } 133 }
202 134
203 #[test] 135 #[test]
204 fn test_struct_field_visibility_private() { 136 fn test_visibility_filtering() {
205 assert_debug_snapshot!( 137 check(
206 do_ref_completion( 138 r#"
207 r" 139mod inner {
208 mod inner { 140 pub struct A {
209 struct A { 141 private_field: u32,
210 private_field: u32, 142 pub pub_field: u32,
211 pub pub_field: u32, 143 pub(crate) crate_field: u32,
212 pub(crate) crate_field: u32, 144 pub(super) super_field: u32,
213 pub(super) super_field: u32,
214 }
215 }
216 fn foo(a: inner::A) {
217 a.<|>
218 }
219 ",
220 ),
221 @r###"
222 [
223 CompletionItem {
224 label: "crate_field",
225 source_range: 192..192,
226 delete: 192..192,
227 insert: "crate_field",
228 kind: Field,
229 detail: "u32",
230 },
231 CompletionItem {
232 label: "pub_field",
233 source_range: 192..192,
234 delete: 192..192,
235 insert: "pub_field",
236 kind: Field,
237 detail: "u32",
238 },
239 CompletionItem {
240 label: "super_field",
241 source_range: 192..192,
242 delete: 192..192,
243 insert: "super_field",
244 kind: Field,
245 detail: "u32",
246 },
247 ]
248 "###
249 );
250 } 145 }
251 146}
252 #[test] 147fn foo(a: inner::A) { a.<|> }
253 fn test_union_field_completion() { 148"#,
254 assert_debug_snapshot!( 149 expect![[r#"
255 do_ref_completion( 150 fd crate_field u32
256 r" 151 fd pub_field u32
257 union Un { 152 fd super_field u32
258 field: u8, 153 "#]],
259 other: u16,
260 }
261
262 fn foo(u: Un) {
263 u.<|>
264 }
265 ",
266 ),
267 @r###"
268 [
269 CompletionItem {
270 label: "field",
271 source_range: 67..67,
272 delete: 67..67,
273 insert: "field",
274 kind: Field,
275 detail: "u8",
276 },
277 CompletionItem {
278 label: "other",
279 source_range: 67..67,
280 delete: 67..67,
281 insert: "other",
282 kind: Field,
283 detail: "u16",
284 },
285 ]
286 "###
287 ); 154 );
288 }
289 155
290 #[test] 156 check(
291 fn test_method_completion() { 157 r#"
292 assert_debug_snapshot!( 158struct A {}
293 do_ref_completion( 159mod m {
294 r" 160 impl super::A {
295 struct A {} 161 fn private_method(&self) {}
296 impl A { 162 pub(super) fn the_method(&self) {}
297 fn the_method(&self) {} 163 }
298 } 164}
299 fn foo(a: A) { 165fn foo(a: A) { a.<|> }
300 a.<|> 166"#,
301 } 167 expect![[r#"
302 ", 168 me the_method() pub(super) fn the_method(&self)
303 ), 169 "#]],
304 @r###"
305 [
306 CompletionItem {
307 label: "the_method()",
308 source_range: 71..71,
309 delete: 71..71,
310 insert: "the_method()$0",
311 kind: Method,
312 lookup: "the_method",
313 detail: "fn the_method(&self)",
314 },
315 ]
316 "###
317 ); 170 );
318 } 171 }
319 172
320 #[test] 173 #[test]
321 fn test_method_completion_only_fitting_impls() { 174 fn test_union_field_completion() {
322 assert_debug_snapshot!( 175 check(
323 do_ref_completion( 176 r#"
324 r" 177union U { field: u8, other: u16 }
325 struct A<T> {} 178fn foo(u: U) { u.<|> }
326 impl A<u32> { 179"#,
327 fn the_method(&self) {} 180 expect![[r#"
328 } 181 fd field u8
329 impl A<i32> { 182 fd other u16
330 fn the_other_method(&self) {} 183 "#]],
331 }
332 fn foo(a: A<u32>) {
333 a.<|>
334 }
335 ",
336 ),
337 @r###"
338 [
339 CompletionItem {
340 label: "the_method()",
341 source_range: 134..134,
342 delete: 134..134,
343 insert: "the_method()$0",
344 kind: Method,
345 lookup: "the_method",
346 detail: "fn the_method(&self)",
347 },
348 ]
349 "###
350 ); 184 );
351 } 185 }
352 186
353 #[test] 187 #[test]
354 fn test_method_completion_private() { 188 fn test_method_completion_only_fitting_impls() {
355 assert_debug_snapshot!( 189 check(
356 do_ref_completion( 190 r#"
357 r" 191struct A<T> {}
358 struct A {} 192impl A<u32> {
359 mod m { 193 fn the_method(&self) {}
360 impl super::A { 194}
361 fn private_method(&self) {} 195impl A<i32> {
362 pub(super) fn the_method(&self) {} 196 fn the_other_method(&self) {}
363 } 197}
364 } 198fn foo(a: A<u32>) { a.<|> }
365 fn foo(a: A) { 199"#,
366 a.<|> 200 expect![[r#"
367 } 201 me the_method() fn the_method(&self)
368 ", 202 "#]],
369 ), 203 )
370 @r###"
371 [
372 CompletionItem {
373 label: "the_method()",
374 source_range: 147..147,
375 delete: 147..147,
376 insert: "the_method()$0",
377 kind: Method,
378 lookup: "the_method",
379 detail: "pub(super) fn the_method(&self)",
380 },
381 ]
382 "###
383 );
384 } 204 }
385 205
386 #[test] 206 #[test]
387 fn test_trait_method_completion() { 207 fn test_trait_method_completion() {
388 assert_debug_snapshot!( 208 check(
389 do_ref_completion( 209 r#"
390 r" 210struct A {}
391 struct A {} 211trait Trait { fn the_method(&self); }
392 trait Trait { fn the_method(&self); } 212impl Trait for A {}
393 impl Trait for A {} 213fn foo(a: A) { a.<|> }
394 fn foo(a: A) { 214"#,
395 a.<|> 215 expect![[r#"
396 } 216 me the_method() fn the_method(&self)
397 ", 217 "#]],
398 ),
399 @r###"
400 [
401 CompletionItem {
402 label: "the_method()",
403 source_range: 90..90,
404 delete: 90..90,
405 insert: "the_method()$0",
406 kind: Method,
407 lookup: "the_method",
408 detail: "fn the_method(&self)",
409 },
410 ]
411 "###
412 ); 218 );
413 } 219 }
414 220
415 #[test] 221 #[test]
416 fn test_trait_method_completion_deduplicated() { 222 fn test_trait_method_completion_deduplicated() {
417 assert_debug_snapshot!( 223 check(
418 do_ref_completion( 224 r"
419 r" 225struct A {}
420 struct A {} 226trait Trait { fn the_method(&self); }
421 trait Trait { fn the_method(&self); } 227impl<T> Trait for T {}
422 impl<T> Trait for T {} 228fn foo(a: &A) { a.<|> }
423 fn foo(a: &A) { 229",
424 a.<|> 230 expect![[r#"
425 } 231 me the_method() fn the_method(&self)
426 ", 232 "#]],
427 ),
428 @r###"
429 [
430 CompletionItem {
431 label: "the_method()",
432 source_range: 94..94,
433 delete: 94..94,
434 insert: "the_method()$0",
435 kind: Method,
436 lookup: "the_method",
437 detail: "fn the_method(&self)",
438 },
439 ]
440 "###
441 ); 233 );
442 } 234 }
443 235
444 #[test] 236 #[test]
445 fn completes_trait_method_from_other_module() { 237 fn completes_trait_method_from_other_module() {
446 assert_debug_snapshot!( 238 check(
447 do_ref_completion(
448 r"
449 struct A {}
450 mod m {
451 pub trait Trait { fn the_method(&self); }
452 }
453 use m::Trait;
454 impl Trait for A {}
455 fn foo(a: A) {
456 a.<|>
457 }
458 ",
459 ),
460 @r###"
461 [
462 CompletionItem {
463 label: "the_method()",
464 source_range: 122..122,
465 delete: 122..122,
466 insert: "the_method()$0",
467 kind: Method,
468 lookup: "the_method",
469 detail: "fn the_method(&self)",
470 },
471 ]
472 "###
473 );
474 }
475
476 #[test]
477 fn test_no_non_self_method() {
478 assert_debug_snapshot!(
479 do_ref_completion(
480 r" 239 r"
481 struct A {} 240struct A {}
482 impl A { 241mod m {
483 fn the_method() {} 242 pub trait Trait { fn the_method(&self); }
484 } 243}
485 fn foo(a: A) { 244use m::Trait;
486 a.<|> 245impl Trait for A {}
487 } 246fn foo(a: A) { a.<|> }
488 ", 247",
489 ), 248 expect![[r#"
490 @"[]" 249 me the_method() fn the_method(&self)
250 "#]],
491 ); 251 );
492 } 252 }
493 253
494 #[test] 254 #[test]
495 fn test_method_attr_filtering() { 255 fn test_no_non_self_method() {
496 assert_debug_snapshot!( 256 check(
497 do_ref_completion( 257 r#"
498 r" 258struct A {}
499 struct A {} 259impl A {
500 impl A { 260 fn the_method() {}
501 #[inline] 261}
502 fn the_method(&self) { 262fn foo(a: A) {
503 let x = 1; 263 a.<|>
504 let y = 2; 264}
505 } 265"#,
506 } 266 expect![[""]],
507 fn foo(a: A) {
508 a.<|>
509 }
510 ",
511 ),
512 @r###"
513 [
514 CompletionItem {
515 label: "the_method()",
516 source_range: 128..128,
517 delete: 128..128,
518 insert: "the_method()$0",
519 kind: Method,
520 lookup: "the_method",
521 detail: "fn the_method(&self)",
522 },
523 ]
524 "###
525 ); 267 );
526 } 268 }
527 269
528 #[test] 270 #[test]
529 fn test_tuple_field_completion() { 271 fn test_tuple_field_completion() {
530 assert_debug_snapshot!( 272 check(
531 do_ref_completion( 273 r#"
532 r" 274fn foo() {
533 fn foo() { 275 let b = (0, 3.14);
534 let b = (0, 3.14); 276 b.<|>
535 b.<|> 277}
536 } 278"#,
537 ", 279 expect![[r#"
538 ), 280 fd 0 i32
539 @r###" 281 fd 1 f64
540 [ 282 "#]],
541 CompletionItem { 283 )
542 label: "0",
543 source_range: 38..38,
544 delete: 38..38,
545 insert: "0",
546 kind: Field,
547 detail: "i32",
548 },
549 CompletionItem {
550 label: "1",
551 source_range: 38..38,
552 delete: 38..38,
553 insert: "1",
554 kind: Field,
555 detail: "f64",
556 },
557 ]
558 "###
559 );
560 } 284 }
561 285
562 #[test] 286 #[test]
563 fn test_tuple_field_inference() { 287 fn test_tuple_field_inference() {
564 assert_debug_snapshot!( 288 check(
565 do_ref_completion( 289 r#"
566 r" 290pub struct S;
567 pub struct S; 291impl S { pub fn blah(&self) {} }
568 impl S {
569 pub fn blah(&self) {}
570 }
571 292
572 struct T(S); 293struct T(S);
573 294
574 impl T { 295impl T {
575 fn foo(&self) { 296 fn foo(&self) {
576 // FIXME: This doesn't work without the trailing `a` as `0.` is a float 297 // FIXME: This doesn't work without the trailing `a` as `0.` is a float
577 self.0.a<|> 298 self.0.a<|>
578 }
579 }
580 ",
581 ),
582 @r###"
583 [
584 CompletionItem {
585 label: "blah()",
586 source_range: 190..191,
587 delete: 190..191,
588 insert: "blah()$0",
589 kind: Method,
590 lookup: "blah",
591 detail: "pub fn blah(&self)",
592 },
593 ]
594 "###
595 );
596 } 299 }
597 300}
598 #[test] 301"#,
599 fn test_completion_works_in_consts() { 302 expect![[r#"
600 assert_debug_snapshot!( 303 me blah() pub fn blah(&self)
601 do_ref_completion( 304 "#]],
602 r"
603 struct A { the_field: u32 }
604 const X: u32 = {
605 A { the_field: 92 }.<|>
606 };
607 ",
608 ),
609 @r###"
610 [
611 CompletionItem {
612 label: "the_field",
613 source_range: 69..69,
614 delete: 69..69,
615 insert: "the_field",
616 kind: Field,
617 detail: "u32",
618 },
619 ]
620 "###
621 ); 305 );
622 } 306 }
623 307
624 #[test] 308 #[test]
625 fn test_completion_await_impls_future() { 309 fn test_completion_works_in_consts() {
626 assert_debug_snapshot!( 310 check(
627 do_completion( 311 r#"
628 r###" 312struct A { the_field: u32 }
629 //- /main.rs 313const X: u32 = {
630 use std::future::*; 314 A { the_field: 92 }.<|>
631 struct A {} 315};
632 impl Future for A {} 316"#,
633 fn foo(a: A) { 317 expect![[r#"
634 a.<|> 318 fd the_field u32
635 } 319 "#]],
636
637 //- /std/lib.rs
638 pub mod future {
639 #[lang = "future_trait"]
640 pub trait Future {}
641 }
642 "###, CompletionKind::Keyword),
643 @r###"
644 [
645 CompletionItem {
646 label: "await",
647 source_range: 74..74,
648 delete: 74..74,
649 insert: "await",
650 detail: "expr.await",
651 },
652 ]
653 "###
654 )
655 }
656
657 #[test]
658 fn test_super_super_completion() {
659 assert_debug_snapshot!(
660 do_ref_completion(
661 r"
662 mod a {
663 const A: usize = 0;
664
665 mod b {
666 const B: usize = 0;
667
668 mod c {
669 use super::super::<|>
670 }
671 }
672 }
673 ",
674 ),
675 @r###"
676 [
677 CompletionItem {
678 label: "A",
679 source_range: 120..120,
680 delete: 120..120,
681 insert: "A",
682 kind: Const,
683 },
684 CompletionItem {
685 label: "b",
686 source_range: 120..120,
687 delete: 120..120,
688 insert: "b",
689 kind: Module,
690 },
691 ]
692 "###
693 ); 320 );
694 } 321 }
695 322
696 #[test] 323 #[test]
697 fn works_in_simple_macro_1() { 324 fn works_in_simple_macro_1() {
698 assert_debug_snapshot!( 325 check(
699 do_ref_completion( 326 r#"
700 r" 327macro_rules! m { ($e:expr) => { $e } }
701 macro_rules! m { ($e:expr) => { $e } } 328struct A { the_field: u32 }
702 struct A { the_field: u32 } 329fn foo(a: A) {
703 fn foo(a: A) { 330 m!(a.x<|>)
704 m!(a.x<|>) 331}
705 } 332"#,
706 ", 333 expect![[r#"
707 ), 334 fd the_field u32
708 @r###" 335 "#]],
709 [
710 CompletionItem {
711 label: "the_field",
712 source_range: 91..92,
713 delete: 91..92,
714 insert: "the_field",
715 kind: Field,
716 detail: "u32",
717 },
718 ]
719 "###
720 );
721 }
722
723 #[test]
724 fn works_in_simple_macro_recursive() {
725 assert_debug_snapshot!(
726 do_ref_completion(
727 r"
728 macro_rules! m { ($e:expr) => { $e } }
729 struct A { the_field: u32 }
730 fn foo(a: A) {
731 m!(a.x<|>)
732 }
733 ",
734 ),
735 @r###"
736 [
737 CompletionItem {
738 label: "the_field",
739 source_range: 91..92,
740 delete: 91..92,
741 insert: "the_field",
742 kind: Field,
743 detail: "u32",
744 },
745 ]
746 "###
747 ); 336 );
748 } 337 }
749 338
750 #[test] 339 #[test]
751 fn works_in_simple_macro_2() { 340 fn works_in_simple_macro_2() {
752 // this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery 341 // this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery
753 assert_debug_snapshot!( 342 check(
754 do_ref_completion( 343 r#"
755 r" 344macro_rules! m { ($e:expr) => { $e } }
756 macro_rules! m { ($e:expr) => { $e } } 345struct A { the_field: u32 }
757 struct A { the_field: u32 } 346fn foo(a: A) {