aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src')
-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_injection.html47
-rw-r--r--crates/ra_ide/src/snapshots/highlight_strings.html95
-rw-r--r--crates/ra_ide/src/snapshots/highlight_unsafe.html53
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html117
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html48
-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
57 files changed, 9393 insertions, 13716 deletions
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) {
758 fn foo(a: A) { 347 m!(a.<|>)
759 m!(a.<|>) 348}
760 } 349"#,
761 ", 350 expect![[r#"
762 ), 351 fd the_field u32
763 @r###" 352 "#]],
764 [
765 CompletionItem {
766 label: "the_field",
767 source_range: 91..91,
768 delete: 91..91,
769 insert: "the_field",
770 kind: Field,
771 detail: "u32",
772 },
773 ]
774 "###
775 ); 353 );
776 } 354 }
777 355
778 #[test] 356 #[test]
779 fn works_in_simple_macro_recursive_1() { 357 fn works_in_simple_macro_recursive_1() {
780 assert_debug_snapshot!( 358 check(
781 do_ref_completion( 359 r#"
782 r" 360macro_rules! m { ($e:expr) => { $e } }
783 macro_rules! m { ($e:expr) => { $e } } 361struct A { the_field: u32 }
784 struct A { the_field: u32 } 362fn foo(a: A) {
785 fn foo(a: A) { 363 m!(m!(m!(a.x<|>)))
786 m!(m!(m!(a.x<|>))) 364}
787 } 365"#,
788 ", 366 expect![[r#"
789 ), 367 fd the_field u32
790 @r###" 368 "#]],
791 [
792 CompletionItem {
793 label: "the_field",
794 source_range: 97..98,
795 delete: 97..98,
796 insert: "the_field",
797 kind: Field,
798 detail: "u32",
799 },
800 ]
801 "###
802 ); 369 );
803 } 370 }
804 371
805 #[test] 372 #[test]
806 fn macro_expansion_resilient() { 373 fn macro_expansion_resilient() {
807 assert_debug_snapshot!( 374 check(
808 do_ref_completion( 375 r#"
809 r" 376macro_rules! dbg {
810 macro_rules! dbg { 377 () => {};
811 () => {}; 378 ($val:expr) => {
812 ($val:expr) => { 379 match $val { tmp => { tmp } }
813 match $val { tmp => { tmp } } 380 };
814 }; 381 // Trailing comma with single argument is ignored
815 // Trailing comma with single argument is ignored 382 ($val:expr,) => { $crate::dbg!($val) };
816 ($val:expr,) => { $crate::dbg!($val) }; 383 ($($val:expr),+ $(,)?) => {
817 ($($val:expr),+ $(,)?) => { 384 ($($crate::dbg!($val)),+,)
818 ($($crate::dbg!($val)),+,) 385 };
819 }; 386}
820 } 387struct A { the_field: u32 }
821 struct A { the_field: u32 } 388fn foo(a: A) {
822 fn foo(a: A) { 389 dbg!(a.<|>)
823 dbg!(a.<|>) 390}
824 } 391"#,
825 ", 392 expect![[r#"
826 ), 393 fd the_field u32
827 @r###" 394 "#]],
828 [
829 CompletionItem {
830 label: "the_field",
831 source_range: 327..327,
832 delete: 327..327,
833 insert: "the_field",
834 kind: Field,
835 detail: "u32",
836 },
837 ]
838 "###
839 ); 395 );
840 } 396 }
841 397
842 #[test] 398 #[test]
843 fn test_method_completion_3547() { 399 fn test_method_completion_issue_3547() {
844 assert_debug_snapshot!( 400 check(
845 do_ref_completion( 401 r#"
846 r" 402struct HashSet<T> {}
847 struct HashSet<T> {} 403impl<T> HashSet<T> {
848 impl<T> HashSet<T> { 404 pub fn the_method(&self) {}
849 pub fn the_method(&self) {} 405}
850 } 406fn foo() {
851 fn foo() { 407 let s: HashSet<_>;
852 let s: HashSet<_>; 408 s.<|>
853 s.<|> 409}
854 } 410"#,
855 ", 411 expect![[r#"
856 ), 412 me the_method() pub fn the_method(&self)
857 @r###" 413 "#]],
858 [
859 CompletionItem {
860 label: "the_method()",
861 source_range: 116..116,
862 delete: 116..116,
863 insert: "the_method()$0",
864 kind: Method,
865 lookup: "the_method",
866 detail: "pub fn the_method(&self)",
867 },
868 ]
869 "###
870 ); 414 );
871 } 415 }
872} 416}
diff --git a/crates/ra_ide/src/completion/complete_fn_param.rs b/crates/ra_ide/src/completion/complete_fn_param.rs
index f5573ddf7..406334257 100644
--- a/crates/ra_ide/src/completion/complete_fn_param.rs
+++ b/crates/ra_ide/src/completion/complete_fn_param.rs
@@ -1,4 +1,4 @@
1//! FIXME: write short doc here 1//! See `complete_fn_param`.
2 2
3use ra_syntax::{ 3use ra_syntax::{
4 ast::{self, ModuleItemOwner}, 4 ast::{self, ModuleItemOwner},
@@ -18,35 +18,47 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
18 } 18 }
19 19
20 let mut params = FxHashMap::default(); 20 let mut params = FxHashMap::default();
21
22 let me = ctx.token.ancestors().find_map(ast::Fn::cast);
23 let mut process_fn = |func: ast::Fn| {
24 if Some(&func) == me.as_ref() {
25 return;
26 }
27 func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
28 let text = param.syntax().text().to_string();
29 params.entry(text).or_insert(param);
30 })
31 };
32
21 for node in ctx.token.parent().ancestors() { 33 for node in ctx.token.parent().ancestors() {
22 let items = match_ast! { 34 match_ast! {
23 match node { 35 match node {
24 ast::SourceFile(it) => it.items(), 36 ast::SourceFile(it) => it.items().filter_map(|item| match item {
25 ast::ItemList(it) => it.items(), 37 ast::Item::Fn(it) => Some(it),
38 _ => None,
39 }).for_each(&mut process_fn),
40 ast::ItemList(it) => it.items().filter_map(|item| match item {
41 ast::Item::Fn(it) => Some(it),
42 _ => None,
43 }).for_each(&mut process_fn),
44 ast::AssocItemList(it) => it.assoc_items().filter_map(|item| match item {
45 ast::AssocItem::Fn(it) => Some(it),
46 _ => None,
47 }).for_each(&mut process_fn),
26 _ => continue, 48 _ => continue,
27 } 49 }
28 }; 50 };
29 for item in items {
30 if let ast::ModuleItem::FnDef(func) = item {
31 func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
32 let text = param.syntax().text().to_string();
33 params.entry(text).or_insert((0, param)).0 += 1;
34 })
35 }
36 }
37 } 51 }
52
38 params 53 params
39 .into_iter() 54 .into_iter()
40 .filter_map(|(label, (count, param))| { 55 .filter_map(|(label, param)| {
41 let lookup = param.pat()?.syntax().text().to_string(); 56 let lookup = param.pat()?.syntax().text().to_string();
42 if count < 2 { 57 Some((label, lookup))
43 None
44 } else {
45 Some((label, lookup))
46 }
47 }) 58 })
48 .for_each(|(label, lookup)| { 59 .for_each(|(label, lookup)| {
49 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label) 60 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
61 .kind(crate::CompletionItemKind::Binding)
50 .lookup_by(lookup) 62 .lookup_by(lookup)
51 .add_to(acc) 63 .add_to(acc)
52 }); 64 });
@@ -54,85 +66,70 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
54 66
55#[cfg(test)] 67#[cfg(test)]
56mod tests { 68mod tests {
57 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 69 use expect::{expect, Expect};
58 use insta::assert_debug_snapshot; 70
71 use crate::completion::{test_utils::completion_list, CompletionKind};
59 72
60 fn do_magic_completion(code: &str) -> Vec<CompletionItem> { 73 fn check(ra_fixture: &str, expect: Expect) {
61 do_completion(code, CompletionKind::Magic) 74 let actual = completion_list(ra_fixture, CompletionKind::Magic);
75 expect.assert_eq(&actual);
62 } 76 }
63 77
64 #[test] 78 #[test]
65 fn test_param_completion_last_param() { 79 fn test_param_completion_last_param() {
66 assert_debug_snapshot!( 80 check(
67 do_magic_completion( 81 r#"
68 r" 82fn foo(file_id: FileId) {}
69 fn foo(file_id: FileId) {} 83fn bar(file_id: FileId) {}
70 fn bar(file_id: FileId) {} 84fn baz(file<|>) {}
71 fn baz(file<|>) {} 85"#,
72 ", 86 expect![[r#"
73 ), 87 bn file_id: FileId
74 @r###" 88 "#]],
75 [
76 CompletionItem {
77 label: "file_id: FileId",
78 source_range: 61..65,
79 delete: 61..65,
80 insert: "file_id: FileId",
81 lookup: "file_id",
82 },
83 ]
84 "###
85 ); 89 );
86 } 90 }
87 91
88 #[test] 92 #[test]
89 fn test_param_completion_nth_param() { 93 fn test_param_completion_nth_param() {
90 assert_debug_snapshot!( 94 check(
91 do_magic_completion( 95 r#"
92 r" 96fn foo(file_id: FileId) {}
93 fn foo(file_id: FileId) {} 97fn baz(file<|>, x: i32) {}
94 fn bar(file_id: FileId) {} 98"#,
95 fn baz(file<|>, x: i32) {} 99 expect![[r#"
96 ", 100 bn file_id: FileId
97 ), 101 "#]],
98 @r###"
99 [
100 CompletionItem {
101 label: "file_id: FileId",
102 source_range: 61..65,
103 delete: 61..65,
104 insert: "file_id: FileId",
105 lookup: "file_id",
106 },
107 ]
108 "###
109 ); 102 );
110 } 103 }
111 104
112 #[test] 105 #[test]
113 fn test_param_completion_trait_param() { 106 fn test_param_completion_trait_param() {
114 assert_debug_snapshot!( 107 check(
115 do_magic_completion( 108 r#"
116 r" 109pub(crate) trait SourceRoot {
117 pub(crate) trait SourceRoot { 110 pub fn contains(&self, file_id: FileId) -> bool;
118 pub fn contains(&self, file_id: FileId) -> bool; 111 pub fn module_map(&self) -> &ModuleMap;
119 pub fn module_map(&self) -> &ModuleMap; 112 pub fn lines(&self, file_id: FileId) -> &LineIndex;
120 pub fn lines(&self, file_id: FileId) -> &LineIndex; 113 pub fn syntax(&self, file<|>)
121 pub fn syntax(&self, file<|>) 114}
122 } 115"#,
123 ", 116 expect![[r#"
124 ), 117 bn file_id: FileId
125 @r###" 118 "#]],
126 [
127 CompletionItem {
128 label: "file_id: FileId",
129 source_range: 208..212,
130 delete: 208..212,
131 insert: "file_id: FileId",
132 lookup: "file_id",
133 },
134 ]
135 "###
136 ); 119 );
137 } 120 }
121
122 #[test]
123 fn completes_param_in_inner_function() {
124 check(
125 r#"
126fn outer(text: String) {
127 fn inner(<|>)
128}
129"#,
130 expect![[r#"
131 bn text: String
132 "#]],
133 )
134 }
138} 135}
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs
index 3b174f916..b62064797 100644
--- a/crates/ra_ide/src/completion/complete_keyword.rs
+++ b/crates/ra_ide/src/completion/complete_keyword.rs
@@ -1,6 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_syntax::ast; 3use ra_syntax::{ast, SyntaxKind};
4use test_utils::mark;
4 5
5use crate::completion::{ 6use crate::completion::{
6 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, 7 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
@@ -34,9 +35,27 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
34 } 35 }
35 _ => {} 36 _ => {}
36 } 37 }
38
39 // Suggest .await syntax for types that implement Future trait
40 if let Some(receiver) = &ctx.dot_receiver {
41 if let Some(ty) = ctx.sema.type_of_expr(receiver) {
42 if ty.impls_future(ctx.db) {
43 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
44 .kind(CompletionItemKind::Keyword)
45 .detail("expr.await")
46 .insert_text("await")
47 .add_to(acc);
48 }
49 };
50 }
37} 51}
38 52
39pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { 53pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
54 if ctx.token.kind() == SyntaxKind::COMMENT {
55 mark::hit!(no_keyword_completion_in_comments);
56 return;
57 }
58
40 let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent; 59 let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
41 if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling { 60 if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
42 add_keyword(ctx, acc, "where", "where "); 61 add_keyword(ctx, acc, "where", "where ");
@@ -47,73 +66,67 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
47 add_keyword(ctx, acc, "fn", "fn $0() {}") 66 add_keyword(ctx, acc, "fn", "fn $0() {}")
48 } 67 }
49 68
50 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) 69 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
51 || ctx.block_expr_parent
52 {
53 add_keyword(ctx, acc, "trait", "trait $0 {}"); 70 add_keyword(ctx, acc, "trait", "trait $0 {}");
54 add_keyword(ctx, acc, "impl", "impl $0 {}"); 71 add_keyword(ctx, acc, "impl", "impl $0 {}");
55 } 72 }
56 73
57 return; 74 return;
58 } 75 }
59 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent { 76 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
77 {
60 add_keyword(ctx, acc, "fn", "fn $0() {}"); 78 add_keyword(ctx, acc, "fn", "fn $0() {}");
61 } 79 }
62 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) 80 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
63 || ctx.block_expr_parent
64 {
65 add_keyword(ctx, acc, "use", "use "); 81 add_keyword(ctx, acc, "use", "use ");
66 add_keyword(ctx, acc, "impl", "impl $0 {}"); 82 add_keyword(ctx, acc, "impl", "impl $0 {}");
67 add_keyword(ctx, acc, "trait", "trait $0 {}"); 83 add_keyword(ctx, acc, "trait", "trait $0 {}");
68 } 84 }
69 85
70 if ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent { 86 if ctx.has_item_list_or_source_file_parent {
71 add_keyword(ctx, acc, "enum", "enum $0 {}"); 87 add_keyword(ctx, acc, "enum", "enum $0 {}");
72 add_keyword(ctx, acc, "struct", "struct $0 {}"); 88 add_keyword(ctx, acc, "struct", "struct $0");
73 add_keyword(ctx, acc, "union", "union $0 {}"); 89 add_keyword(ctx, acc, "union", "union $0 {}");
74 } 90 }
75 91
76 if ctx.block_expr_parent || ctx.is_match_arm { 92 if ctx.is_expr {
77 add_keyword(ctx, acc, "match", "match $0 {}"); 93 add_keyword(ctx, acc, "match", "match $0 {}");
78 add_keyword(ctx, acc, "loop", "loop {$0}");
79 }
80 if ctx.block_expr_parent {
81 add_keyword(ctx, acc, "while", "while $0 {}"); 94 add_keyword(ctx, acc, "while", "while $0 {}");
95 add_keyword(ctx, acc, "loop", "loop {$0}");
96 add_keyword(ctx, acc, "if", "if ");
97 add_keyword(ctx, acc, "if let", "if let ");
82 } 98 }
99
83 if ctx.if_is_prev || ctx.block_expr_parent { 100 if ctx.if_is_prev || ctx.block_expr_parent {
84 add_keyword(ctx, acc, "let", "let "); 101 add_keyword(ctx, acc, "let", "let ");
85 } 102 }
86 if ctx.if_is_prev || ctx.block_expr_parent || ctx.is_match_arm { 103
87 add_keyword(ctx, acc, "if", "if ");
88 add_keyword(ctx, acc, "if let", "if let ");
89 }
90 if ctx.after_if { 104 if ctx.after_if {
91 add_keyword(ctx, acc, "else", "else {$0}"); 105 add_keyword(ctx, acc, "else", "else {$0}");
92 add_keyword(ctx, acc, "else if", "else if $0 {}"); 106 add_keyword(ctx, acc, "else if", "else if $0 {}");
93 } 107 }
94 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) 108 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
95 || ctx.block_expr_parent
96 {
97 add_keyword(ctx, acc, "mod", "mod $0 {}"); 109 add_keyword(ctx, acc, "mod", "mod $0 {}");
98 } 110 }
99 if ctx.bind_pat_parent || ctx.ref_pat_parent { 111 if ctx.bind_pat_parent || ctx.ref_pat_parent {
100 add_keyword(ctx, acc, "mut", "mut "); 112 add_keyword(ctx, acc, "mut", "mut ");
101 } 113 }
102 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent { 114 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
115 {
103 add_keyword(ctx, acc, "const", "const "); 116 add_keyword(ctx, acc, "const", "const ");
104 add_keyword(ctx, acc, "type", "type "); 117 add_keyword(ctx, acc, "type", "type ");
105 } 118 }
106 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) 119 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
107 || ctx.block_expr_parent
108 {
109 add_keyword(ctx, acc, "static", "static "); 120 add_keyword(ctx, acc, "static", "static ");
110 }; 121 };
111 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) 122 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
112 || ctx.block_expr_parent
113 {
114 add_keyword(ctx, acc, "extern", "extern "); 123 add_keyword(ctx, acc, "extern", "extern ");
115 } 124 }
116 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent || ctx.is_match_arm { 125 if ctx.has_item_list_or_source_file_parent
126 || has_trait_or_impl_parent
127 || ctx.block_expr_parent
128 || ctx.is_match_arm
129 {
117 add_keyword(ctx, acc, "unsafe", "unsafe "); 130 add_keyword(ctx, acc, "unsafe", "unsafe ");
118 } 131 }
119 if ctx.in_loop_body { 132 if ctx.in_loop_body {
@@ -125,7 +138,7 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
125 add_keyword(ctx, acc, "break", "break"); 138 add_keyword(ctx, acc, "break", "break");
126 } 139 }
127 } 140 }
128 if ctx.has_item_list_or_source_file_parent && !ctx.has_trait_parent { 141 if ctx.has_item_list_or_source_file_parent || ctx.has_impl_parent {
129 add_keyword(ctx, acc, "pub", "pub ") 142 add_keyword(ctx, acc, "pub", "pub ")
130 } 143 }
131 144
@@ -156,7 +169,7 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet
156 169
157fn complete_return( 170fn complete_return(
158 ctx: &CompletionContext, 171 ctx: &CompletionContext,
159 fn_def: &ast::FnDef, 172 fn_def: &ast::Fn,
160 can_be_stmt: bool, 173 can_be_stmt: bool,
161) -> Option<CompletionItem> { 174) -> Option<CompletionItem> {
162 let snip = match (can_be_stmt, fn_def.ret_type().is_some()) { 175 let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
@@ -170,289 +183,354 @@ fn complete_return(
170 183
171#[cfg(test)] 184#[cfg(test)]
172mod tests { 185mod tests {
173 use crate::completion::{test_utils::completion_list, CompletionKind}; 186 use expect::{expect, Expect};
174 use insta::assert_snapshot; 187
188 use crate::completion::{
189 test_utils::{check_edit, completion_list},
190 CompletionKind,
191 };
192 use test_utils::mark;
175 193
176 fn get_keyword_completions(code: &str) -> String { 194 fn check(ra_fixture: &str, expect: Expect) {
177 completion_list(code, CompletionKind::Keyword) 195 let actual = completion_list(ra_fixture, CompletionKind::Keyword);
196 expect.assert_eq(&actual)
178 } 197 }
179 198
180 #[test] 199 #[test]
181 fn test_keywords_in_use_stmt() { 200 fn test_keywords_in_use_stmt() {
182 assert_snapshot!( 201 check(
183 get_keyword_completions(r"use <|>"), 202 r"use <|>",
184 @r###" 203 expect![[r#"
185 kw crate:: 204 kw crate::
186 kw self 205 kw self
187 kw super:: 206 kw super::
188 "### 207 "#]],
189 ); 208 );
190 209
191 assert_snapshot!( 210 check(
192 get_keyword_completions(r"use a::<|>"), 211 r"use a::<|>",
193 @r###" 212 expect![[r#"
194 kw self 213 kw self
195 kw super:: 214 kw super::
196 "### 215 "#]],
197 ); 216 );
198 217
199 assert_snapshot!( 218 check(
200 get_keyword_completions(r"use a::{b, <|>}"), 219 r"use a::{b, <|>}",
201 @r###" 220 expect![[r#"
202 kw self 221 kw self
203 kw super:: 222 kw super::
204 "### 223 "#]],
205 ); 224 );
206 } 225 }
207 226
208 #[test] 227 #[test]
209 fn test_keywords_at_source_file_level() { 228 fn test_keywords_at_source_file_level() {
210 assert_snapshot!( 229 check(
211 get_keyword_completions(r"m<|>"), 230 r"m<|>",
212 @r###" 231 expect![[r#"
213 kw const 232 kw const
214 kw enum 233 kw enum
215 kw extern 234 kw extern
216 kw fn 235 kw fn
217 kw impl 236 kw impl
218 kw mod 237 kw mod
219 kw pub 238 kw pub
220 kw static 239 kw static
221 kw struct 240 kw struct
222 kw trait 241 kw trait
223 kw type 242 kw type
224 kw union 243 kw union
225 kw unsafe 244 kw unsafe
226 kw use 245 kw use
227 "### 246 "#]],
228 ); 247 );
229 } 248 }
230 249
231 #[test] 250 #[test]
232 fn test_keywords_in_function() { 251 fn test_keywords_in_function() {
233 assert_snapshot!( 252 check(
234 get_keyword_completions(r"fn quux() { <|> }"), 253 r"fn quux() { <|> }",
235 @r###" 254 expect![[r#"
236 kw const 255 kw const
237 kw extern 256 kw extern
238 kw fn 257 kw fn
239 kw if 258 kw if
240 kw if let 259 kw if let
241 kw impl 260 kw impl
242 kw let 261 kw let
243 kw loop 262 kw loop
244 kw match 263 kw match
245 kw mod 264 kw mod
246 kw return 265 kw return
247 kw static 266 kw static
248 kw trait 267 kw trait
249 kw type 268 kw type
250 kw unsafe 269 kw unsafe
251 kw use 270 kw use
252 kw while 271 kw while
253 "### 272 "#]],
254 ); 273 );
255 } 274 }
256 275
257 #[test] 276 #[test]
258 fn test_keywords_inside_block() { 277 fn test_keywords_inside_block() {
259 assert_snapshot!( 278 check(
260 get_keyword_completions(r"fn quux() { if true { <|> } }"), 279 r"fn quux() { if true { <|> } }",
261 @r###" 280 expect![[r#"
262 kw const 281 kw const
263 kw extern 282 kw extern
264 kw fn 283 kw fn
265 kw if 284 kw if
266 kw if let 285 kw if let
267 kw impl 286 kw impl
268 kw let 287 kw let
269 kw loop 288 kw loop
270 kw match 289 kw match
271 kw mod 290 kw mod
272 kw return 291 kw return
273 kw static 292 kw static
274 kw trait 293 kw trait
275 kw type 294 kw type
276 kw unsafe 295 kw unsafe
277 kw use 296 kw use
278 kw while 297 kw while
279 "### 298 "#]],
280 ); 299 );
281 } 300 }
282 301
283 #[test] 302 #[test]
284 fn test_keywords_after_if() { 303 fn test_keywords_after_if() {
285 assert_snapshot!( 304 check(
286 get_keyword_completions( 305 r#"fn quux() { if true { () } <|> }"#,
287 r" 306 expect![[r#"
288 fn quux() { 307 kw const
289 if true { 308 kw else
290 () 309 kw else if
291 } <|> 310 kw extern
292 } 311 kw fn
293 ", 312 kw if
294 ), 313 kw if let
295 @r###" 314 kw impl
296 kw const 315 kw let
297 kw else 316 kw loop
298 kw else if 317 kw match
299 kw extern 318 kw mod
300 kw fn 319 kw return
301 kw if 320 kw static
302 kw if let 321 kw trait
303 kw impl 322 kw type
304 kw let 323 kw unsafe
305 kw loop 324 kw use
306 kw match 325 kw while
307 kw mod 326 "#]],
308 kw return 327 );
309 kw static 328 check_edit(
310 kw trait 329 "else",
311 kw type 330 r#"fn quux() { if true { () } <|> }"#,
312 kw unsafe 331 r#"fn quux() { if true { () } else {$0} }"#,
313 kw use
314 kw while
315 "###
316 ); 332 );
317 } 333 }
318 334
319 #[test] 335 #[test]
320 fn test_keywords_in_match_arm() { 336 fn test_keywords_in_match_arm() {
321 assert_snapshot!( 337 check(
322 get_keyword_completions( 338 r#"
323 r" 339fn quux() -> i32 {
324 fn quux() -> i32 { 340 match () { () => <|> }
325 match () { 341}
326 () => <|> 342"#,
327 } 343 expect![[r#"
328 } 344 kw if
329 ", 345 kw if let
330 ), 346 kw loop
331 @r###" 347 kw match
332 kw if 348 kw return
333 kw if let 349 kw unsafe
334 kw loop 350 kw while
335 kw match 351 "#]],
336 kw return
337 kw unsafe
338 "###
339 ); 352 );
340 } 353 }
341 354
342 #[test] 355 #[test]
343 fn test_keywords_in_trait_def() { 356 fn test_keywords_in_trait_def() {
344 assert_snapshot!( 357 check(
345 get_keyword_completions(r"trait My { <|> }"), 358 r"trait My { <|> }",
346 @r###" 359 expect![[r#"
347 kw const 360 kw const
348 kw fn 361 kw fn
349 kw type 362 kw type
350 kw unsafe 363 kw unsafe
351 "### 364 "#]],
352 ); 365 );
353 } 366 }
354 367
355 #[test] 368 #[test]
356 fn test_keywords_in_impl_def() { 369 fn test_keywords_in_impl_def() {
357 assert_snapshot!( 370 check(
358 get_keyword_completions(r"impl My { <|> }"), 371 r"impl My { <|> }",
359 @r###" 372 expect![[r#"
360 kw const 373 kw const
361 kw fn 374 kw fn
362 kw pub 375 kw pub
363 kw type 376 kw type
364 kw unsafe 377 kw unsafe
365 "### 378 "#]],
366 ); 379 );
367 } 380 }
368 381
369 #[test] 382 #[test]
370 fn test_keywords_in_loop() { 383 fn test_keywords_in_loop() {
371 assert_snapshot!( 384 check(
372 get_keyword_completions(r"fn my() { loop { <|> } }"), 385 r"fn my() { loop { <|> } }",
373 @r###" 386 expect![[r#"
374 kw break 387 kw break
375 kw const 388 kw const
376 kw continue 389 kw continue
377 kw extern 390 kw extern
378 kw fn 391 kw fn
379 kw if 392 kw if
380 kw if let 393 kw if let
381 kw impl 394 kw impl
382 kw let 395 kw let
383 kw loop 396 kw loop
384 kw match 397 kw match
385 kw mod 398 kw mod
386 kw return 399 kw return
387 kw static 400 kw static
388 kw trait 401 kw trait
389 kw type 402 kw type
390 kw unsafe 403 kw unsafe
391 kw use 404 kw use
392 kw while 405 kw while
393 "### 406 "#]],
394 ); 407 );
395 } 408 }
396 409
397 #[test] 410 #[test]
398 fn test_keywords_after_unsafe_in_item_list() { 411 fn test_keywords_after_unsafe_in_item_list() {
399 assert_snapshot!( 412 check(
400 get_keyword_completions(r"unsafe <|>"), 413 r"unsafe <|>",
401 @r###" 414 expect![[r#"
402 kw fn 415 kw fn
403 kw impl 416 kw impl
404 kw trait 417 kw trait
405 "### 418 "#]],
406 ); 419 );
407 } 420 }
408 421
409 #[test] 422 #[test]
410 fn test_keywords_after_unsafe_in_block_expr() { 423 fn test_keywords_after_unsafe_in_block_expr() {
411 assert_snapshot!( 424 check(
412 get_keyword_completions(r"fn my_fn() { unsafe <|> }"), 425 r"fn my_fn() { unsafe <|> }",
413 @r###" 426 expect![[r#"
414 kw fn 427 kw fn
415 kw impl 428 kw impl
416 kw trait 429 kw trait
417 "### 430 "#]],
418 ); 431 );
419 } 432 }
420 433
421 #[test] 434 #[test]
422 fn test_mut_in_ref_and_in_fn_parameters_list() { 435 fn test_mut_in_ref_and_in_fn_parameters_list() {
423 assert_snapshot!( 436 check(
424 get_keyword_completions(r"fn my_fn(&<|>) {}"), 437 r"fn my_fn(&<|>) {}",
425 @r###" 438 expect![[r#"
426 kw mut 439 kw mut
427 "### 440 "#]],
428 ); 441 );
429 assert_snapshot!( 442 check(
430 get_keyword_completions(r"fn my_fn(<|>) {}"), 443 r"fn my_fn(<|>) {}",
431 @r###" 444 expect![[r#"
432 kw mut 445 kw mut
433 "### 446 "#]],
434 ); 447 );
435 assert_snapshot!( 448 check(
436 get_keyword_completions(r"fn my_fn() { let &<|> }"), 449 r"fn my_fn() { let &<|> }",
437 @r###" 450 expect![[r#"
438 kw mut 451 kw mut
439 "### 452 "#]],
440 ); 453 );
441 } 454 }
442 455
443 #[test] 456 #[test]
444 fn test_where_keyword() { 457 fn test_where_keyword() {
445 assert_snapshot!( 458 check(
446 get_keyword_completions(r"trait A <|>"), 459 r"trait A <|>",
447 @r###" 460 expect![[r#"
448 kw where 461 kw where
449 "### 462 "#]],
450 ); 463 );
451 assert_snapshot!( 464 check(
452 get_keyword_completions(r"impl A <|>"), 465 r"impl A <|>",
453 @r###" 466 expect![[r#"
454 kw where 467 kw where
455 "### 468 "#]],
456 ); 469 );
457 } 470 }
471
472 #[test]
473 fn no_keyword_completion_in_comments() {
474 mark::check!(no_keyword_completion_in_comments);
475 check(
476 r#"
477fn test() {
478 let x = 2; // A comment<|>
479}
480"#,
481 expect![[""]],
482 );
483 check(
484 r#"
485/*
486Some multi-line comment<|>
487*/
488"#,
489 expect![[""]],
490 );
491 check(
492 r#"
493/// Some doc comment
494/// let test<|> = 1
495"#,
496 expect![[""]],
497 );
498 }
499
500 #[test]
501 fn test_completion_await_impls_future() {
502 check(
503 r#"
504//- /main.rs
505use std::future::*;
506struct A {}
507impl Future for A {}
508fn foo(a: A) { a.<|> }
509
510//- /std/lib.rs
511pub mod future {
512 #[lang = "future_trait"]
513 pub trait Future {}
514}
515"#,
516 expect![[r#"
517 kw await expr.await
518 "#]],
519 )
520 }
521
522 #[test]
523 fn after_let() {
524 check(
525 r#"fn main() { let _ = <|> }"#,
526 expect![[r#"
527 kw if
528 kw if let
529 kw loop
530 kw match
531 kw return
532 kw while
533 "#]],
534 )
535 }
458} 536}
diff --git a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
index 4c33f41d4..0447f0511 100644
--- a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
+++ b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
@@ -5,7 +5,7 @@ use crate::completion::{CompletionContext, Completions};
5pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { 5pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
6 // Show only macros in top level. 6 // Show only macros in top level.
7 if ctx.is_new_item { 7 if ctx.is_new_item {
8 ctx.scope().process_all_names(&mut |name, res| { 8 ctx.scope.process_all_names(&mut |name, res| {
9 if let hir::ScopeDef::MacroDef(mac) = res { 9 if let hir::ScopeDef::MacroDef(mac) = res {
10 acc.add_macro(ctx, Some(name.to_string()), mac); 10 acc.add_macro(ctx, Some(name.to_string()), mac);
11 } 11 }
@@ -15,130 +15,27 @@ pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &Compl
15 15
16#[cfg(test)] 16#[cfg(test)]
17mod tests { 17mod tests {
18 use insta::assert_debug_snapshot; 18 use expect::{expect, Expect};
19 19
20 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 20 use crate::completion::{test_utils::completion_list, CompletionKind};
21 21
22 fn do_reference_completion(code: &str) -> Vec<CompletionItem> { 22 fn check(ra_fixture: &str, expect: Expect) {
23 do_completion(code, CompletionKind::Reference) 23 let actual = completion_list(ra_fixture, CompletionKind::Reference);
24 expect.assert_eq(&actual)
24 } 25 }
25 26
26 #[test] 27 #[test]
27 fn completes_macros_as_item() { 28 fn completes_macros_as_item() {
28 assert_debug_snapshot!( 29 check(
29 do_reference_completion( 30 r#"
30 " 31macro_rules! foo { () => {} }
31 //- /main.rs 32fn foo() {}
32 macro_rules! foo { 33
33 () => {} 34<|>
34 } 35"#,
35 36 expect![[r#"
36 fn foo() {} 37 ma foo!(…) macro_rules! foo
37 38 "#]],
38 <|> 39 )
39 "
40 ),
41 @r###"
42 [
43 CompletionItem {
44 label: "foo!(…)",
45 source_range: 48..48,
46 delete: 48..48,
47 insert: "foo!($0)",
48 kind: Macro,
49 detail: "macro_rules! foo",
50 },
51 ]
52 "###
53 );
54 }
55
56 #[test]
57 fn completes_vec_macros_with_square_brackets() {
58 assert_debug_snapshot!(
59 do_reference_completion(
60 "
61 //- /main.rs
62 /// Creates a [`Vec`] containing the arguments.
63 ///
64 /// - Create a [`Vec`] containing a given list of elements:
65 ///
66 /// ```
67 /// let v = vec![1, 2, 3];
68 /// assert_eq!(v[0], 1);
69 /// assert_eq!(v[1], 2);
70 /// assert_eq!(v[2], 3);
71 /// ```
72 macro_rules! vec {
73 () => {}
74 }
75
76 fn foo() {}
77
78 <|>
79 "
80 ),
81 @r###"
82 [
83 CompletionItem {
84 label: "vec![…]",
85 source_range: 282..282,
86 delete: 282..282,
87 insert: "vec![$0]",
88 kind: Macro,
89 detail: "macro_rules! vec",
90 documentation: Documentation(
91 "Creates a [`Vec`] containing the arguments.\n\n- Create a [`Vec`] containing a given list of elements:\n\n```\nlet v = vec![1, 2, 3];\nassert_eq!(v[0], 1);\nassert_eq!(v[1], 2);\nassert_eq!(v[2], 3);\n```",
92 ),
93 },
94 ]
95 "###
96 );
97 }
98
99 #[test]
100 fn completes_macros_braces_guessing() {
101 assert_debug_snapshot!(
102 do_reference_completion(
103 "
104 //- /main.rs
105 /// Foo
106 ///
107 /// Not call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`.
108 /// Call as `let _=foo! { hello world };`
109 macro_rules! foo {
110 () => {}
111 }
112
113 fn main() {
114 <|>
115 }
116 "
117 ),
118 @r###"
119 [
120 CompletionItem {
121 label: "foo! {…}",
122 source_range: 164..164,
123 delete: 164..164,
124 insert: "foo! {$0}",
125 kind: Macro,
126 detail: "macro_rules! foo",
127 documentation: Documentation(
128 "Foo\n\nNot call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`.\nCall as `let _=foo! { hello world };`",
129 ),
130 },
131 CompletionItem {
132 label: "main()",
133 source_range: 164..164,
134 delete: 164..164,
135 insert: "main()$0",
136 kind: Function,
137 lookup: "main",
138 detail: "fn main()",
139 },
140 ]
141 "###
142 );
143 } 40 }
144} 41}
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs
index 367e2bbce..aceb77cb5 100644
--- a/crates/ra_ide/src/completion/complete_pattern.rs
+++ b/crates/ra_ide/src/completion/complete_pattern.rs
@@ -13,7 +13,7 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
13 13
14 // FIXME: ideally, we should look at the type we are matching against and 14 // FIXME: ideally, we should look at the type we are matching against and
15 // suggest variants + auto-imports 15 // suggest variants + auto-imports
16 ctx.scope().process_all_names(&mut |name, res| { 16 ctx.scope.process_all_names(&mut |name, res| {
17 match &res { 17 match &res {
18 hir::ScopeDef::ModuleDef(def) => match def { 18 hir::ScopeDef::ModuleDef(def) => match def {
19 hir::ModuleDef::Adt(hir::Adt::Enum(..)) 19 hir::ModuleDef::Adt(hir::Adt::Enum(..))
@@ -33,106 +33,56 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
33 33
34#[cfg(test)] 34#[cfg(test)]
35mod tests { 35mod tests {
36 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 36 use expect::{expect, Expect};
37 use insta::assert_debug_snapshot;
38 37
39 fn complete(code: &str) -> Vec<CompletionItem> { 38 use crate::completion::{test_utils::completion_list, CompletionKind};
40 do_completion(code, CompletionKind::Reference) 39
40 fn check(ra_fixture: &str, expect: Expect) {
41 let actual = completion_list(ra_fixture, CompletionKind::Reference);
42 expect.assert_eq(&actual)
41 } 43 }
42 44
43 #[test] 45 #[test]
44 fn completes_enum_variants_and_modules() { 46 fn completes_enum_variants_and_modules() {
45 let completions = complete( 47 check(
46 r" 48 r#"
47 enum E { X } 49enum E { X }
48 use self::E::X; 50use self::E::X;
49 const Z: E = E::X; 51const Z: E = E::X;
50 mod m {} 52mod m {}
51 53
52 static FOO: E = E::X; 54static FOO: E = E::X;
53 struct Bar { f: u32 } 55struct Bar { f: u32 }
54 56
55 fn foo() { 57fn foo() {
56 match E::X { 58 match E::X { <|> }
57 <|> 59}
58 } 60"#,
59 } 61 expect![[r#"
60 ", 62 st Bar
63 en E
64 ev X ()
65 ct Z
66 md m
67 "#]],
61 ); 68 );
62 assert_debug_snapshot!(completions, @r###"
63 [
64 CompletionItem {
65 label: "Bar",
66 source_range: 137..137,
67 delete: 137..137,
68 insert: "Bar",
69 kind: Struct,
70 },
71 CompletionItem {
72 label: "E",
73 source_range: 137..137,
74 delete: 137..137,
75 insert: "E",
76 kind: Enum,
77 },
78 CompletionItem {
79 label: "X",
80 source_range: 137..137,
81 delete: 137..137,
82 insert: "X",
83 kind: EnumVariant,
84 detail: "()",
85 },
86 CompletionItem {
87 label: "Z",
88 source_range: 137..137,
89 delete: 137..137,
90 insert: "Z",
91 kind: Const,
92 },
93 CompletionItem {
94 label: "m",
95 source_range: 137..137,
96 delete: 137..137,
97 insert: "m",
98 kind: Module,
99 },
100 ]
101 "###);
102 } 69 }
103 70
104 #[test] 71 #[test]
105 fn completes_in_simple_macro_call() { 72 fn completes_in_simple_macro_call() {
106 let completions = complete( 73 check(
107 r" 74 r#"
108 macro_rules! m { ($e:expr) => { $e } } 75macro_rules! m { ($e:expr) => { $e } }
109 enum E { X } 76enum E { X }
110 77
111 fn foo() { 78fn foo() {
112 m!(match E::X { 79 m!(match E::X { <|> })
113 <|> 80}
114 }) 81"#,
115 } 82 expect![[r#"
116 ", 83 en E
84 ma m!(…) macro_rules! m
85 "#]],
117 ); 86 );
118 assert_debug_snapshot!(completions, @r###"
119 [
120 CompletionItem {
121 label: "E",
122 source_range: 90..90,
123 delete: 90..90,
124 insert: "E",
125 kind: Enum,
126 },
127 CompletionItem {
128 label: "m!(…)",
129 source_range: 90..90,
130 delete: 90..90,
131 insert: "m!($0)",
132 kind: Macro,
133 detail: "macro_rules! m",
134 },
135 ]
136 "###);
137 } 87 }
138} 88}
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 3bd64804f..8735b9010 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -8,14 +8,13 @@ use ra_text_edit::TextEdit;
8 8
9use crate::{ 9use crate::{
10 completion::{ 10 completion::{
11 completion_config::SnippetCap,
11 completion_context::CompletionContext, 12 completion_context::CompletionContext,
12 completion_item::{Builder, CompletionKind, Completions}, 13 completion_item::{Builder, CompletionKind, Completions},
13 }, 14 },
14 CompletionItem, 15 CompletionItem, CompletionItemKind,
15}; 16};
16 17
17use super::completion_config::SnippetCap;
18
19pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { 18pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
20 if !ctx.config.enable_postfix_completions { 19 if !ctx.config.enable_postfix_completions {
21 return; 20 return;
@@ -103,10 +102,9 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
103 &format!("while {} {{\n $0\n}}", receiver_text), 102 &format!("while {} {{\n $0\n}}", receiver_text),
104 ) 103 )
105 .add_to(acc); 104 .add_to(acc);
105 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
106 .add_to(acc);
106 } 107 }
107 // !&&&42 is a compiler error, ergo process it before considering the references
108 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
109 .add_to(acc);
110 108
111 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)) 109 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
112 .add_to(acc); 110 .add_to(acc);
@@ -125,33 +123,35 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
125 let dot_receiver = include_references(dot_receiver); 123 let dot_receiver = include_references(dot_receiver);
126 let receiver_text = 124 let receiver_text =
127 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal); 125 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
126
128 match try_enum { 127 match try_enum {
129 Some(try_enum) => { 128 Some(try_enum) => match try_enum {
130 match try_enum { 129 TryEnum::Result => {
131 TryEnum::Result => { 130 postfix_snippet(
132 postfix_snippet(
133 ctx, 131 ctx,
134 cap, 132 cap,
135 &dot_receiver, 133 &dot_receiver,
136 "match", 134 "match",
137 "match expr {}", 135 "match expr {}",
138 &format!("match {} {{\n Ok(${{1:_}}) => {{$2\\}},\n Err(${{3:_}}) => {{$0\\}},\n}}", receiver_text), 136 &format!("match {} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}", receiver_text),
139 ) 137 )
140 .add_to(acc); 138 .add_to(acc);
141 } 139 }
142 TryEnum::Option => { 140 TryEnum::Option => {
143 postfix_snippet( 141 postfix_snippet(
144 ctx, 142 ctx,
145 cap, 143 cap,
146 &dot_receiver, 144 &dot_receiver,
147 "match", 145 "match",
148 "match expr {}", 146 "match expr {}",
149 &format!("match {} {{\n Some(${{1:_}}) => {{$2\\}},\n None => {{$0\\}},\n}}", receiver_text), 147 &format!(
148 "match {} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}",
149 receiver_text
150 ),
150 ) 151 )
151 .add_to(acc); 152 .add_to(acc);
152 }
153 } 153 }
154 } 154 },
155 None => { 155 None => {
156 postfix_snippet( 156 postfix_snippet(
157 ctx, 157 ctx,
@@ -159,7 +159,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
159 &dot_receiver, 159 &dot_receiver,
160 "match", 160 "match",
161 "match expr {}", 161 "match expr {}",
162 &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text), 162 &format!("match {} {{\n ${{1:_}} => {{$0}},\n}}", receiver_text),
163 ) 163 )
164 .add_to(acc); 164 .add_to(acc);
165 } 165 }
@@ -232,536 +232,147 @@ fn postfix_snippet(
232 }; 232 };
233 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label) 233 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
234 .detail(detail) 234 .detail(detail)
235 .kind(CompletionItemKind::Snippet)
235 .snippet_edit(cap, edit) 236 .snippet_edit(cap, edit)
236} 237}
237 238
238#[cfg(test)] 239#[cfg(test)]
239mod tests { 240mod tests {
240 use insta::assert_debug_snapshot; 241 use expect::{expect, Expect};
241 242
242 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 243 use crate::completion::{
244 test_utils::{check_edit, completion_list},
245 CompletionKind,
246 };
243 247
244 fn do_postfix_completion(code: &str) -> Vec<CompletionItem> { 248 fn check(ra_fixture: &str, expect: Expect) {
245 do_completion(code, CompletionKind::Postfix) 249 let actual = completion_list(ra_fixture, CompletionKind::Postfix);
250 expect.assert_eq(&actual)
246 } 251 }
247 252
248 #[test] 253 #[test]
249 fn postfix_completion_works_for_trivial_path_expression() { 254 fn postfix_completion_works_for_trivial_path_expression() {
250 assert_debug_snapshot!( 255 check(
251 do_postfix_completion( 256 r#"
252 r#" 257fn main() {
253 fn main() { 258 let bar = true;
254 let bar = true; 259 bar.<|>
255 bar.<|> 260}
256 } 261"#,
257 "#, 262 expect![[r#"
258 ), 263 sn box Box::new(expr)
259 @r###" 264 sn call function(expr)
260 [ 265 sn dbg dbg!(expr)
261 CompletionItem { 266 sn if if expr {}
262 label: "box", 267 sn match match expr {}
263 source_range: 40..40, 268 sn not !expr
264 delete: 36..40, 269 sn ref &expr
265 insert: "Box::new(bar)", 270 sn refm &mut expr
266 detail: "Box::new(expr)", 271 sn while while expr {}
267 }, 272 "#]],
268 CompletionItem {
269 label: "call",
270 source_range: 40..40,
271 delete: 36..40,
272 insert: "${1}(bar)",
273 detail: "function(expr)",
274 },
275 CompletionItem {
276 label: "dbg",
277 source_range: 40..40,
278 delete: 36..40,
279 insert: "dbg!(bar)",
280 detail: "dbg!(expr)",
281 },
282 CompletionItem {
283 label: "if",
284 source_range: 40..40,
285 delete: 36..40,
286 insert: "if bar {\n $0\n}",
287 detail: "if expr {}",
288 },
289 CompletionItem {
290 label: "match",
291 source_range: 40..40,
292 delete: 36..40,
293 insert: "match bar {\n ${1:_} => {$0\\},\n}",
294 detail: "match expr {}",
295 },
296 CompletionItem {
297 label: "not",
298 source_range: 40..40,
299 delete: 36..40,
300 insert: "!bar",
301 detail: "!expr",
302 },
303 CompletionItem {
304 label: "ref",
305 source_range: 40..40,
306 delete: 36..40,
307 insert: "&bar",
308 detail: "&expr",
309 },
310 CompletionItem {
311 label: "refm",
312 source_range: 40..40,
313 delete: 36..40,
314 insert: "&mut bar",
315 detail: "&mut expr",
316 },
317 CompletionItem {
318 label: "while",
319 source_range: 40..40,
320 delete: 36..40,
321 insert: "while bar {\n $0\n}",
322 detail: "while expr {}",
323 },
324 ]
325 "###
326 ); 273 );
327 } 274 }
328 275
329 #[test] 276 #[test]
330 fn postfix_completion_works_for_option() { 277 fn postfix_type_filtering() {
331 assert_debug_snapshot!( 278 check(
332 do_postfix_completion( 279 r#"
333 r#" 280fn main() {
334 enum Option<T> { 281 let bar: u8 = 12;
335 Some(T), 282 bar.<|>
336 None, 283}
337 } 284"#,
338 285 expect![[r#"
339 fn main() { 286 sn box Box::new(expr)
340 let bar = Option::Some(true); 287 sn call function(expr)
341 bar.<|> 288 sn dbg dbg!(expr)
342 } 289 sn match match expr {}
343 "#, 290 sn ref &expr
344 ), 291 sn refm &mut expr
345 @r###" 292 "#]],
346 [ 293 )
347 CompletionItem {
348 label: "box",
349 source_range: 97..97,
350 delete: 93..97,
351 insert: "Box::new(bar)",
352 detail: "Box::new(expr)",
353 },
354 CompletionItem {
355 label: "call",
356 source_range: 97..97,
357 delete: 93..97,
358 insert: "${1}(bar)",
359 detail: "function(expr)",
360 },
361 CompletionItem {
362 label: "dbg",
363 source_range: 97..97,
364 delete: 93..97,
365 insert: "dbg!(bar)",
366 detail: "dbg!(expr)",
367 },
368 CompletionItem {
369 label: "ifl",
370 source_range: 97..97,
371 delete: 93..97,
372 insert: "if let Some($1) = bar {\n $0\n}",
373 detail: "if let Some {}",
374 },
375 CompletionItem {
376 label: "match",
377 source_range: 97..97,
378 delete: 93..97,
379 insert: "match bar {\n Some(${1:_}) => {$2\\},\n None => {$0\\},\n}",
380 detail: "match expr {}",
381 },
382 CompletionItem {
383 label: "not",
384 source_range: 97..97,
385 delete: 93..97,
386 insert: "!bar",
387 detail: "!expr",
388 },
389 CompletionItem {
390 label: "ref",
391 source_range: 97..97,
392 delete: 93..97,
393 insert: "&bar",
394 detail: "&expr",
395 },
396 CompletionItem {
397 label: "refm",
398 source_range: 97..97,
399 delete: 93..97,
400 insert: "&mut bar",
401 detail: "&mut expr",
402 },
403 CompletionItem {
404 label: "while",
405 source_range: 97..97,
406 delete: 93..97,
407 insert: "while let Some($1) = bar {\n $0\n}",
408 detail: "while let Some {}",
409 },
410 ]
411 "###
412 );
413 } 294 }
414 295
415 #[test] 296 #[test]
416 fn postfix_completion_works_for_result() { 297 fn option_iflet() {
417 assert_debug_snapshot!( 298 check_edit(
418 do_postfix_completion( 299 "ifl",
419 r#" 300 r#"
420 enum Result<T, E> { 301enum Option<T> { Some(T), None }
421 Ok(T), 302
422 Err(E), 303fn main() {
423 } 304 let bar = Option::Some(true);
305 bar.<|>
306}
307"#,
308 r#"
309enum Option<T> { Some(T), None }
424 310
425 fn main() { 311fn main() {
426 let bar = Result::Ok(true); 312 let bar = Option::Some(true);
427 bar.<|> 313 if let Some($1) = bar {
428 } 314 $0
429 "#, 315}
430 ), 316}
431 @r###" 317"#,
432 [
433 CompletionItem {
434 label: "box",
435 source_range: 98..98,
436 delete: 94..98,
437 insert: "Box::new(bar)",
438 detail: "Box::new(expr)",
439 },
440 CompletionItem {
441 label: "call",
442 source_range: 98..98,
443 delete: 94..98,
444 insert: "${1}(bar)",
445 detail: "function(expr)",
446 },
447 CompletionItem {
448 label: "dbg",
449 source_range: 98..98,
450 delete: 94..98,
451 insert: "dbg!(bar)",
452 detail: "dbg!(expr)",
453 },
454 CompletionItem {
455 label: "ifl",
456 source_range: 98..98,
457 delete: 94..98,
458 insert: "if let Ok($1) = bar {\n $0\n}",
459 detail: "if let Ok {}",
460 },
461 CompletionItem {
462 label: "match",
463 source_range: 98..98,
464 delete: 94..98,
465 insert: "match bar {\n Ok(${1:_}) => {$2\\},\n Err(${3:_}) => {$0\\},\n}",
466 detail: "match expr {}",
467 },
468 CompletionItem {
469 label: "not",
470 source_range: 98..98,
471 delete: 94..98,
472 insert: "!bar",
473 detail: "!expr",
474 },
475 CompletionItem {
476 label: "ref",
477 source_range: 98..98,
478 delete: 94..98,
479 insert: "&bar",
480 detail: "&expr",
481 },
482 CompletionItem {
483 label: "refm",
484 source_range: 98..98,
485 delete: 94..98,
486 insert: "&mut bar",
487 detail: "&mut expr",
488 },
489 CompletionItem {
490 label: "while",
491 source_range: 98..98,
492 delete: 94..98,
493 insert: "while let Ok($1) = bar {\n $0\n}",
494 detail: "while let Ok {}",
495 },
496 ]
497 "###
498 ); 318 );
499 } 319 }
500 320
501 #[test] 321 #[test]
502 fn some_postfix_completions_ignored() { 322 fn result_match() {
503 assert_debug_snapshot!( 323 check_edit(
504 do_postfix_completion( 324 "match",
505 r#" 325 r#"
506 fn main() { 326enum Result<T, E> { Ok(T), Err(E) }
507 let bar: u8 = 12; 327
508 bar.<|> 328fn main() {
509 } 329 let bar = Result::Ok(true);
510 "#, 330 bar.<|>
511 ), 331}
512 @r###" 332"#,
513 [ 333 r#"
514 CompletionItem { 334enum Result<T, E> { Ok(T), Err(E) }
515 label: "box", 335
516 source_range: 42..42, 336fn main() {
517 delete: 38..42, 337 let bar = Result::Ok(true);
518 insert: "Box::new(bar)", 338 match bar {
519 detail: "Box::new(expr)", 339 Ok(${1:_}) => {$2},
520 }, 340 Err(${3:_}) => {$0},
521 CompletionItem { 341}
522 label: "call", 342}
523 source_range: 42..42, 343"#,
524 delete: 38..42,
525 insert: "${1}(bar)",
526 detail: "function(expr)",
527 },
528 CompletionItem {
529 label: "dbg",
530 source_range: 42..42,
531 delete: 38..42,
532 insert: "dbg!(bar)",
533 detail: "dbg!(expr)",
534 },
535 CompletionItem {
536 label: "match",
537 source_range: 42..42,
538 delete: 38..42,
539 insert: "match bar {\n ${1:_} => {$0\\},\n}",
540 detail: "match expr {}",
541 },
542 CompletionItem {
543 label: "not",
544 source_range: 42..42,
545 delete: 38..42,
546 insert: "!bar",
547 detail: "!expr",
548 },
549 CompletionItem {
550 label: "ref",
551 source_range: 42..42,
552 delete: 38..42,
553 insert: "&bar",
554 detail: "&expr",
555 },
556 CompletionItem {
557 label: "refm",
558 source_range: 42..42,
559 delete: 38..42,
560 insert: "&mut bar",
561 detail: "&mut expr",
562 },
563 ]
564 "###
565 ); 344 );
566 } 345 }
567 346
568 #[test] 347 #[test]
569 fn postfix_completion_works_for_ambiguous_float_literal() { 348 fn postfix_completion_works_for_ambiguous_float_literal() {
570 assert_debug_snapshot!( 349 check_edit("refm", r#"fn main() { 42.<|> }"#, r#"fn main() { &mut 42 }"#)
571 do_postfix_completion(
572 r#"
573 fn main() {
574 42.<|>
575 }
576 "#,
577 ),
578 @r###"
579 [
580 CompletionItem {
581 label: "box",
582 source_range: 19..19,
583 delete: 16..19,
584 insert: "Box::new(42)",
585 detail: "Box::new(expr)",
586 },
587 CompletionItem {
588 label: "call",
589 source_range: 19..19,
590 delete: 16..19,
591 insert: "${1}(42)",
592 detail: "function(expr)",
593 },
594 CompletionItem {
595 label: "dbg",
596 source_range: 19..19,
597 delete: 16..19,
598 insert: "dbg!(42)",
599 detail: "dbg!(expr)",
600 },
601 CompletionItem {
602 label: "match",
603 source_range: 19..19,
604 delete: 16..19,
605 insert: "match 42 {\n ${1:_} => {$0\\},\n}",
606 detail: "match expr {}",
607 },
608 CompletionItem {
609 label: "not",
610 source_range: 19..19,
611 delete: 16..19,
612 insert: "!42",
613 detail: "!expr",
614 },
615 CompletionItem {
616 label: "ref",
617 source_range: 19..19,
618 delete: 16..19,
619 insert: "&42",
620 detail: "&expr",
621 },
622 CompletionItem {
623 label: "refm",
624 source_range: 19..19,
625 delete: 16..19,
626 insert: "&mut 42",
627 detail: "&mut expr",
628 },
629 ]
630 "###
631 );
632 } 350 }
633 351
634 #[test] 352 #[test]
635 fn works_in_simple_macro() { 353 fn works_in_simple_macro() {
636 assert_debug_snapshot!( 354 check_edit(
637 do_postfix_completion( 355 "dbg",
638 r#" 356 r#"
639 macro_rules! m { ($e:expr) => { $e } } 357macro_rules! m { ($e:expr) => { $e } }
640 fn main() { 358fn main() {
641 let bar: u8 = 12; 359 let bar: u8 = 12;
642 m!(bar.b<|>) 360 m!(bar.d<|>)
643 } 361}
644 "#, 362"#,
645 ), 363 r#"
646 @r###" 364macro_rules! m { ($e:expr) => { $e } }
647 [ 365fn main() {
648 CompletionItem { 366 let bar: u8 = 12;
649 label: "box", 367 m!(dbg!(bar))
650 source_range: 84..85, 368}
651 delete: 80..85, 369"#,
652 insert: "Box::new(bar)",
653 detail: "Box::new(expr)",
654 },
655 CompletionItem {
656 label: "call",
657 source_range: 84..85,
658 delete: 80..85,
659 insert: "${1}(bar)",
660 detail: "function(expr)",
661 },
662 CompletionItem {
663 label: "dbg",
664 source_range: 84..85,
665 delete: 80..85,
666 insert: "dbg!(bar)",
667 detail: "dbg!(expr)",
668 },
669 CompletionItem {
670 label: "match",
671 source_range: 84..85,
672 delete: 80..85,
673 insert: "match bar {\n ${1:_} => {$0\\},\n}",
674 detail: "match expr {}",
675 },
676 CompletionItem {
677 label: "not",
678 source_range: 84..85,
679 delete: 80..85,
680 insert: "!bar",
681 detail: "!expr",
682 },
683 CompletionItem {
684 label: "ref",
685 source_range: 84..85,
686 delete: 80..85,
687 insert: "&bar",
688 detail: "&expr",
689 },
690 CompletionItem {
691 label: "refm",
692 source_range: 84..85,
693 delete: 80..85,
694 insert: "&mut bar",
695 detail: "&mut expr",
696 },
697 ]
698 "###
699 ); 370 );
700 } 371 }
701 372
702 #[test] 373 #[test]
703 fn postfix_completion_for_references() { 374 fn postfix_completion_for_references() {
704 assert_debug_snapshot!( 375 check_edit("dbg", r#"fn main() { &&42.<|> }"#, r#"fn main() { dbg!(&&42) }"#);
705 do_postfix_completion( 376 check_edit("refm", r#"fn main() { &&42.<|> }"#, r#"fn main() { &&&mut 42 }"#);
706 r#"
707 fn main() {
708 &&&&42.<|>
709 }
710 "#,
711 ),
712 @r###"
713 [
714 CompletionItem {
715 label: "box",
716 source_range: 23..23,
717 delete: 16..23,
718 insert: "Box::new(&&&&42)",
719 detail: "Box::new(expr)",
720 },
721 CompletionItem {
722 label: "call",
723 source_range: 23..23,
724 delete: 16..23,
725 insert: "${1}(&&&&42)",
726 detail: "function(expr)",
727 },
728 CompletionItem {
729 label: "dbg",
730 source_range: 23..23,
731 delete: 16..23,
732 insert: "dbg!(&&&&42)",
733 detail: "dbg!(expr)",
734 },
735 CompletionItem {
736 label: "match",
737 source_range: 23..23,
738 delete: 16..23,
739 insert: "match &&&&42 {\n ${1:_} => {$0\\},\n}",
740 detail: "match expr {}",
741 },
742 CompletionItem {
743 label: "not",
744 source_range: 23..23,
745 delete: 20..23,
746 insert: "!42",
747 detail: "!expr",
748 },
749 CompletionItem {
750 label: "ref",
751 source_range: 23..23,
752 delete: 20..23,
753 insert: "&42",
754 detail: "&expr",
755 },
756 CompletionItem {
757 label: "refm",
758 source_range: 23..23,
759 delete: 20..23,
760 insert: "&mut 42",
761 detail: "&mut expr",
762 },
763 ]
764 "###
765 );
766 } 377 }
767} 378}
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs
index f133ce3ce..b08f5b9b4 100644
--- a/crates/ra_ide/src/completion/complete_qualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_qualified_path.rs
@@ -17,21 +17,20 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
17 return; 17 return;
18 } 18 }
19 19
20 let scope = ctx.scope(); 20 let context_module = ctx.scope.module();
21 let context_module = scope.module();
22 21
23 let res = match scope.resolve_hir_path_qualifier(&path) { 22 let resolution = match ctx.scope.resolve_hir_path_qualifier(&path) {
24 Some(res) => res, 23 Some(res) => res,
25 None => return, 24 None => return,
26 }; 25 };
27 26
28 // Add associated types on type parameters and `Self`. 27 // Add associated types on type parameters and `Self`.
29 res.assoc_type_shorthand_candidates(ctx.db, |alias| { 28 resolution.assoc_type_shorthand_candidates(ctx.db, |alias| {
30 acc.add_type_alias(ctx, alias); 29 acc.add_type_alias(ctx, alias);
31 None::<()> 30 None::<()>
32 }); 31 });
33 32
34 match res { 33 match resolution {
35 PathResolution::Def(hir::ModuleDef::Module(module)) => { 34 PathResolution::Def(hir::ModuleDef::Module(module)) => {
36 let module_scope = module.scope(ctx.db, context_module); 35 let module_scope = module.scope(ctx.db, context_module);
37 for (name, def) in module_scope { 36 for (name, def) in module_scope {
@@ -68,7 +67,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
68 67
69 let krate = ctx.krate; 68 let krate = ctx.krate;
70 if let Some(krate) = krate { 69 if let Some(krate) = krate {
71 let traits_in_scope = ctx.scope().traits_in_scope(); 70 let traits_in_scope = ctx.scope.traits_in_scope();
72 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { 71 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
73 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { 72 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
74 return None; 73 return None;
@@ -113,13 +112,13 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
113 } 112 }
114 PathResolution::TypeParam(_) | PathResolution::SelfType(_) => { 113 PathResolution::TypeParam(_) | PathResolution::SelfType(_) => {
115 if let Some(krate) = ctx.krate { 114 if let Some(krate) = ctx.krate {
116 let ty = match res { 115 let ty = match resolution {
117 PathResolution::TypeParam(param) => param.ty(ctx.db), 116 PathResolution::TypeParam(param) => param.ty(ctx.db),
118 PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db), 117 PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db),
119 _ => return, 118 _ => return,
120 }; 119 };
121 120
122 let traits_in_scope = ctx.scope().traits_in_scope(); 121 let traits_in_scope = ctx.scope.traits_in_scope();
123 let mut seen = FxHashSet::default(); 122 let mut seen = FxHashSet::default();
124 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { 123 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
125 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { 124 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
@@ -147,1229 +146,588 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
147 146
148#[cfg(test)] 147#[cfg(test)]
149mod tests { 148mod tests {
149 use expect::{expect, Expect};
150 use test_utils::mark; 150 use test_utils::mark;
151 151
152 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 152 use crate::completion::{
153 use insta::assert_debug_snapshot; 153 test_utils::{check_edit, completion_list},
154 CompletionKind,
155 };
156
157 fn check(ra_fixture: &str, expect: Expect) {
158 let actual = completion_list(ra_fixture, CompletionKind::Reference);
159 expect.assert_eq(&actual);
160 }
154 161
155 fn do_reference_completion(code: &str) -> Vec<CompletionItem> { 162 fn check_builtin(ra_fixture: &str, expect: Expect) {
156 do_completion(code, CompletionKind::Reference) 163 let actual = completion_list(ra_fixture, CompletionKind::BuiltinType);
164 expect.assert_eq(&actual);
157 } 165 }
158 166
159 #[test] 167 #[test]
160 fn dont_complete_current_use() { 168 fn dont_complete_current_use() {
161 mark::check!(dont_complete_current_use); 169 mark::check!(dont_complete_current_use);
162 let completions = do_completion(r"use self::foo<|>;", CompletionKind::Reference); 170 check(r#"use self::foo<|>;"#, expect![[""]]);
163 assert!(completions.is_empty());
164 } 171 }
165 172
166 #[test] 173 #[test]
167 fn dont_complete_current_use_in_braces_with_glob() { 174 fn dont_complete_current_use_in_braces_with_glob() {
168 let completions = do_completion( 175 check(
169 r" 176 r#"
170 mod foo { pub struct S; } 177mod foo { pub struct S; }
171 use self::{foo::*, bar<|>}; 178use self::{foo::*, bar<|>};
172 ", 179"#,
173 CompletionKind::Reference, 180 expect![[r#"
181 st S
182 md foo
183 "#]],
174 ); 184 );
175 assert_eq!(completions.len(), 2);
176 } 185 }
177 186
178 #[test] 187 #[test]
179 fn dont_complete_primitive_in_use() { 188 fn dont_complete_primitive_in_use() {
180 let completions = do_completion(r"use self::<|>;", CompletionKind::BuiltinType); 189 check_builtin(r#"use self::<|>;"#, expect![[""]]);
181 assert!(completions.is_empty());
182 } 190 }
183 191
184 #[test] 192 #[test]
185 fn dont_complete_primitive_in_module_scope() { 193 fn dont_complete_primitive_in_module_scope() {
186 let completions = do_completion(r"fn foo() { self::<|> }", CompletionKind::BuiltinType); 194 check_builtin(r#"fn foo() { self::<|> }"#, expect![[""]]);
187 assert!(completions.is_empty());
188 } 195 }
189 196
190 #[test] 197 #[test]
191 fn completes_primitives() { 198 fn completes_primitives() {
192 let completions = 199 check_builtin(
193 do_completion(r"fn main() { let _: <|> = 92; }", CompletionKind::BuiltinType); 200 r#"fn main() { let _: <|> = 92; }"#,
194 assert_eq!(completions.len(), 17); 201 expect![[r#"
195 } 202 bt bool
196 203 bt char
197 #[test] 204 bt f32
198 fn completes_mod_with_docs() { 205 bt f64
199 assert_debug_snapshot!( 206 bt i128
200 do_reference_completion( 207 bt i16
201 r" 208 bt i32
202 use self::my<|>; 209 bt i64
203 210 bt i8
204 /// Some simple 211 bt isize
205 /// docs describing `mod my`. 212 bt str
206 mod my { 213 bt u128
207 struct Bar; 214 bt u16
208 } 215 bt u32
209 " 216 bt u64
210 ), 217 bt u8
211 @r###" 218 bt usize
212 [ 219 "#]],
213 CompletionItem {
214 label: "my",
215 source_range: 10..12,
216 delete: 10..12,
217 insert: "my",
218 kind: Module,
219 documentation: Documentation(
220 "Some simple\ndocs describing `mod my`.",
221 ),
222 },
223 ]
224 "###
225 ); 220 );
226 } 221 }
227 222
228 #[test] 223 #[test]
229 fn completes_mod_with_same_name_as_function() { 224 fn completes_mod_with_same_name_as_function() {
230 assert_debug_snapshot!( 225 check(
231 do_reference_completion( 226 r#"
232 r" 227use self::my::<|>;
233 use self::my::<|>; 228
234 229mod my { pub struct Bar; }
235 mod my { 230fn my() {}
236 pub struct Bar; 231"#,
237 } 232 expect![[r#"
238 233 st Bar
239 fn my() {} 234 "#]],
240 "
241 ),
242 @r###"
243 [
244 CompletionItem {
245 label: "Bar",
246 source_range: 14..14,
247 delete: 14..14,
248 insert: "Bar",
249 kind: Struct,
250 },
251 ]
252 "###
253 ); 235 );
254 } 236 }
255 237
256 #[test] 238 #[test]
257 fn path_visibility() { 239 fn filters_visibility() {
258 assert_debug_snapshot!( 240 check(
259 do_reference_completion( 241 r#"
260 r" 242use self::my::<|>;
261 use self::my::<|>; 243
262 244mod my {
263 mod my { 245 struct Bar;
264 struct Bar; 246 pub struct Foo;
265 pub struct Foo; 247 pub use Bar as PublicBar;
266 pub use Bar as PublicBar; 248}
267 } 249"#,
268 " 250 expect![[r#"
269 ), 251 st Foo
270 @r###" 252 st PublicBar
271 [ 253 "#]],
272 CompletionItem {
273 label: "Foo",
274 source_range: 14..14,
275 delete: 14..14,
276 insert: "Foo",
277 kind: Struct,
278 },
279 CompletionItem {
280 label: "PublicBar",
281 source_range: 14..14,
282 delete: 14..14,
283 insert: "PublicBar",
284 kind: Struct,
285 },
286 ]
287 "###
288 ); 254 );
289 } 255 }
290 256
291 #[test] 257 #[test]
292 fn completes_use_item_starting_with_self() { 258 fn completes_use_item_starting_with_self() {
293 assert_debug_snapshot!( 259 check(
294 do_reference_completion( 260 r#"
295 r" 261use self::m::<|>;
296 use self::m::<|>;
297 262
298 mod m { 263mod m { pub struct Bar; }
299 pub struct Bar; 264"#,
300 } 265 expect![[r#"
301 " 266 st Bar
302 ), 267 "#]],
303 @r###"
304 [
305 CompletionItem {
306 label: "Bar",
307 source_range: 13..13,
308 delete: 13..13,
309 insert: "Bar",
310 kind: Struct,
311 },
312 ]
313 "###
314 ); 268 );
315 } 269 }
316 270
317 #[test] 271 #[test]
318 fn completes_use_item_starting_with_crate() { 272 fn completes_use_item_starting_with_crate() {
319 assert_debug_snapshot!( 273 check(
320 do_reference_completion( 274 r#"
321 " 275//- /lib.rs
322 //- /lib.rs 276mod foo;
323 mod foo; 277struct Spam;
324 struct Spam; 278//- /foo.rs
325 //- /foo.rs 279use crate::Sp<|>
326 use crate::Sp<|> 280"#,
327 " 281 expect![[r#"
328 ), 282 st Spam
329 @r###" 283 md foo
330 [ 284 "#]],
331 CompletionItem {
332 label: "Spam",
333 source_range: 11..13,
334 delete: 11..13,
335 insert: "Spam",
336 kind: Struct,
337 },
338 CompletionItem {
339 label: "foo",
340 source_range: 11..13,
341 delete: 11..13,
342 insert: "foo",
343 kind: Module,
344 },
345 ]
346 "###
347 ); 285 );
348 } 286 }
349 287
350 #[test] 288 #[test]
351 fn completes_nested_use_tree() { 289 fn completes_nested_use_tree() {
352 assert_debug_snapshot!( 290 check(
353 do_reference_completion( 291 r#"
354 " 292//- /lib.rs
355 //- /lib.rs 293mod foo;
356 mod foo; 294struct Spam;
357 struct Spam; 295//- /foo.rs
358 //- /foo.rs 296use crate::{Sp<|>};
359 use crate::{Sp<|>}; 297"#,
360 " 298 expect![[r#"
361 ), 299 st Spam
362 @r###" 300 md foo
363 [ 301 "#]],
364 CompletionItem {
365 label: "Spam",
366 source_range: 12..14,
367 delete: 12..14,
368 insert: "Spam",
369 kind: Struct,
370 },
371 CompletionItem {
372 label: "foo",
373 source_range: 12..14,
374 delete: 12..14,
375 insert: "foo",
376 kind: Module,
377 },
378 ]
379 "###
380 ); 302 );
381 } 303 }
382 304
383 #[test] 305 #[test]
384 fn completes_deeply_nested_use_tree() { 306 fn completes_deeply_nested_use_tree() {
385 assert_debug_snapshot!( 307 check(
386 do_reference_completion( 308 r#"
387 " 309//- /lib.rs
388 //- /lib.rs 310mod foo;
389 mod foo; 311pub mod bar {
390 pub mod bar { 312 pub mod baz {
391 pub mod baz { 313 pub struct Spam;
392 pub struct Spam;
393 }
394 }
395 //- /foo.rs
396 use crate::{bar::{baz::Sp<|>}};
397 "
398 ),
399 @r###"
400 [
401 CompletionItem {
402 label: "Spam",
403 source_range: 23..25,
404 delete: 23..25,
405 insert: "Spam",
406 kind: Struct,
407 },
408 ]
409 "###
410 );
411 }
412
413 #[test]
414 fn completes_enum_variant() {
415 assert_debug_snapshot!(
416 do_reference_completion(
417 "
418 //- /lib.rs
419 /// An enum
420 enum E {
421 /// Foo Variant
422 Foo,
423 /// Bar Variant with i32
424 Bar(i32)
425 }
426 fn foo() { let _ = E::<|> }
427 "
428 ),
429 @r###"
430 [
431 CompletionItem {
432 label: "Bar(…)",
433 source_range: 116..116,
434 delete: 116..116,
435 insert: "Bar($0)",
436 kind: EnumVariant,
437 lookup: "Bar",
438 detail: "(i32)",
439 documentation: Documentation(
440 "Bar Variant with i32",
441 ),
442 trigger_call_info: true,
443 },
444 CompletionItem {
445 label: "Foo",
446 source_range: 116..116,
447 delete: 116..116,
448 insert: "Foo",
449 kind: EnumVariant,
450 detail: "()",
451 documentation: Documentation(
452 "Foo Variant",
453 ),
454 },
455 ]
456 "###
457 );
458 }
459
460 #[test]
461 fn completes_enum_variant_with_details() {
462 assert_debug_snapshot!(
463 do_reference_completion(
464 "
465 //- /lib.rs
466 struct S { field: u32 }
467 /// An enum
468 enum E {
469 /// Foo Variant (empty)
470 Foo,
471 /// Bar Variant with i32 and u32
472 Bar(i32, u32),
473 ///
474 S(S),
475 }
476 fn foo() { let _ = E::<|> }
477 "
478 ),
479 @r###"
480 [
481 CompletionItem {
482 label: "Bar(…)",
483 source_range: 180..180,
484 delete: 180..180,
485 insert: "Bar($0)",
486 kind: EnumVariant,
487 lookup: "Bar",
488 detail: "(i32, u32)",
489 documentation: Documentation(
490 "Bar Variant with i32 and u32",
491 ),
492 trigger_call_info: true,
493 },
494 CompletionItem {
495 label: "Foo",
496 source_range: 180..180,
497 delete: 180..180,
498 insert: "Foo",
499 kind: EnumVariant,
500 detail: "()",
501 documentation: Documentation(
502 "Foo Variant (empty)",
503 ),
504 },
505 CompletionItem {
506 label: "S(…)",
507 source_range: 180..180,
508 delete: 180..180,
509 insert: "S($0)",
510 kind: EnumVariant,
511 lookup: "S",
512 detail: "(S)",
513 documentation: Documentation(
514 "",
515 ),
516 trigger_call_info: true,
517 },
518 ]
519 "###
520 );
521 } 314 }
522 315}
523 #[test] 316//- /foo.rs
524 fn completes_struct_associated_method() { 317use crate::{bar::{baz::Sp<|>}};
525 assert_debug_snapshot!( 318"#,
526 do_reference_completion( 319 expect![[r#"
527 " 320 st Spam
528 //- /lib.rs 321 "#]],
529 /// A Struct
530 struct S;
531
532 impl S {
533 /// An associated method
534 fn m() { }
535 }
536
537 fn foo() { let _ = S::<|> }
538 "
539 ),
540 @r###"
541 [
542 CompletionItem {
543 label: "m()",
544 source_range: 102..102,
545 delete: 102..102,
546 insert: "m()$0",
547 kind: Function,
548 lookup: "m",
549 detail: "fn m()",
550 documentation: Documentation(
551 "An associated method",
552 ),
553 },
554 ]
555 "###
556 ); 322 );
557 } 323 }
558 324
559 #[test] 325 #[test]
560 fn completes_struct_associated_method_with_self() { 326 fn completes_enum_variant() {
561 assert_debug_snapshot!( 327 check(
562 do_reference_completion( 328 r#"
563 " 329enum E { Foo, Bar(i32) }
564 //- /lib.rs 330fn foo() { let _ = E::<|> }
565 /// A Struct 331"#,
566 struct S; 332 expect![[r#"
567 333 ev Bar(…) (i32)
568 impl S { 334 ev Foo ()
569 /// An associated method 335 "#]],
570 fn m(&self) { }
571 }
572
573 fn foo() { let _ = S::<|> }
574 "
575 ),
576 @r###"
577 [
578 CompletionItem {
579 label: "m()",
580 source_range: 107..107,
581 delete: 107..107,
582 insert: "m()$0",
583 kind: Method,
584 lookup: "m",
585 detail: "fn m(&self)",
586 documentation: Documentation(
587 "An associated method",
588 ),
589 },
590 ]
591 "###
592 ); 336 );
593 } 337 }
594 338
595 #[test] 339 #[test]
596 fn completes_struct_associated_const() { 340 fn completes_struct_associated_items() {
597 assert_debug_snapshot!( 341 check(
598 do_reference_completion( 342 r#"
599 " 343//- /lib.rs
600 //- /lib.rs 344struct S;
601 /// A Struct 345
602 struct S; 346impl S {
603 347 fn a() {}
604 impl S { 348 fn b(&self) {}
605 /// An associated const 349 const C: i32 = 42;
606 const C: i32 = 42; 350 type T = i32;
607 } 351}
608 352
609 fn foo() { let _ = S::<|> } 353fn foo() { let _ = S::<|> }
610 " 354"#,
611 ), 355 expect![[r#"
612 @r###" 356 ct C const C: i32 = 42;
613 [ 357 ta T type T = i32;
614 CompletionItem { 358 fn a() fn a()
615 label: "C", 359 me b() fn b(&self)
616 source_range: 109..109, 360 "#]],
617 delete: 109..109,
618 insert: "C",
619 kind: Const,
620 detail: "const C: i32 = 42;",
621 documentation: Documentation(
622 "An associated const",
623 ),
624 },
625 ]
626 "###
627 ); 361 );
628 } 362 }
629 363
630 #[test] 364 #[test]
631 fn completes_struct_associated_type() { 365 fn associated_item_visibility() {
632 assert_debug_snapshot!( 366 check(
633 do_reference_completion( 367 r#"
634 " 368struct S;
635 //- /lib.rs
636 /// A Struct
637 struct S;
638
639 impl S {
640 /// An associated type
641 type T = i32;
642 }
643 369
644 fn foo() { let _ = S::<|> } 370mod m {
645 " 371 impl super::S {
646 ), 372 pub(super) fn public_method() { }
647 @r###" 373 fn private_method() { }
648 [ 374 pub(super) type PublicType = u32;
649 CompletionItem { 375 type PrivateType = u32;
650 label: "T", 376 pub(super) const PUBLIC_CONST: u32 = 1;
651 source_range: 103..103, 377 const PRIVATE_CONST: u32 = 1;
652 delete: 103..103,
653 insert: "T",
654 kind: TypeAlias,
655 detail: "type T = i32;",
656 documentation: Documentation(
657 "An associated type",
658 ),
659 },
660 ]
661 "###
662 );
663 } 378 }
379}
664 380
665 #[test] 381fn foo() { let _ = S::<|> }
666 fn associated_item_visibility() { 382"#,
667 assert_debug_snapshot!( 383 expect![[r#"
668 do_reference_completion( 384 ct PUBLIC_CONST pub(super) const PUBLIC_CONST: u32 = 1;
669 " 385 ta PublicType pub(super) type PublicType = u32;
670 //- /lib.rs 386 fn public_method() pub(super) fn public_method()
671 struct S; 387 "#]],
672
673 mod m {
674 impl super::S {
675 pub(super) fn public_method() { }
676 fn private_method() { }
677 pub(super) type PublicType = u32;
678 type PrivateType = u32;
679 pub(super) const PUBLIC_CONST: u32 = 1;
680 const PRIVATE_CONST: u32 = 1;
681 }
682 }
683
684 fn foo() { let _ = S::<|> }
685 "
686 ),
687 @r###"
688 [
689 CompletionItem {
690 label: "PUBLIC_CONST",
691 source_range: 304..304,
692 delete: 304..304,
693 insert: "PUBLIC_CONST",
694 kind: Const,
695 detail: "pub(super) const PUBLIC_CONST: u32 = 1;",
696 },
697 CompletionItem {
698 label: "PublicType",
699 source_range: 304..304,
700 delete: 304..304,
701 insert: "PublicType",
702 kind: TypeAlias,
703 detail: "pub(super) type PublicType = u32;",
704 },
705 CompletionItem {
706 label: "public_method()",
707 source_range: 304..304,
708 delete: 304..304,
709 insert: "public_method()$0",
710 kind: Function,
711 lookup: "public_method",
712 detail: "pub(super) fn public_method()",
713 },
714 ]
715 "###
716 ); 388 );
717 } 389 }
718 390
719 #[test] 391 #[test]
720 fn completes_enum_associated_method() { 392 fn completes_enum_associated_method() {
721 assert_debug_snapshot!( 393 check(
722 do_reference_completion( 394 r#"
723 " 395enum E {};
724 //- /lib.rs 396impl E { fn m() { } }
725 /// An enum 397
726 enum S {}; 398fn foo() { let _ = E::<|> }
727 399 "#,
728 impl S { 400 expect![[r#"
729 /// An associated method 401 fn m() fn m()
730 fn m() { } 402 "#]],
731 }
732
733 fn foo() { let _ = S::<|> }
734 "
735 ),
736 @r###"
737 [
738 CompletionItem {
739 label: "m()",
740 source_range: 102..102,
741 delete: 102..102,
742 insert: "m()$0",
743 kind: Function,
744 lookup: "m",
745 detail: "fn m()",
746 documentation: Documentation(
747 "An associated method",
748 ),
749 },
750 ]
751 "###
752 ); 403 );
753 } 404 }
754 405
755 #[test] 406 #[test]
756 fn completes_union_associated_method() { 407 fn completes_union_associated_method() {
757 assert_debug_snapshot!( 408 check(
758 do_reference_completion( 409 r#"
759 " 410union U {};
760 //- /lib.rs 411impl U { fn m() { } }
761 /// A union 412
762 union U {}; 413fn foo() { let _ = U::<|> }
763 414"#,
764 impl U { 415 expect![[r#"
765 /// An associated method 416 fn m() fn m()
766 fn m() { } 417 "#]],
767 }
768
769 fn foo() { let _ = U::<|> }
770 "
771 ),
772 @r###"
773 [
774 CompletionItem {
775 label: "m()",
776 source_range: 103..103,
777 delete: 103..103,
778 insert: "m()$0",
779 kind: Function,
780 lookup: "m",
781 detail: "fn m()",
782 documentation: Documentation(
783 "An associated method",
784 ),
785 },
786 ]
787 "###
788 ); 418 );
789 } 419 }
790 420
791 #[test] 421 #[test]
792 fn completes_use_paths_across_crates() { 422 fn completes_use_paths_across_crates() {
793 assert_debug_snapshot!( 423 check(
794 do_reference_completion( 424 r#"
795 " 425//- /main.rs
796 //- /main.rs 426use foo::<|>;
797 use foo::<|>; 427
798 428//- /foo/lib.rs
799 //- /foo/lib.rs 429pub mod bar { pub struct S; }
800 pub mod bar { 430"#,
801 pub struct S; 431 expect![[r#"
802 } 432 md bar
803 " 433 "#]],
804 ),
805 @r###"
806 [
807 CompletionItem {
808 label: "bar",
809 source_range: 9..9,
810 delete: 9..9,
811 insert: "bar",
812 kind: Module,
813 },
814 ]
815 "###
816 ); 434 );
817 } 435 }
818 436
819 #[test] 437 #[test]
820 fn completes_trait_associated_method_1() { 438 fn completes_trait_associated_method_1() {
821 assert_debug_snapshot!( 439 check(
822 do_reference_completion( 440 r#"
823 " 441trait Trait { fn m(); }
824 //- /lib.rs
825 trait Trait {
826 /// A trait method
827 fn m();
828 }
829 442
830 fn foo() { let _ = Trait::<|> } 443fn foo() { let _ = Trait::<|> }
831 " 444"#,
832 ), 445 expect![[r#"
833 @r###" 446 fn m() fn m()
834 [ 447 "#]],
835 CompletionItem {
836 label: "m()",
837 source_range: 74..74,
838 delete: 74..74,
839 insert: "m()$0",
840 kind: Function,
841 lookup: "m",
842 detail: "fn m()",
843 documentation: Documentation(
844 "A trait method",
845 ),
846 },
847 ]
848 "###
849 ); 448 );
850 } 449 }
851 450
852 #[test] 451 #[test]
853 fn completes_trait_associated_method_2() { 452 fn completes_trait_associated_method_2() {
854 assert_debug_snapshot!( 453 check(
855 do_reference_completion( 454 r#"
856 " 455trait Trait { fn m(); }
857 //- /lib.rs 456
858 trait Trait { 457struct S;
859 /// A trait method 458impl Trait for S {}
860 fn m();
861 }
862 459
863 struct S; 460fn foo() { let _ = S::<|> }
864 impl Trait for S {} 461"#,
865 462 expect![[r#"
866 fn foo() { let _ = S::<|> } 463 fn m() fn m()
867 " 464 "#]],
868 ),
869 @r###"
870 [
871 CompletionItem {
872 label: "m()",
873 source_range: 101..101,
874 delete: 101..101,
875 insert: "m()$0",
876 kind: Function,
877 lookup: "m",
878 detail: "fn m()",
879 documentation: Documentation(
880 "A trait method",
881 ),
882 },
883 ]
884 "###
885 ); 465 );
886 } 466 }
887 467
888 #[test] 468 #[test]
889 fn completes_trait_associated_method_3() { 469 fn completes_trait_associated_method_3() {
890 assert_debug_snapshot!( 470 check(
891 do_reference_completion( 471 r#"
892 " 472trait Trait { fn m(); }
893 //- /lib.rs
894 trait Trait {
895 /// A trait method
896 fn m();
897 }
898 473
899 struct S; 474struct S;
900 impl Trait for S {} 475impl Trait for S {}
901 476
902 fn foo() { let _ = <S as Trait>::<|> } 477fn foo() { let _ = <S as Trait>::<|> }
903 " 478"#,
904 ), 479 expect![[r#"
905 @r###" 480 fn m() fn m()
906 [ 481 "#]],
907 CompletionItem {
908 label: "m()",
909 source_range: 112..112,
910 delete: 112..112,
911 insert: "m()$0",
912 kind: Function,
913 lookup: "m",
914 detail: "fn m()",
915 documentation: Documentation(
916 "A trait method",
917 ),
918 },
919 ]
920 "###
921 ); 482 );
922 } 483 }
923 484
924 #[test] 485 #[test]
925 fn completes_ty_param_assoc_ty() { 486 fn completes_ty_param_assoc_ty() {
926 assert_debug_snapshot!( 487 check(
927 do_reference_completion( 488 r#"
928 " 489trait Super {
929 //- /lib.rs 490 type Ty;
930 trait Super { 491 const CONST: u8;
931 type Ty; 492 fn func() {}
932 const CONST: u8; 493 fn method(&self) {}
933 fn func() {} 494}
934 fn method(&self) {}
935 }
936 495
937 trait Sub: Super { 496trait Sub: Super {
938 type SubTy; 497 type SubTy;
939 const C2: (); 498 const C2: ();
940 fn subfunc() {} 499 fn subfunc() {}
941 fn submethod(&self) {} 500 fn submethod(&self) {}
942 } 501}
943 502
944 fn foo<T: Sub>() { 503fn foo<T: Sub>() { T::<|> }
945 T::<|> 504"#,
946 } 505 expect![[r#"
947 " 506 ct C2 const C2: ();
948 ), 507 ct CONST const CONST: u8;
949 @r###" 508 ta SubTy type SubTy;
950 [ 509 ta Ty type Ty;
951 CompletionItem { 510 fn func() fn func()
952 label: "C2", 511 me method() fn method(&self)
953 source_range: 221..221, 512 fn subfunc() fn subfunc()
954 delete: 221..221, 513 me submethod() fn submethod(&self)
955 insert: "C2", 514 "#]],
956 kind: Const,
957 detail: "const C2: ();",
958 },
959 CompletionItem {
960 label: "CONST",
961 source_range: 221..221,
962 delete: 221..221,
963 insert: "CONST",
964 kind: Const,
965 detail: "const CONST: u8;",
966 },
967 CompletionItem {
968 label: "SubTy",
969 source_range: 221..221,
970 delete: 221..221,
971 insert: "SubTy",
972 kind: TypeAlias,
973 detail: "type SubTy;",
974 },
975 CompletionItem {
976 label: "Ty",
977 source_range: 221..221,
978 delete: 221..221,
979 insert: "Ty",
980 kind: TypeAlias,
981 detail: "type Ty;",
982 },
983 CompletionItem {
984 label: "func()",
985 source_range: 221..221,
986 delete: 221..221,
987 insert: "func()$0",
988 kind: Function,
989 lookup: "func",
990 detail: "fn func()",
991 },
992 CompletionItem {
993 label: "method()",
994 source_range: 221..221,
995 delete: 221..221,
996 insert: "method()$0",
997 kind: Method,
998 lookup: "method",
999 detail: "fn method(&self)",
1000 },
1001 CompletionItem {
1002 label: "subfunc()",
1003 source_range: 221..221,
1004 delete: 221..221,
1005 insert: "subfunc()$0",
1006 kind: Function,
1007 lookup: "subfunc",
1008 detail: "fn subfunc()",
1009 },
1010 CompletionItem {
1011 label: "submethod()",
1012 source_range: 221..221,
1013 delete: 221..221,
1014 insert: "submethod()$0",
1015 kind: Method,
1016 lookup: "submethod",
1017 detail: "fn submethod(&self)",
1018 },
1019 ]
1020 "###
1021 ); 515 );
1022 } 516 }
1023 517
1024 #[test] 518 #[test]
1025 fn completes_self_param_assoc_ty() { 519 fn completes_self_param_assoc_ty() {
1026 assert_debug_snapshot!( 520 check(
1027 do_reference_completion( 521 r#"
1028 " 522trait Super {
1029 //- /lib.rs 523 type Ty;
1030 trait Super { 524 const CONST: u8 = 0;
1031 type Ty; 525 fn func() {}
1032 const CONST: u8 = 0; 526 fn method(&self) {}
1033 fn func() {} 527}
1034 fn method(&self) {}
1035 }
1036 528
1037 trait Sub: Super { 529trait Sub: Super {
1038 type SubTy; 530 type SubTy;
1039 const C2: () = (); 531 const C2: () = ();
1040 fn subfunc() {} 532 fn subfunc() {}
1041 fn submethod(&self) {} 533 fn submethod(&self) {}
1042 } 534}
1043 535
1044 struct Wrap<T>(T); 536struct Wrap<T>(T);
1045 impl<T> Super for Wrap<T> {} 537impl<T> Super for Wrap<T> {}
1046 impl<T> Sub for Wrap<T> { 538impl<T> Sub for Wrap<T> {
1047 fn subfunc() { 539 fn subfunc() {
1048 // Should be able to assume `Self: Sub + Super` 540 // Should be able to assume `Self: Sub + Super`
1049 Self::<|> 541 Self::<|>
1050 } 542 }
1051 } 543}
1052 " 544"#,
1053 ), 545 expect![[r#"
1054 @r###" 546 ct C2 const C2: () = ();
1055 [ 547 ct CONST const CONST: u8 = 0;
1056 CompletionItem { 548 ta SubTy type SubTy;
1057 label: "C2", 549 ta Ty type Ty;
1058 source_range: 367..367, 550 fn func() fn func()
1059 delete: 367..367, 551 me method() fn method(&self)
1060 insert: "C2", 552 fn subfunc() fn subfunc()
1061 kind: Const, 553 me submethod() fn submethod(&self)
1062 detail: "const C2: () = ();", 554 "#]],
1063 },
1064 CompletionItem {
1065 label: "CONST",
1066 source_range: 367..367,
1067 delete: 367..367,
1068 insert: "CONST",
1069 kind: Const,
1070 detail: "const CONST: u8 = 0;",
1071 },
1072 CompletionItem {
1073 label: "SubTy",
1074 source_range: 367..367,
1075 delete: 367..367,
1076 insert: "SubTy",
1077 kind: TypeAlias,
1078 detail: "type SubTy;",
1079 },
1080 CompletionItem {
1081 label: "Ty",
1082 source_range: 367..367,
1083 delete: 367..367,
1084 insert: "Ty",
1085 kind: TypeAlias,
1086 detail: "type Ty;",
1087 },
1088 CompletionItem {
1089 label: "func()",
1090 source_range: 367..367,
1091 delete: 367..367,
1092 insert: "func()$0",
1093 kind: Function,
1094 lookup: "func",
1095 detail: "fn func()",
1096 },
1097 CompletionItem {
1098 label: "method()",
1099 source_range: 367..367,
1100 delete: 367..367,
1101 insert: "method()$0",
1102 kind: Method,
1103 lookup: "method",
1104 detail: "fn method(&self)",
1105 },
1106 CompletionItem {
1107 label: "subfunc()",
1108 source_range: 367..367,
1109 delete: 367..367,
1110 insert: "subfunc()$0",
1111 kind: Function,
1112 lookup: "subfunc",
1113 detail: "fn subfunc()",
1114 },
1115 CompletionItem {
1116 label: "submethod()",
1117 source_range: 367..367,
1118 delete: 367..367,
1119 insert: "submethod()$0",
1120 kind: Method,
1121 lookup: "submethod",
1122 detail: "fn submethod(&self)",
1123 },
1124 ]
1125 "###
1126 ); 555 );
1127 } 556 }
1128 557
1129 #[test] 558 #[test]
1130 fn completes_type_alias() { 559 fn completes_type_alias() {
1131 assert_debug_snapshot!( 560 check(
1132 do_reference_completion( 561 r#"
1133 " 562struct S;
1134 struct S; 563impl S { fn foo() {} }
1135 impl S { fn foo() {} } 564type T = S;
1136 type T = S; 565impl T { fn bar() {} }
1137 impl T { fn bar() {} } 566
1138 567fn main() { T::<|>; }
1139 fn main() { 568"#,
1140 T::<|>; 569 expect![[r#"
1141 } 570 fn bar() fn bar()
1142 " 571 fn foo() fn foo()
1143 ), 572 "#]],
1144 @r###"
1145 [
1146 CompletionItem {
1147 label: "bar()",
1148 source_range: 88..88,
1149 delete: 88..88,
1150 insert: "bar()$0",
1151 kind: Function,
1152 lookup: "bar",
1153 detail: "fn bar()",
1154 },
1155 CompletionItem {
1156 label: "foo()",
1157 source_range: 88..88,
1158 delete: 88..88,
1159 insert: "foo()$0",
1160 kind: Function,
1161 lookup: "foo",
1162 detail: "fn foo()",
1163 },
1164 ]
1165 "###
1166 ); 573 );
1167 } 574 }
1168 575
1169 #[test] 576 #[test]
1170 fn completes_qualified_macros() { 577 fn completes_qualified_macros() {
1171 assert_debug_snapshot!( 578 check(
1172 do_reference_completion( 579 r#"
1173 " 580#[macro_export]
1174 #[macro_export] 581macro_rules! foo { () => {} }
1175 macro_rules! foo { 582
1176 () => {} 583fn main() { let _ = crate::<|> }
1177 } 584 "#,
585 expect![[r##"
586 ma foo!(…) #[macro_export]
587 macro_rules! foo
588 fn main() fn main()
589 "##]],
590 );
591 }
1178 592
1179 fn main() { 593 #[test]
1180 let _ = crate::<|> 594 fn test_super_super_completion() {
1181 } 595 check(
1182 " 596 r#"
1183 ), 597mod a {
1184 @r###" 598 const A: usize = 0;
1185 [ 599 mod b {
1186 CompletionItem { 600 const B: usize = 0;
1187 label: "foo!(…)", 601 mod c { use super::super::<|> }
1188 source_range: 82..82, 602 }
1189 delete: 82..82, 603}
1190 insert: "foo!($0)", 604"#,
1191 kind: Macro, 605 expect![[r#"
1192 detail: "#[macro_export]\nmacro_rules! foo", 606 ct A
1193 }, 607 md b
1194 CompletionItem { 608 "#]],
1195 label: "main()",
1196 source_range: 82..82,
1197 delete: 82..82,
1198 insert: "main()$0",
1199 kind: Function,
1200 lookup: "main",
1201 detail: "fn main()",
1202 },
1203 ]
1204 "###
1205 ); 609 );
1206 } 610 }
1207 611
1208 #[test] 612 #[test]
1209 fn completes_reexported_items_under_correct_name() { 613 fn completes_reexported_items_under_correct_name() {
1210 assert_debug_snapshot!( 614 check(
1211 do_reference_completion( 615 r#"
1212 r" 616fn foo() { self::m::<|> }
1213 fn foo() {
1214 self::m::<|>
1215 }
1216 617
1217 mod m { 618mod m {
1218 pub use super::p::wrong_fn as right_fn; 619 pub use super::p::wrong_fn as right_fn;
1219 pub use super::p::WRONG_CONST as RIGHT_CONST; 620 pub use super::p::WRONG_CONST as RIGHT_CONST;
1220 pub use super::p::WrongType as RightType; 621 pub use super::p::WrongType as RightType;
1221 } 622}
1222 mod p { 623mod p {
1223 fn wrong_fn() {} 624 fn wrong_fn() {}
1224 const WRONG_CONST: u32 = 1; 625 const WRONG_CONST: u32 = 1;
1225 struct WrongType {}; 626 struct WrongType {};
1226 } 627}
1227 " 628"#,
1228 ), 629 expect![[r#"
1229 @r###" 630 ct RIGHT_CONST
1230 [ 631 st RightType
1231 CompletionItem { 632 fn right_fn() fn wrong_fn()
1232 label: "RIGHT_CONST", 633 "#]],
1233 source_range: 24..24, 634 );
1234 delete: 24..24, 635
1235 insert: "RIGHT_CONST", 636 check_edit(
1236 kind: Const, 637 "RightType",
1237 }, 638 r#"
1238 CompletionItem { 639fn foo() { self::m::<|> }
1239 label: "RightType", 640
1240 source_range: 24..24, 641mod m {
1241 delete: 24..24, 642 pub use super::p::wrong_fn as right_fn;
1242 insert: "RightType", 643 pub use super::p::WRONG_CONST as RIGHT_CONST;
1243 kind: Struct, 644 pub use super::p::WrongType as RightType;
1244 }, 645}
1245 CompletionItem { 646mod p {
1246 label: "right_fn()", 647 fn wrong_fn() {}
1247 source_range: 24..24, 648 const WRONG_CONST: u32 = 1;
1248 delete: 24..24, 649 struct WrongType {};
1249 insert: "right_fn()$0", 650}
1250 kind: Function, 651"#,
1251 lookup: "right_fn", 652 r#"
1252 detail: "fn wrong_fn()", 653fn foo() { self::m::RightType }
1253 }, 654
1254 ] 655mod m {
1255 "### 656 pub use super::p::wrong_fn as right_fn;
657 pub use super::p::WRONG_CONST as RIGHT_CONST;
658 pub use super::p::WrongType as RightType;
659}
660mod p {
661 fn wrong_fn() {}
662 const WRONG_CONST: u32 = 1;
663 struct WrongType {};
664}
665"#,
1256 ); 666 );
1257 } 667 }
1258 668
1259 #[test] 669 #[test]
1260 fn completes_in_simple_macro_call() { 670 fn completes_in_simple_macro_call() {
1261 let completions = do_reference_completion( 671 check(
1262 r#" 672 r#"
1263 macro_rules! m { ($e:expr) => { $e } } 673macro_rules! m { ($e:expr) => { $e } }
1264 fn main() { m!(self::f<|>); } 674fn main() { m!(self::f<|>); }
1265 fn foo() {} 675fn foo() {}
1266 "#, 676"#,
677 expect![[r#"
678 fn foo() fn foo()
679 fn main() fn main()
680 "#]],
1267 ); 681 );
1268 assert_debug_snapshot!(completions, @r###"
1269 [
1270 CompletionItem {
1271 label: "foo()",
1272 source_range: 60..61,
1273 delete: 60..61,
1274 insert: "foo()$0",
1275 kind: Function,
1276 lookup: "foo",
1277 detail: "fn foo()",
1278 },
1279 CompletionItem {
1280 label: "main()",
1281 source_range: 60..61,
1282 delete: 60..61,
1283 insert: "main()$0",
1284 kind: Function,
1285 lookup: "main",
1286 detail: "fn main()",
1287 },
1288 ]
1289 "###);
1290 } 682 }
1291 683
1292 #[test] 684 #[test]
1293 fn function_mod_share_name() { 685 fn function_mod_share_name() {
1294 assert_debug_snapshot!( 686 check(
1295 do_reference_completion( 687 r#"
1296 r" 688fn foo() { self::m::<|> }
1297 fn foo() {
1298 self::m::<|>
1299 }
1300 689
1301 mod m { 690mod m {
1302 pub mod z {} 691 pub mod z {}
1303 pub fn z() {} 692 pub fn z() {}
1304 } 693}
1305 ", 694"#,
1306 ), 695 expect![[r#"
1307 @r###" 696 md z
1308 [ 697 fn z() pub fn z()
1309 CompletionItem { 698 "#]],
1310 label: "z",
1311 source_range: 24..24,
1312 delete: 24..24,
1313 insert: "z",
1314 kind: Module,
1315 },
1316 CompletionItem {
1317 label: "z()",
1318 source_range: 24..24,
1319 delete: 24..24,
1320 insert: "z()$0",
1321 kind: Function,
1322 lookup: "z",
1323 detail: "pub fn z()",
1324 },
1325 ]
1326 "###
1327 ); 699 );
1328 } 700 }
1329 701
1330 #[test] 702 #[test]
1331 fn completes_hashmap_new() { 703 fn completes_hashmap_new() {
1332 assert_debug_snapshot!( 704 check(
1333 do_reference_completion( 705 r#"
1334 r" 706struct RandomState;
1335 struct RandomState; 707struct HashMap<K, V, S = RandomState> {}
1336 struct HashMap<K, V, S = RandomState> {} 708
1337 709impl<K, V> HashMap<K, V, RandomState> {
1338 impl<K, V> HashMap<K, V, RandomState> { 710 pub fn new() -> HashMap<K, V, RandomState> { }
1339 pub fn new() -> HashMap<K, V, RandomState> { } 711}
1340 } 712fn foo() {
1341 fn foo() { 713 HashMap::<|>
1342 HashMap::<|> 714}
1343 } 715"#,
1344 " 716 expect![[r#"
1345 ), 717 fn new() pub fn new() -> HashMap<K, V, RandomState>
1346 @r###" 718 "#]],
1347 [
1348 CompletionItem {
1349 label: "new()",
1350 source_range: 179..179,
1351 delete: 179..179,
1352 insert: "new()$0",
1353 kind: Function,
1354 lookup: "new",
1355 detail: "pub fn new() -> HashMap<K, V, RandomState>",
1356 },
1357 ]
1358 "###
1359 ); 719 );
1360 } 720 }
1361 721
1362 #[test] 722 #[test]
1363 fn dont_complete_attr() { 723 fn dont_complete_attr() {
1364 assert_debug_snapshot!( 724 check(
1365 do_reference_completion( 725 r#"
1366 r" 726mod foo { pub struct Foo; }
1367 mod foo { pub struct Foo; } 727#[foo::<|>]
1368 #[foo::<|>] 728fn f() {}
1369 fn f() {} 729"#,
1370 " 730 expect![[""]],
1371 ), 731 );
1372 @r###"[]"###
1373 )
1374 } 732 }
1375} 733}
diff --git a/crates/ra_ide/src/completion/complete_record.rs b/crates/ra_ide/src/completion/complete_record.rs
index 13eb2f79f..74b94594d 100644
--- a/crates/ra_ide/src/completion/complete_record.rs
+++ b/crates/ra_ide/src/completion/complete_record.rs
@@ -18,389 +18,209 @@ pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
18 18
19#[cfg(test)] 19#[cfg(test)]
20mod tests { 20mod tests {
21 mod record_pat_tests { 21 use expect::{expect, Expect};
22 use insta::assert_debug_snapshot;
23 22
24 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 23 use crate::completion::{test_utils::completion_list, CompletionKind};
25 24
26 fn complete(code: &str) -> Vec<CompletionItem> { 25 fn check(ra_fixture: &str, expect: Expect) {
27 do_completion(code, CompletionKind::Reference) 26 let actual = completion_list(ra_fixture, CompletionKind::Reference);
28 } 27 expect.assert_eq(&actual);
29 28 }
30 #[test]
31 fn test_record_pattern_field() {
32 let completions = complete(
33 r"
34 struct S { foo: u32 }
35
36 fn process(f: S) {
37 match f {
38 S { f<|>: 92 } => (),
39 }
40 }
41 ",
42 );
43 assert_debug_snapshot!(completions, @r###"
44 [
45 CompletionItem {
46 label: "foo",
47 source_range: 68..69,
48 delete: 68..69,
49 insert: "foo",
50 kind: Field,
51 detail: "u32",
52 },
53 ]
54 "###);
55 }
56
57 #[test]
58 fn test_record_pattern_enum_variant() {
59 let completions = complete(
60 r"
61 enum E {
62 S { foo: u32, bar: () }
63 }
64
65 fn process(e: E) {
66 match e {
67 E::S { <|> } => (),
68 }
69 }
70 ",
71 );
72 assert_debug_snapshot!(completions, @r###"
73 [
74 CompletionItem {
75 label: "bar",
76 source_range: 88..88,
77 delete: 88..88,
78 insert: "bar",
79 kind: Field,
80 detail: "()",
81 },
82 CompletionItem {
83 label: "foo",
84 source_range: 88..88,
85 delete: 88..88,
86 insert: "foo",
87 kind: Field,
88 detail: "u32",
89 },
90 ]
91 "###);
92 }
93 29
94 #[test] 30 #[test]
95 fn test_record_pattern_field_in_simple_macro() { 31 fn test_record_pattern_field() {
96 let completions = complete( 32 check(
97 r" 33 r#"
98 macro_rules! m { ($e:expr) => { $e } } 34struct S { foo: u32 }
99 struct S { foo: u32 }
100 35
101 fn process(f: S) { 36fn process(f: S) {
102 m!(match f { 37 match f {
103 S { f<|>: 92 } => (), 38 S { f<|>: 92 } => (),
104 }) 39 }
105 } 40}
106 ", 41"#,
107 ); 42 expect![[r#"
108 assert_debug_snapshot!(completions, @r###" 43 fd foo u32
109 [ 44 "#]],
110 CompletionItem { 45 );
111 label: "foo", 46 }
112 source_range: 110..111,
113 delete: 110..111,
114 insert: "foo",
115 kind: Field,
116 detail: "u32",
117 },
118 ]
119 "###);
120 }
121 47
122 #[test] 48 #[test]
123 fn only_missing_fields_are_completed_in_destruct_pats() { 49 fn test_record_pattern_enum_variant() {
124 let completions = complete( 50 check(
125 r" 51 r#"
126 struct S { 52enum E { S { foo: u32, bar: () } }
127 foo1: u32,
128 foo2: u32,
129 bar: u32,
130 baz: u32,
131 }
132 53
133 fn main() { 54fn process(e: E) {
134 let s = S { 55 match e {
135 foo1: 1, 56 E::S { <|> } => (),
136 foo2: 2, 57 }
137 bar: 3, 58}
138 baz: 4, 59"#,
139 }; 60 expect![[r#"
140 if let S { foo1, foo2: a, <|> } = s {} 61 fd bar ()
141 } 62 fd foo u32
142 ", 63 "#]],
143 ); 64 );
144 assert_debug_snapshot!(completions, @r###"
145 [
146 CompletionItem {
147 label: "bar",
148 source_range: 203..203,
149 delete: 203..203,
150 insert: "bar",
151 kind: Field,
152 detail: "u32",
153 },
154 CompletionItem {
155 label: "baz",
156 source_range: 203..203,
157 delete: 203..203,
158 insert: "baz",
159 kind: Field,
160 detail: "u32",
161 },
162 ]
163 "###);
164 }
165 } 65 }
166 66
167 mod record_lit_tests { 67 #[test]
168 use insta::assert_debug_snapshot; 68 fn test_record_pattern_field_in_simple_macro() {
169 69 check(
170 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 70 r"
71macro_rules! m { ($e:expr) => { $e } }
72struct S { foo: u32 }
73
74fn process(f: S) {
75 m!(match f {
76 S { f<|>: 92 } => (),
77 })
78}
79",
80 expect![[r#"
81 fd foo u32
82 "#]],
83 );
84 }
171 85
172 fn complete(code: &str) -> Vec<CompletionItem> { 86 #[test]
173 do_completion(code, CompletionKind::Reference) 87 fn only_missing_fields_are_completed_in_destruct_pats() {
174 } 88 check(
89 r#"
90struct S {
91 foo1: u32, foo2: u32,
92 bar: u32, baz: u32,
93}
175 94
176 #[test] 95fn main() {
177 fn test_record_literal_deprecated_field() { 96 let s = S {
178 let completions = complete( 97 foo1: 1, foo2: 2,
179 r" 98 bar: 3, baz: 4,
180 struct A { 99 };
181 #[deprecated] 100 if let S { foo1, foo2: a, <|> } = s {}
182 the_field: u32, 101}
183 } 102"#,
184 fn foo() { 103 expect![[r#"
185 A { the<|> } 104 fd bar u32
186 } 105 fd baz u32
187 ", 106 "#]],
188 ); 107 );
189 assert_debug_snapshot!(completions, @r###" 108 }
190 [
191 CompletionItem {
192 label: "the_field",
193 source_range: 69..72,
194 delete: 69..72,
195 insert: "the_field",
196 kind: Field,
197 detail: "u32",
198 deprecated: true,
199 },
200 ]
201 "###);
202 }
203 109
204 #[test] 110 #[test]
205 fn test_record_literal_field() { 111 fn test_record_literal_field() {
206 let completions = complete( 112 check(
207 r" 113 r#"
208 struct A { the_field: u32 } 114struct A { the_field: u32 }
209 fn foo() { 115fn foo() {
210 A { the<|> } 116 A { the<|> }
211 } 117}
212 ", 118"#,
213 ); 119 expect![[r#"
214 assert_debug_snapshot!(completions, @r###" 120 fd the_field u32
215 [ 121 "#]],
216 CompletionItem { 122 );
217 label: "the_field", 123 }
218 source_range: 46..49,
219 delete: 46..49,
220 insert: "the_field",
221 kind: Field,
222 detail: "u32",
223 },
224 ]
225 "###);
226 }
227 124
228 #[test] 125 #[test]
229 fn test_record_literal_enum_variant() { 126 fn test_record_literal_enum_variant() {
230 let completions = complete( 127 check(
231 r" 128 r#"
232 enum E { 129enum E { A { a: u32 } }
233 A { a: u32 } 130fn foo() {
234 } 131 let _ = E::A { <|> }
235 fn foo() { 132}
236 let _ = E::A { <|> } 133"#,
237 } 134 expect![[r#"
238 ", 135 fd a u32
239 ); 136 "#]],
240 assert_debug_snapshot!(completions, @r###" 137 );
241 [ 138 }
242 CompletionItem {
243 label: "a",
244 source_range: 58..58,
245 delete: 58..58,
246 insert: "a",
247 kind: Field,
248 detail: "u32",
249 },
250 ]
251 "###);
252 }
253 139
254 #[test] 140 #[test]
255 fn test_record_literal_two_structs() { 141 fn test_record_literal_two_structs() {
256 let completions = complete( 142 check(
257 r" 143 r#"
258 struct A { a: u32 } 144struct A { a: u32 }
259 struct B { b: u32 } 145struct B { b: u32 }
260 146
261 fn foo() { 147fn foo() {
262 let _: A = B { <|> } 148 let _: A = B { <|> }
263 } 149}
264 ", 150"#,
265 ); 151 expect![[r#"
266 assert_debug_snapshot!(completions, @r###" 152 fd b u32
267 [ 153 "#]],
268 CompletionItem { 154 );
269 label: "b", 155 }
270 source_range: 70..70,
271 delete: 70..70,
272 insert: "b",
273 kind: Field,
274 detail: "u32",
275 },
276 ]
277 "###);
278 }
279 156
280 #[test] 157 #[test]
281 fn test_record_literal_generic_struct() { 158 fn test_record_literal_generic_struct() {
282 let completions = complete( 159 check(
283 r" 160 r#"
284 struct A<T> { a: T } 161struct A<T> { a: T }
285 162
286 fn foo() { 163fn foo() {
287 let _: A<u32> = A { <|> } 164 let _: A<u32> = A { <|> }
288 } 165}
289 ", 166"#,
290 ); 167 expect![[r#"
291 assert_debug_snapshot!(completions, @r###" 168 fd a u32
292 [ 169 "#]],
293 CompletionItem { 170 );
294 label: "a", 171 }
295 source_range: 56..56,
296 delete: 56..56,
297 insert: "a",
298 kind: Field,
299 detail: "u32",
300 },
301 ]
302 "###);
303 }
304 172
305 #[test] 173 #[test]
306 fn test_record_literal_field_in_simple_macro() { 174 fn test_record_literal_field_in_simple_macro() {
307 let completions = complete( 175 check(
308 r" 176 r#"
309 macro_rules! m { ($e:expr) => { $e } } 177macro_rules! m { ($e:expr) => { $e } }
310 struct A { the_field: u32 } 178struct A { the_field: u32 }
311 fn foo() { 179fn foo() {
312 m!(A { the<|> }) 180 m!(A { the<|> })
313 } 181}
314 ", 182"#,
315 ); 183 expect![[r#"
316 assert_debug_snapshot!(completions, @r###" 184 fd the_field u32
317 [ 185 "#]],
318 CompletionItem { 186 );
319 label: "the_field", 187 }
320 source_range: 88..91,
321 delete: 88..91,
322 insert: "the_field",
323 kind: Field,
324 detail: "u32",
325 },
326 ]
327 "###);
328 }
329 188
330 #[test] 189 #[test]
331 fn only_missing_fields_are_completed() { 190 fn only_missing_fields_are_completed() {
332 let completions = complete( 191 check(
333 r" 192 r#"
334 struct S { 193struct S {
335 foo1: u32, 194 foo1: u32, foo2: u32,
336 foo2: u32, 195 bar: u32, baz: u32,
337 bar: u32, 196}
338 baz: u32,
339 }
340 197
341 fn main() { 198fn main() {
342 let foo1 = 1; 199 let foo1 = 1;
343 let s = S { 200 let s = S { foo1, foo2: 5, <|> }
344 foo1, 201}
345 foo2: 5, 202"#,
346 <|> 203 expect![[r#"
347 } 204 fd bar u32
348 } 205 fd baz u32
349 ", 206 "#]],
350 ); 207 );
351 assert_debug_snapshot!(completions, @r###" 208 }
352 [
353 CompletionItem {
354 label: "bar",
355 source_range: 157..157,
356 delete: 157..157,
357 insert: "bar",
358 kind: Field,
359 detail: "u32",
360 },
361 CompletionItem {
362 label: "baz",
363 source_range: 157..157,
364 delete: 157..157,
365 insert: "baz",
366 kind: Field,
367 detail: "u32",
368 },
369 ]
370 "###);
371 }
372 209
373 #[test] 210 #[test]
374 fn completes_functional_update() { 211 fn completes_functional_update() {
375 let completions = complete( 212 check(
376 r" 213 r#"
377 struct S { 214struct S { foo1: u32, foo2: u32 }
378 foo1: u32,
379 foo2: u32,
380 }
381 215
382 fn main() { 216fn main() {
383 let foo1 = 1; 217 let foo1 = 1;
384 let s = S { 218 let s = S { foo1, <|> .. loop {} }
385 foo1, 219}
386 <|> 220"#,
387 .. loop {} 221 expect![[r#"
388 } 222 fd foo2 u32
389 } 223 "#]],
390 ", 224 );
391 );
392 assert_debug_snapshot!(completions, @r###"
393 [
394 CompletionItem {
395 label: "foo2",
396 source_range: 112..112,
397 delete: 112..112,
398 insert: "foo2",
399 kind: Field,
400 detail: "u32",
401 },
402 ]
403 "###);
404 }
405 } 225 }
406} 226}
diff --git a/crates/ra_ide/src/completion/complete_snippet.rs b/crates/ra_ide/src/completion/complete_snippet.rs
index 52aaa70f0..28d8f7876 100644
--- a/crates/ra_ide/src/completion/complete_snippet.rs
+++ b/crates/ra_ide/src/completion/complete_snippet.rs
@@ -70,95 +70,47 @@ fn ${1:feature}() {
70 70
71#[cfg(test)] 71#[cfg(test)]
72mod tests { 72mod tests {
73 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 73 use expect::{expect, Expect};
74 use insta::assert_debug_snapshot;
75 74
76 fn do_snippet_completion(code: &str) -> Vec<CompletionItem> { 75 use crate::completion::{test_utils::completion_list, CompletionKind};
77 do_completion(code, CompletionKind::Snippet) 76
77 fn check(ra_fixture: &str, expect: Expect) {
78 let actual = completion_list(ra_fixture, CompletionKind::Snippet);
79 expect.assert_eq(&actual)
78 } 80 }
79 81
80 #[test] 82 #[test]
81 fn completes_snippets_in_expressions() { 83 fn completes_snippets_in_expressions() {
82 assert_debug_snapshot!( 84 check(
83 do_snippet_completion(r"fn foo(x: i32) { <|> }"), 85 r#"fn foo(x: i32) { <|> }"#,
84 @r###" 86 expect![[r#"
85 [ 87 sn pd
86 CompletionItem { 88 sn ppd
87 label: "pd", 89 "#]],
88 source_range: 17..17, 90 );
89 delete: 17..17,
90 insert: "eprintln!(\"$0 = {:?}\", $0);",
91 kind: Snippet,
92 },
93 CompletionItem {
94 label: "ppd",
95 source_range: 17..17,
96 delete: 17..17,
97 insert: "eprintln!(\"$0 = {:#?}\", $0);",
98 kind: Snippet,
99 },
100 ]
101 "###
102 );
103 } 91 }
104 92
105 #[test] 93 #[test]
106 fn should_not_complete_snippets_in_path() { 94 fn should_not_complete_snippets_in_path() {
107 assert_debug_snapshot!( 95 check(r#"fn foo(x: i32) { ::foo<|> }"#, expect![[""]]);
108 do_snippet_completion(r"fn foo(x: i32) { ::foo<|> }"), 96 check(r#"fn foo(x: i32) { ::<|> }"#, expect![[""]]);
109 @"[]"
110 );
111 assert_debug_snapshot!(
112 do_snippet_completion(r"fn foo(x: i32) { ::<|> }"),
113 @"[]"
114 );
115 } 97 }
116 98
117 #[test] 99 #[test]
118 fn completes_snippets_in_items() { 100 fn completes_snippets_in_items() {
119 assert_debug_snapshot!( 101 check(
120 do_snippet_completion( 102 r#"
121 r" 103#[cfg(test)]
122 #[cfg(test)] 104mod tests {
123 mod tests { 105 <|>
124 <|> 106}
125 } 107"#,
126 " 108 expect![[r#"
127 ), 109 sn Test function
128 @r###" 110 sn Test module
129 [ 111 sn macro_rules
130 CompletionItem { 112 sn pub(crate)
131 label: "Test function", 113 "#]],
132 source_range: 29..29, 114 )
133 delete: 29..29,
134 insert: "#[test]\nfn ${1:feature}() {\n $0\n}",
135 kind: Snippet,
136 lookup: "tfn",
137 },
138 CompletionItem {
139 label: "Test module",
140 source_range: 29..29,
141 delete: 29..29,
142 insert: "#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn ${1:test_name}() {\n $0\n }\n}",
143 kind: Snippet,
144 lookup: "tmod",
145 },
146 CompletionItem {
147 label: "macro_rules",
148 source_range: 29..29,
149 delete: 29..29,
150 insert: "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}",
151 kind: Snippet,
152 },
153 CompletionItem {
154 label: "pub(crate)",
155 source_range: 29..29,
156 delete: 29..29,
157 insert: "pub(crate) $0",
158 kind: Snippet,
159 },
160 ]
161 "###
162 );
163 } 115 }
164} 116}
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index 23e42928d..d9a0ef167 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -2,8 +2,8 @@
2//! 2//!
3//! This module adds the completion items related to implementing associated 3//! This module adds the completion items related to implementing associated
4//! items within a `impl Trait for Struct` block. The current context node 4//! items within a `impl Trait for Struct` block. The current context node
5//! must be within either a `FN_DEF`, `TYPE_ALIAS_DEF`, or `CONST_DEF` node 5//! must be within either a `FN`, `TYPE_ALIAS`, or `CONST` node
6//! and an direct child of an `IMPL_DEF`. 6//! and an direct child of an `IMPL`.
7//! 7//!
8//! # Examples 8//! # Examples
9//! 9//!
@@ -34,7 +34,7 @@
34use hir::{self, Docs, HasSource}; 34use hir::{self, Docs, HasSource};
35use ra_assists::utils::get_missing_assoc_items; 35use ra_assists::utils::get_missing_assoc_items;
36use ra_syntax::{ 36use ra_syntax::{
37 ast::{self, edit, ImplDef}, 37 ast::{self, edit, Impl},
38 AstNode, SyntaxKind, SyntaxNode, TextRange, T, 38 AstNode, SyntaxKind, SyntaxNode, TextRange, T,
39}; 39};
40use ra_text_edit::TextEdit; 40use ra_text_edit::TextEdit;
@@ -43,7 +43,7 @@ use crate::{
43 completion::{ 43 completion::{
44 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, 44 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
45 }, 45 },
46 display::FunctionSignature, 46 display::function_declaration,
47}; 47};
48 48
49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { 49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
@@ -63,7 +63,7 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
63 } 63 }
64 }), 64 }),
65 65
66 SyntaxKind::FN_DEF => { 66 SyntaxKind::FN => {
67 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def) 67 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
68 .into_iter() 68 .into_iter()
69 .filter_map(|item| match item { 69 .filter_map(|item| match item {
@@ -75,7 +75,7 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
75 } 75 }
76 } 76 }
77 77
78 SyntaxKind::TYPE_ALIAS_DEF => { 78 SyntaxKind::TYPE_ALIAS => {
79 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def) 79 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
80 .into_iter() 80 .into_iter()
81 .filter_map(|item| match item { 81 .filter_map(|item| match item {
@@ -87,7 +87,7 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
87 } 87 }
88 } 88 }
89 89
90 SyntaxKind::CONST_DEF => { 90 SyntaxKind::CONST => {
91 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def) 91 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
92 .into_iter() 92 .into_iter()
93 .filter_map(|item| match item { 93 .filter_map(|item| match item {
@@ -104,18 +104,17 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
104 } 104 }
105} 105}
106 106
107fn completion_match(ctx: &CompletionContext) -> Option<(SyntaxNode, ImplDef)> { 107fn completion_match(ctx: &CompletionContext) -> Option<(SyntaxNode, Impl)> {
108 let (trigger, impl_def_offset) = ctx.token.ancestors().find_map(|p| match p.kind() { 108 let (trigger, impl_def_offset) = ctx.token.ancestors().find_map(|p| match p.kind() {
109 SyntaxKind::FN_DEF 109 SyntaxKind::FN | SyntaxKind::TYPE_ALIAS | SyntaxKind::CONST | SyntaxKind::BLOCK_EXPR => {
110 | SyntaxKind::TYPE_ALIAS_DEF 110 Some((p, 2))
111 | SyntaxKind::CONST_DEF 111 }
112 | SyntaxKind::BLOCK_EXPR => Some((p, 2)),
113 SyntaxKind::NAME_REF => Some((p, 5)), 112 SyntaxKind::NAME_REF => Some((p, 5)),
114 _ => None, 113 _ => None,
115 })?; 114 })?;
116 let impl_def = (0..impl_def_offset - 1) 115 let impl_def = (0..impl_def_offset - 1)
117 .try_fold(trigger.parent()?, |t, _| t.parent()) 116 .try_fold(trigger.parent()?, |t, _| t.parent())
118 .and_then(ast::ImplDef::cast)?; 117 .and_then(ast::Impl::cast)?;
119 Some((trigger, impl_def)) 118 Some((trigger, impl_def))
120} 119}
121 120
@@ -125,8 +124,6 @@ fn add_function_impl(
125 ctx: &CompletionContext, 124 ctx: &CompletionContext,
126 func: hir::Function, 125 func: hir::Function,
127) { 126) {
128 let signature = FunctionSignature::from_hir(ctx.db, func);
129
130 let fn_name = func.name(ctx.db).to_string(); 127 let fn_name = func.name(ctx.db).to_string();
131 128
132 let label = if !func.params(ctx.db).is_empty() { 129 let label = if !func.params(ctx.db).is_empty() {
@@ -146,13 +143,14 @@ fn add_function_impl(
146 }; 143 };
147 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end()); 144 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end());
148 145
146 let function_decl = function_declaration(&func.source(ctx.db).value);
149 match ctx.config.snippet_cap { 147 match ctx.config.snippet_cap {
150 Some(cap) => { 148 Some(cap) => {
151 let snippet = format!("{} {{\n $0\n}}", signature); 149 let snippet = format!("{} {{\n $0\n}}", function_decl);
152 builder.snippet_edit(cap, TextEdit::replace(range, snippet)) 150 builder.snippet_edit(cap, TextEdit::replace(range, snippet))
153 } 151 }
154 None => { 152 None => {
155 let header = format!("{} {{", signature); 153 let header = format!("{} {{", function_decl);
156 builder.text_edit(TextEdit::replace(range, header)) 154 builder.text_edit(TextEdit::replace(range, header))
157 } 155 }
158 } 156 }
@@ -202,7 +200,7 @@ fn add_const_impl(
202 } 200 }
203} 201}
204 202
205fn make_const_compl_syntax(const_: &ast::ConstDef) -> String { 203fn make_const_compl_syntax(const_: &ast::Const) -> String {
206 let const_ = edit::remove_attrs_and_docs(const_); 204 let const_ = edit::remove_attrs_and_docs(const_);
207 205
208 let const_start = const_.syntax().text_range().start(); 206 let const_start = const_.syntax().text_range().start();
@@ -227,330 +225,264 @@ fn make_const_compl_syntax(const_: &ast::ConstDef) -> String {
227 225
228#[cfg(test)] 226#[cfg(test)]
229mod tests { 227mod tests {
230 use insta::assert_debug_snapshot; 228 use expect::{expect, Expect};
231 229
232 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 230 use crate::completion::{
231 test_utils::{check_edit, completion_list},
232 CompletionKind,
233 };
233 234
234 fn complete(code: &str) -> Vec<CompletionItem> { 235 fn check(ra_fixture: &str, expect: Expect) {
235 do_completion(code, CompletionKind::Magic) 236 let actual = completion_list(ra_fixture, CompletionKind::Magic);
237 expect.assert_eq(&actual)
236 } 238 }
237 239
238 #[test] 240 #[test]
239 fn name_ref_function_type_const() { 241 fn name_ref_function_type_const() {
240 let completions = complete( 242 check(
241 r" 243 r#"
242 trait Test { 244trait Test {
243 type TestType; 245 type TestType;
244 const TEST_CONST: u16; 246 const TEST_CONST: u16;
245 fn test(); 247 fn test();
246 } 248}
247 249struct T;
248 struct T1;
249 250
250 impl Test for T1 { 251impl Test for T {
251 t<|> 252 t<|>
252 } 253}
253 ", 254"#,
255 expect![["
256ct const TEST_CONST: u16 = \n\
257fn fn test()
258ta type TestType = \n\
259 "]],
254 ); 260 );
255 assert_debug_snapshot!(completions, @r###"
256 [
257 CompletionItem {
258 label: "const TEST_CONST: u16 = ",
259 source_range: 112..113,
260 delete: 112..113,
261 insert: "const TEST_CONST: u16 = ",
262 kind: Const,
263 lookup: "TEST_CONST",
264 },
265 CompletionItem {
266 label: "fn test()",
267 source_range: 112..113,
268 delete: 112..113,
269 insert: "fn test() {\n $0\n}",
270 kind: Function,
271 lookup: "test",
272 },
273 CompletionItem {
274 label: "type TestType = ",
275 source_range: 112..113,
276 delete: 112..113,
277 insert: "type TestType = ",
278 kind: TypeAlias,
279 lookup: "TestType",
280 },
281 ]
282 "###);
283 } 261 }
284 262
285 #[test] 263 #[test]
286 fn no_nested_fn_completions() { 264 fn no_nested_fn_completions() {
287 let completions = complete( 265 check(
288 r" 266 r"
289 trait Test { 267trait Test {
290 fn test(); 268 fn test();
291 fn test2(); 269 fn test2();
292 } 270}
293 271struct T;
294 struct T1;
295 272
296 impl Test for T1 { 273impl Test for T {
297 fn test() { 274 fn test() {
298 t<|> 275 t<|>
299 } 276 }
300 } 277}
301 ", 278",
279 expect![[""]],
302 ); 280 );
303 assert_debug_snapshot!(completions, @r###"[]"###);
304 } 281 }
305 282
306 #[test] 283 #[test]
307 fn name_ref_single_function() { 284 fn name_ref_single_function() {
308 let completions = complete( 285 check_edit(
309 r" 286 "test",
310 trait Test { 287 r#"
311 fn test(); 288trait Test {
312 } 289 fn test();
290}
291struct T;
313 292
314 struct T1; 293impl Test for T {
294 t<|>
295}
296"#,
297 r#"
298trait Test {
299 fn test();
300}
301struct T;
315 302
316 impl Test for T1 { 303impl Test for T {
317 t<|> 304 fn test() {
318 } 305 $0
319 ", 306}
307}
308"#,
320 ); 309 );
321 assert_debug_snapshot!(completions, @r###"
322 [
323 CompletionItem {
324 label: "fn test()",
325 source_range: 66..67,
326 delete: 66..67,
327 insert: "fn test() {\n $0\n}",
328 kind: Function,
329 lookup: "test",
330 },
331 ]
332 "###);
333 } 310 }
334 311
335 #[test] 312 #[test]
336 fn single_function() { 313 fn single_function() {
337 let completions = complete( 314 check_edit(
338 r" 315 "test",
339 trait Test { 316 r#"
340 fn foo(); 317trait Test {
341 } 318 fn test();
319}
320struct T;
342 321
343 struct T1; 322impl Test for T {
323 fn t<|>
324}
325"#,
326 r#"
327trait Test {
328 fn test();
329}
330struct T;
344 331
345 impl Test for T1 { 332impl Test for T {
346 fn f<|> 333 fn test() {
347 } 334 $0
348 ", 335}
336}
337"#,
349 ); 338 );
350 assert_debug_snapshot!(completions, @r###"
351 [
352 CompletionItem {
353 label: "fn foo()",
354 source_range: 68..69,
355 delete: 65..69,
356 insert: "fn foo() {\n $0\n}",
357 kind: Function,
358 lookup: "foo",
359 },
360 ]
361 "###);
362 } 339 }
363 340
364 #[test] 341 #[test]
365 fn hide_implemented_fn() { 342 fn hide_implemented_fn() {
366 let completions = complete( 343 check(
367 r" 344 r#"
368 trait Test { 345trait Test {
369 fn foo(); 346 fn foo();
370 fn foo_bar(); 347 fn foo_bar();
371 } 348}
372 349struct T;
373 struct T1;
374
375 impl Test for T1 {
376 fn foo() {}
377
378 fn f<|>
379 }
380 ",
381 );
382 assert_debug_snapshot!(completions, @r###"
383 [
384 CompletionItem {
385 label: "fn foo_bar()",
386 source_range: 103..104,
387 delete: 100..104,
388 insert: "fn foo_bar() {\n $0\n}",
389 kind: Function,
390 lookup: "foo_bar",
391 },
392 ]
393 "###);
394 }
395
396 #[test]
397 fn completes_only_on_top_level() {
398 let completions = complete(
399 r"
400 trait Test {
401 fn foo();
402
403 fn foo_bar();
404 }
405
406 struct T1;
407 350
408 impl Test for T1 { 351impl Test for T {
409 fn foo() { 352 fn foo() {}
410 <|> 353 fn f<|>
411 } 354}
412 } 355"#,
413 ", 356 expect![[r#"
357 fn fn foo_bar()
358 "#]],
414 ); 359 );
415 assert_debug_snapshot!(completions, @r###"[]"###);
416 } 360 }
417 361
418 #[test] 362 #[test]
419 fn generic_fn() { 363 fn generic_fn() {
420 let completions = complete( 364 check_edit(
421 r" 365 "foo",
422 trait Test { 366 r#"
423 fn foo<T>(); 367trait Test {
424 } 368 fn foo<T>();
369}
370struct T;
425 371
426 struct T1; 372impl Test for T {
373 fn f<|>
374}
375"#,
376 r#"
377trait Test {
378 fn foo<T>();
379}
380struct T;
427 381
428 impl Test for T1 { 382impl Test for T {
429 fn f<|> 383 fn foo<T>() {
430 } 384 $0
431 ", 385}
386}
387"#,
432 ); 388 );
433 assert_debug_snapshot!(completions, @r###" 389 check_edit(
434 [ 390 "foo",
435 CompletionItem { 391 r#"
436 label: "fn foo()", 392trait Test {
437 source_range: 71..72, 393 fn foo<T>() where T: Into<String>;
438 delete: 68..72, 394}
439 insert: "fn foo<T>() {\n $0\n}", 395struct T;
440 kind: Function,
441 lookup: "foo",
442 },
443 ]
444 "###);
445 }
446
447 #[test]
448 fn generic_constrait_fn() {
449 let completions = complete(
450 r"
451 trait Test {
452 fn foo<T>() where T: Into<String>;
453 }
454 396
455 struct T1; 397impl Test for T {
398 fn f<|>
399}
400"#,
401 r#"
402trait Test {
403 fn foo<T>() where T: Into<String>;
404}
405struct T;
456 406
457 impl Test for T1 { 407impl Test for T {
458 fn f<|> 408 fn foo<T>()
459 } 409where T: Into<String> {
460 ", 410 $0
411}
412}
413"#,
461 ); 414 );
462 assert_debug_snapshot!(completions, @r###"
463 [
464 CompletionItem {
465 label: "fn foo()",
466 source_range: 93..94,
467 delete: 90..94,
468 insert: "fn foo<T>()\nwhere T: Into<String> {\n $0\n}",
469 kind: Function,
470 lookup: "foo",
471 },
472 ]
473 "###);
474 } 415 }
475 416
476 #[test] 417 #[test]
477 fn associated_type() { 418 fn associated_type() {
478 let completions = complete( 419 check_edit(
479 r" 420 "SomeType",
480 trait Test { 421 r#"
481 type SomeType; 422trait Test {
482 } 423 type SomeType;
424}
483 425
484 impl Test for () { 426impl Test for () {
485 type S<|> 427 type S<|>
486 } 428}
487 ", 429"#,
430 "
431trait Test {
432 type SomeType;
433}
434
435impl Test for () {
436 type SomeType = \n\
437}
438",
488 ); 439 );
489 assert_debug_snapshot!(completions, @r###"
490 [
491 CompletionItem {
492 label: "type SomeType = ",
493 source_range: 63..64,
494 delete: 58..64,
495 insert: "type SomeType = ",
496 kind: TypeAlias,
497 lookup: "SomeType",
498 },
499 ]
500 "###);
501 } 440 }
502 441
503 #[test] 442 #[test]
504 fn associated_const() { 443 fn associated_const() {
505 let completions = complete( 444 check_edit(
506 r" 445 "SOME_CONST",
507 trait Test { 446 r#"
508 const SOME_CONST: u16; 447trait Test {
509 } 448 const SOME_CONST: u16;
449}
510 450
511 impl Test for () { 451impl Test for () {
512 const S<|> 452 const S<|>
513 } 453}
514 ", 454"#,
455 "
456trait Test {
457 const SOME_CONST: u16;
458}
459
460impl Test for () {
461 const SOME_CONST: u16 = \n\
462}
463",
515 ); 464 );
516 assert_debug_snapshot!(completions, @r###"
517 [
518 CompletionItem {
519 label: "const SOME_CONST: u16 = ",
520 source_range: 72..73,
521 delete: 66..73,
522 insert: "const SOME_CONST: u16 = ",
523 kind: Const,
524 lookup: "SOME_CONST",
525 },
526 ]
527 "###);
528 }
529 465
530 #[test] 466 check_edit(
531 fn associated_const_with_default() { 467 "SOME_CONST",
532 let completions = complete( 468 r#"
533 r" 469trait Test {
534 trait Test { 470 const SOME_CONST: u16 = 92;
535 const SOME_CONST: u16 = 42; 471}
536 }
537 472
538 impl Test for () { 473impl Test for () {
539 const S<|> 474 const S<|>
540 } 475}
541 ", 476"#,
477 "
478trait Test {
479 const SOME_CONST: u16 = 92;
480}
481
482impl Test for () {
483 const SOME_CONST: u16 = \n\
484}
485",
542 ); 486 );
543 assert_debug_snapshot!(completions, @r###"
544 [
545 CompletionItem {
546 label: "const SOME_CONST: u16 = ",
547 source_range: 77..78,
548 delete: 71..78,
549 insert: "const SOME_CONST: u16 = ",
550 kind: Const,
551 lookup: "SOME_CONST",
552 },
553 ]
554 "###);
555 } 487 }
556} 488}
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index a0a04bb58..bd9551f35 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -1,11 +1,10 @@
1//! Completion of names from the current scope, e.g. locals and imported items. 1//! Completion of names from the current scope, e.g. locals and imported items.
2 2
3use hir::ScopeDef; 3use hir::{Adt, ModuleDef, ScopeDef, Type};
4use ra_syntax::AstNode;
4use test_utils::mark; 5use test_utils::mark;
5 6
6use crate::completion::{CompletionContext, Completions}; 7use crate::completion::{CompletionContext, Completions};
7use hir::{Adt, ModuleDef, Type};
8use ra_syntax::AstNode;
9 8
10pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 9pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
11 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { 10 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
@@ -26,7 +25,7 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
26 return; 25 return;
27 } 26 }
28 27
29 ctx.scope().process_all_names(&mut |name, res| { 28 ctx.scope.process_all_names(&mut |name, res| {
30 if ctx.use_item_syntax.is_some() { 29 if ctx.use_item_syntax.is_some() {
31 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { 30 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
32 if name_ref.syntax().text() == name.to_string().as_str() { 31 if name_ref.syntax().text() == name.to_string().as_str() {
@@ -43,7 +42,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
43 if let Some(Adt::Enum(enum_data)) = ty.as_adt() { 42 if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
44 let variants = enum_data.variants(ctx.db); 43 let variants = enum_data.variants(ctx.db);
45 44
46 let module = if let Some(module) = ctx.scope().module() { 45 let module = if let Some(module) = ctx.scope.module() {
47 // Compute path from the completion site if available. 46 // Compute path from the completion site if available.
48 module 47 module
49 } else { 48 } else {
@@ -65,1361 +64,595 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
65 64
66#[cfg(test)] 65#[cfg(test)]
67mod tests { 66mod tests {
68 use insta::assert_debug_snapshot; 67 use expect::{expect, Expect};
69 use test_utils::mark; 68 use test_utils::mark;
70 69
71 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 70 use crate::completion::{
71 test_utils::{check_edit, completion_list},
72 CompletionKind,
73 };
72 74
73 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> { 75 fn check(ra_fixture: &str, expect: Expect) {
74 do_completion(ra_fixture, CompletionKind::Reference) 76 let actual = completion_list(ra_fixture, CompletionKind::Reference);
77 expect.assert_eq(&actual)
75 } 78 }
76 79
77 #[test] 80 #[test]
78 fn self_fulfilling_completion() { 81 fn self_fulfilling_completion() {
79 mark::check!(self_fulfilling_completion); 82 mark::check!(self_fulfilling_completion);
80 assert_debug_snapshot!( 83 check(
81 do_reference_completion( 84 r#"
82 r#" 85use foo<|>
83 use foo<|> 86use std::collections;
84 use std::collections; 87"#,
85 "#, 88 expect![[r#"
86 ), 89 ?? collections
87 @r###" 90 "#]],
88 [
89 CompletionItem {
90 label: "collections",
91 source_range: 4..7,
92 delete: 4..7,
93 insert: "collections",
94 },
95 ]
96 "###
97 ); 91 );
98 } 92 }
99 93
100 #[test] 94 #[test]
101 fn bind_pat_and_path_ignore_at() { 95 fn bind_pat_and_path_ignore_at() {
102 assert_debug_snapshot!( 96 check(
103 do_reference_completion( 97 r#"
104 r" 98enum Enum { A, B }
105 enum Enum { 99fn quux(x: Option<Enum>) {
106 A, 100 match x {
107 B, 101 None => (),
108 } 102 Some(en<|> @ Enum::A) => (),
109 fn quux(x: Option<Enum>) { 103 }
110 match x { 104}
111 None => (), 105"#,
112 Some(en<|> @ Enum::A) => (), 106 expect![[""]],
113 }
114 }
115 "
116 ),
117 @"[]"
118 ); 107 );
119 } 108 }
120 109
121 #[test] 110 #[test]
122 fn bind_pat_and_path_ignore_ref() { 111 fn bind_pat_and_path_ignore_ref() {
123 assert_debug_snapshot!( 112 check(
124 do_reference_completion( 113 r#"
125 r" 114enum Enum { A, B }
126 enum Enum { 115fn quux(x: Option<Enum>) {
127 A, 116 match x {
128 B, 117 None => (),
129 } 118 Some(ref en<|>) => (),
130 fn quux(x: Option<Enum>) { 119 }
131 match x { 120}
132 None => (), 121"#,
133 Some(ref en<|>) => (), 122 expect![[""]],
134 }
135 }
136 "
137 ),
138 @r###"[]"###
139 ); 123 );
140 } 124 }
141 125
142 #[test] 126 #[test]
143 fn bind_pat_and_path() { 127 fn bind_pat_and_path() {
144 assert_debug_snapshot!( 128 check(
145 do_reference_completion( 129 r#"
146 r" 130enum Enum { A, B }
147 enum Enum { 131fn quux(x: Option<Enum>) {
148 A, 132 match x {
149 B, 133 None => (),
150 } 134 Some(En<|>) => (),
151 fn quux(x: Option<Enum>) { 135 }
152 match x { 136}
153 None => (), 137"#,
154 Some(En<|>) => (), 138 expect![[r#"
155 } 139 en Enum
156 } 140 "#]],
157 "
158 ),
159 @r###"
160 [
161 CompletionItem {
162 label: "Enum",
163 source_range: 102..104,
164 delete: 102..104,
165 insert: "Enum",
166 kind: Enum,
167 },
168 ]
169 "###
170 ); 141 );
171 } 142 }
172 143
173 #[test] 144 #[test]
174 fn completes_bindings_from_let() { 145 fn completes_bindings_from_let() {
175 assert_debug_snapshot!( 146 check(
176 do_reference_completion( 147 r#"
177 r" 148fn quux(x: i32) {
178 fn quux(x: i32) { 149 let y = 92;
179 let y = 92; 150 1 + <|>;
180 1 + <|>; 151 let z = ();
181 let z = (); 152}
182 } 153"#,
183 " 154 expect![[r#"
184 ), 155 fn quux(…) fn quux(x: i32)
185 @r###" 156 bn x i32
186 [ 157 bn y i32
187 CompletionItem { 158 "#]],
188 label: "quux(…)",
189 source_range: 42..42,
190 delete: 42..42,
191 insert: "quux(${1:x})$0",
192 kind: Function,
193 lookup: "quux",
194 detail: "fn quux(x: i32)",
195 trigger_call_info: true,
196 },
197 CompletionItem {
198 label: "x",
199 source_range: 42..42,
200 delete: 42..42,
201 insert: "x",
202 kind: Binding,
203 detail: "i32",
204 },
205 CompletionItem {
206 label: "y",
207 source_range: 42..42,
208 delete: 42..42,
209 insert: "y",
210 kind: Binding,
211 detail: "i32",
212 },
213 ]
214 "###
215 ); 159 );
216 } 160 }
217 161
218 #[test] 162 #[test]
219 fn completes_bindings_from_if_let() { 163 fn completes_bindings_from_if_let() {
220 assert_debug_snapshot!( 164 check(
221 do_reference_completion( 165 r#"
222 r" 166fn quux() {
223 fn quux() { 167 if let Some(x) = foo() {
224 if let Some(x) = foo() { 168 let y = 92;
225 let y = 92; 169 };
226 }; 170 if let Some(a) = bar() {
227 if let Some(a) = bar() { 171 let b = 62;
228 let b = 62; 172 1 + <|>
229 1 + <|> 173 }
230 } 174}
231 } 175"#,
232 " 176 expect![[r#"
233 ), 177 bn a
234 @r###" 178 bn b i32
235 [ 179 fn quux() fn quux()
236 CompletionItem { 180 "#]],
237 label: "a",
238 source_range: 129..129,
239 delete: 129..129,
240 insert: "a",
241 kind: Binding,
242 },
243 CompletionItem {
244 label: "b",
245 source_range: 129..129,
246 delete: 129..129,
247 insert: "b",
248 kind: Binding,
249 detail: "i32",
250 },
251 CompletionItem {
252 label: "quux()",
253 source_range: 129..129,
254 delete: 129..129,
255 insert: "quux()$0",
256 kind: Function,
257 lookup: "quux",
258 detail: "fn quux()",
259 },
260 ]
261 "###
262 ); 181 );
263 } 182 }
264 183
265 #[test] 184 #[test]
266 fn completes_bindings_from_for() { 185 fn completes_bindings_from_for() {
267 assert_debug_snapshot!( 186 check(
268 do_reference_completion( 187 r#"
269 r" 188fn quux() {
270 fn quux() { 189 for x in &[1, 2, 3] { <|> }
271 for x in &[1, 2, 3] { 190}
272 <|> 191"#,
273 } 192 expect![[r#"
274 } 193 fn quux() fn quux()
275 " 194 bn x
276 ), 195 "#]],
277 @r###"
278 [
279 CompletionItem {
280 label: "quux()",
281 source_range: 46..46,
282 delete: 46..46,
283 insert: "quux()$0",
284 kind: Function,
285 lookup: "quux",
286 detail: "fn quux()",
287 },
288 CompletionItem {
289 label: "x",
290 source_range: 46..46,
291 delete: 46..46,
292 insert: "x",
293 kind: Binding,
294 },
295 ]
296 "###
297 ); 196 );
298 } 197 }
299 198
300 #[test] 199 #[test]
301 fn completes_bindings_from_for_with_in_prefix() { 200 fn completes_if_prefix_is_keyword() {
302 mark::check!(completes_bindings_from_for_with_in_prefix); 201 mark::check!(completes_if_prefix_is_keyword);
303 assert_debug_snapshot!( 202 check_edit(
304 do_reference_completion( 203 "wherewolf",
305 r" 204 r#"
306 fn test() { 205fn main() {
307 for index in &[1, 2, 3] { 206 let wherewolf = 92;
308 let t = in<|> 207 drop(where<|>)
309 } 208}
310 } 209"#,
311 " 210 r#"
312 ), 211fn main() {
313 @r###" 212 let wherewolf = 92;
314 [ 213 drop(wherewolf)
315 CompletionItem { 214}
316 label: "index", 215"#,
317 source_range: 58..58, 216 )
318 delete: 58..58,
319 insert: "index",
320 kind: Binding,
321 },
322 CompletionItem {
323 label: "test()",
324 source_range: 58..58,
325 delete: 58..58,
326 insert: "test()$0",
327 kind: Function,
328 lookup: "test",
329 detail: "fn test()",
330 },
331 ]
332 "###
333 );
334 } 217 }
335 218
336 #[test] 219 #[test]
337 fn completes_generic_params() { 220 fn completes_generic_params() {
338 assert_debug_snapshot!( 221 check(
339 do_reference_completion( 222 r#"fn quux<T>() { <|> }"#,
340 r" 223 expect![[r#"
341 fn quux<T>() { 224 tp T
342 <|> 225 fn quux() fn quux<T>()
343 } 226 "#]],
344 "
345 ),
346 @r###"
347 [
348 CompletionItem {
349 label: "T",
350 source_range: 19..19,
351 delete: 19..19,
352 insert: "T",
353 kind: TypeParam,
354 },
355 CompletionItem {
356 label: "quux()",
357 source_range: 19..19,
358 delete: 19..19,
359 insert: "quux()$0",
360 kind: Function,
361 lookup: "quux",
362 detail: "fn quux<T>()",
363 },
364 ]
365 "###
366 ); 227 );
367 } 228 }
368 229
369 #[test] 230 #[test]
370 fn completes_generic_params_in_struct() { 231 fn completes_generic_params_in_struct() {
371 assert_debug_snapshot!( 232 check(
372 do_reference_completion( 233 r#"struct S<T> { x: <|>}"#,
373 r" 234 expect![[r#"
374 struct X<T> { 235 st S<…>
375 x: <|> 236 tp Self
376 } 237 tp T
377 " 238 "#]],
378 ),
379 @r###"
380 [
381 CompletionItem {
382 label: "Self",
383 source_range: 21..21,
384 delete: 21..21,
385 insert: "Self",
386 kind: TypeParam,
387 },
388 CompletionItem {
389 label: "T",
390 source_range: 21..21,
391 delete: 21..21,
392 insert: "T",
393 kind: TypeParam,
394 },
395 CompletionItem {
396 label: "X<…>",
397 source_range: 21..21,
398 delete: 21..21,
399 insert: "X<$0>",
400 kind: Struct,
401 lookup: "X",
402 },
403 ]
404 "###
405 ); 239 );
406 } 240 }
407 241
408 #[test] 242 #[test]
409 fn completes_self_in_enum() { 243 fn completes_self_in_enum() {
410 assert_debug_snapshot!( 244 check(
411 do_reference_completion( 245 r#"enum X { Y(<|>) }"#,
412 r" 246 expect![[r#"
413 enum X { 247 tp Self
414 Y(<|>) 248 en X
415 } 249 "#]],
416 "
417 ),
418 @r###"
419 [
420 CompletionItem {
421 label: "Self",
422 source_range: 15..15,
423 delete: 15..15,
424 insert: "Self",
425 kind: TypeParam,
426 },
427 CompletionItem {
428 label: "X",
429 source_range: 15..15,
430 delete: 15..15,
431 insert: "X",
432 kind: Enum,
433 },
434 ]
435 "###
436 ); 250 );
437 } 251 }
438 252
439 #[test] 253 #[test]
440 fn completes_module_items() { 254 fn completes_module_items() {
441 assert_debug_snapshot!( 255 check(
442 do_reference_completion( 256 r#"
443 r" 257struct S;
444 struct Foo; 258enum E {}
445 enum Baz {} 259fn quux() { <|> }
446 fn quux() { 260"#,
447 <|> 261 expect![[r#"
448 } 262 en E
449 " 263 st S
450 ), 264 fn quux() fn quux()
451 @r###" 265 "#]],
452 [ 266 );
453 CompletionItem {
454 label: "Baz",
455 source_range: 40..40,
456 delete: 40..40,
457 insert: "Baz",
458 kind: Enum,
459 },
460 CompletionItem {
461 label: "Foo",
462 source_range: 40..40,
463 delete: 40..40,
464 insert: "Foo",
465 kind: Struct,
466 },
467 CompletionItem {
468 label: "quux()",
469 source_range: 40..40,
470 delete: 40..40,
471 insert: "quux()$0",
472 kind: Function,
473 lookup: "quux",
474 detail: "fn quux()",
475 },
476 ]
477 "###
478 );
479 } 267 }
480 268
481 #[test] 269 #[test]
482 fn completes_extern_prelude() { 270 fn completes_extern_prelude() {
483 assert_debug_snapshot!( 271 check(
484 do_reference_completion( 272 r#"
485 r" 273//- /lib.rs
486 //- /lib.rs 274use <|>;
487 use <|>; 275
488 276//- /other_crate/lib.rs
489 //- /other_crate/lib.rs 277// nothing here
490 // nothing here 278"#,
491 " 279 expect![[r#"
492 ), 280 md other_crate
493 @r###" 281 "#]],
494 [
495 CompletionItem {
496 label: "other_crate",
497 source_range: 4..4,
498 delete: 4..4,
499 insert: "other_crate",
500 kind: Module,
501 },
502 ]
503 "###
504 ); 282 );
505 } 283 }
506 284
507 #[test] 285 #[test]
508 fn completes_module_items_in_nested_modules() { 286 fn completes_module_items_in_nested_modules() {
509 assert_debug_snapshot!( 287 check(
510 do_reference_completion( 288 r#"
511 r" 289struct Foo;
512 struct Foo; 290mod m {
513 mod m { 291 struct Bar;
514 struct Bar; 292 fn quux() { <|> }
515 fn quux() { <|> } 293}
516 } 294"#,
517 " 295 expect![[r#"
518 ), 296 st Bar
519 @r###" 297 fn quux() fn quux()
520 [ 298 "#]],
521 CompletionItem {
522 label: "Bar",
523 source_range: 52..52,
524 delete: 52..52,
525 insert: "Bar",
526 kind: Struct,
527 },
528 CompletionItem {
529 label: "quux()",
530 source_range: 52..52,
531 delete: 52..52,
532 insert: "quux()$0",
533 kind: Function,
534 lookup: "quux",
535 detail: "fn quux()",
536 },
537 ]
538 "###
539 ); 299 );
540 } 300 }
541 301
542 #[test] 302 #[test]
543 fn completes_return_type() { 303 fn completes_return_type() {
544 assert_debug_snapshot!( 304 check(
545 do_reference_completion( 305 r#"
546 r" 306struct Foo;
547 struct Foo; 307fn x() -> <|>
548 fn x() -> <|> 308"#,
549 " 309 expect![[r#"
550 ), 310 st Foo
551 @r###" 311 fn x() fn x()
552 [ 312 "#]],
553 CompletionItem {
554 label: "Foo",
555 source_range: 22..22,
556 delete: 22..22,
557 insert: "Foo",
558 kind: Struct,
559 },
560 CompletionItem {
561 label: "x()",
562 source_range: 22..22,
563 delete: 22..22,
564 insert: "x()$0",
565 kind: Function,
566 lookup: "x",
567 detail: "fn x()",
568 },
569 ]
570 "###
571 ); 313 );
572 } 314 }
573 315
574 #[test] 316 #[test]
575 fn dont_show_both_completions_for_shadowing() { 317 fn dont_show_both_completions_for_shadowing() {
576 assert_debug_snapshot!( 318 check(
577 do_reference_completion( 319 r#"
578 r" 320fn foo() {
579 fn foo() { 321 let bar = 92;
580 let bar = 92; 322 {
581 { 323 let bar = 62;
582 let bar = 62; 324 drop(<|>)
583 <|> 325 }
584 } 326}
585 } 327"#,
586 " 328 // FIXME: should be only one bar here
587 ), 329 expect![[r#"
588 @r###" 330 bn bar i32
589 [ 331 bn bar i32
590 CompletionItem { 332 fn foo() fn foo()
591 label: "bar", 333 "#]],
592 source_range: 65..65,
593 delete: 65..65,
594 insert: "bar",
595 kind: Binding,
596 detail: "i32",
597 },
598 CompletionItem {
599 label: "foo()",
600 source_range: 65..65,
601 delete: 65..65,
602 insert: "foo()$0",
603 kind: Function,
604 lookup: "foo",
605 detail: "fn foo()",
606 },
607 ]
608 "###
609 ); 334 );
610 } 335 }
611 336
612 #[test] 337 #[test]
613 fn completes_self_in_methods() { 338 fn completes_self_in_methods() {
614 assert_debug_snapshot!( 339 check(
615 do_reference_completion(r"impl S { fn foo(&self) { <|> } }"), 340 r#"impl S { fn foo(&self) { <|> } }"#,
616 @r###" 341 expect![[r#"
617 [ 342 tp Self
618 CompletionItem { 343 bn self &{unknown}
619 label: "Self", 344 "#]],
620 source_range: 25..25,
621 delete: 25..25,
622 insert: "Self",
623 kind: TypeParam,
624 },
625 CompletionItem {
626 label: "self",
627 source_range: 25..25,
628 delete: 25..25,
629 insert: "self",
630 kind: Binding,
631 detail: "&{unknown}",
632 },
633 ]
634 "###
635 ); 345 );
636 } 346 }
637 347
638 #[test] 348 #[test]
639 fn completes_prelude() { 349 fn completes_prelude() {
640 assert_debug_snapshot!( 350 check(
641 do_reference_completion( 351 r#"
642 " 352//- /main.rs
643 //- /main.rs 353fn foo() { let x: <|> }
644 fn foo() { let x: <|> } 354
645 355//- /std/lib.rs
646 //- /std/lib.rs 356#[prelude_import]
647 #[prelude_import] 357use prelude::*;
648 use prelude::*; 358
649 359mod prelude { struct Option; }
650 mod prelude { 360"#,
651 struct Option; 361 expect![[r#"
652 } 362 st Option
653 " 363 fn foo() fn foo()
654 ), 364 md std
655 @r###" 365 "#]],
656 [
657 CompletionItem {
658 label: "Option",
659 source_range: 18..18,
660 delete: 18..18,
661 insert: "Option",
662 kind: Struct,
663 },
664 CompletionItem {
665 label: "foo()",
666 source_range: 18..18,
667 delete: 18..18,
668 insert: "foo()$0",
669 kind: Function,
670 lookup: "foo",
671 detail: "fn foo()",
672 },
673 CompletionItem {
674 label: "std",
675 source_range: 18..18,
676 delete: 18..18,
677 insert: "std",
678 kind: Module,
679 },
680 ]
681 "###
682 ); 366 );
683 } 367 }
684 368
685 #[test] 369 #[test]
686 fn completes_std_prelude_if_core_is_defined() { 370 fn completes_std_prelude_if_core_is_defined() {
687 assert_debug_snapshot!( 371 check(
688 do_reference_completion( 372 r#"
689 " 373//- /main.rs
690 //- /main.rs 374fn foo() { let x: <|> }
691 fn foo() { let x: <|> } 375
692 376//- /core/lib.rs
693 //- /core/lib.rs 377#[prelude_import]
694 #[prelude_import] 378use prelude::*;
695 use prelude::*; 379
696 380mod prelude { struct Option; }
697 mod prelude { 381
698 struct Option; 382//- /std/lib.rs
699 } 383#[prelude_import]
700 384use prelude::*;
701 //- /std/lib.rs 385
702 #[prelude_import] 386mod prelude { struct String; }
703 use prelude::*; 387"#,
704 388 expect![[r#"
705 mod prelude { 389 st String
706 struct String; 390 md core
707 } 391 fn foo() fn foo()
708 " 392 md std
709 ), 393 "#]],
710 @r###"
711 [
712 CompletionItem {
713 label: "String",
714 source_range: 18..18,
715 delete: 18..18,
716 insert: "String",
717 kind: Struct,
718 },
719 CompletionItem {
720 label: "core",
721 source_range: 18..18,
722 delete: 18..18,
723 insert: "core",
724 kind: Module,
725 },
726 CompletionItem {
727 label: "foo()",
728 source_range: 18..18,
729 delete: 18..18,
730 insert: "foo()$0",
731 kind: Function,
732 lookup: "foo",
733 detail: "fn foo()",
734 },
735 CompletionItem {
736 label: "std",
737 source_range: 18..18,
738 delete: 18..18,
739 insert: "std",
740 kind: Module,
741 },
742 ]
743 "###
744 ); 394 );
745 } 395 }
746 396
747 #[test] 397 #[test]
748 fn completes_macros_as_value() { 398 fn completes_macros_as_value() {
749 assert_debug_snapshot!( 399 check(
750 do_reference_completion( 400 r#"
751 " 401macro_rules! foo { () => {} }
752 //- /main.rs
753 macro_rules! foo {
754 () => {}
755 }
756 402
757 #[macro_use] 403#[macro_use]
758 mod m1 { 404mod m1 {
759 macro_rules! bar { 405 macro_rules! bar { () => {} }
760 () => {} 406}
761 }
762 }
763 407
764 mod m2 { 408mod m2 {
765 macro_rules! nope { 409 macro_rules! nope { () => {} }
766 () => {}
767 }
768 410
769 #[macro_export] 411 #[macro_export]
770 macro_rules! baz { 412 macro_rules! baz { () => {} }
771 () => {} 413}
772 }
773 }
774 414
775 fn main() { 415fn main() { let v = <|> }
776 let v = <|> 416"#,
777 } 417 expect![[r##"
778 " 418 ma bar!(…) macro_rules! bar
779 ), 419 ma baz!(…) #[macro_export]
780 @r###" 420 macro_rules! baz
781 [ 421 ma foo!(…) macro_rules! foo
782 CompletionItem { 422 md m1
783 label: "bar!(…)", 423 md m2
784 source_range: 256..256, 424 fn main() fn main()
785 delete: 256..256, 425 "##]],
786 insert: "bar!($0)",
787 kind: Macro,
788 detail: "macro_rules! bar",
789 },
790 CompletionItem {
791 label: "baz!(…)",
792 source_range: 256..256,
793 delete: 256..256,
794 insert: "baz!($0)",
795 kind: Macro,
796 detail: "#[macro_export]\nmacro_rules! baz",
797 },
798 CompletionItem {
799 label: "foo!(…)",
800 source_range: 256..256,
801 delete: 256..256,
802 insert: "foo!($0)",
803 kind: Macro,
804 detail: "macro_rules! foo",
805 },
806 CompletionItem {
807 label: "m1",
808 source_range: 256..256,
809 delete: 256..256,
810 insert: "m1",
811 kind: Module,
812 },
813 CompletionItem {
814 label: "m2",
815 source_range: 256..256,
816 delete: 256..256,
817 insert: "m2",
818 kind: Module,
819 },
820 CompletionItem {
821 label: "main()",
822 source_range: 256..256,
823 delete: 256..256,
824 insert: "main()$0",
825 kind: Function,
826 lookup: "main",
827 detail: "fn main()",
828 },
829 ]
830 "###
831 ); 426 );
832 } 427 }
833 428
834 #[test] 429 #[test]
835 fn completes_both_macro_and_value() { 430 fn completes_both_macro_and_value() {
836 assert_debug_snapshot!( 431 check(
837 do_reference_completion( 432 r#"
838 " 433macro_rules! foo { () => {} }
839 //- /main.rs 434fn foo() { <|> }
840 macro_rules! foo { 435"#,
841 () => {} 436 expect![[r#"
842 } 437 ma foo!(…) macro_rules! foo
843 438 fn foo() fn foo()
844 fn foo() { 439 "#]],
845 <|>
846 }
847 "
848 ),
849 @r###"
850 [
851 CompletionItem {
852 label: "foo!(…)",
853 source_range: 50..50,
854 delete: 50..50,
855 insert: "foo!($0)",
856 kind: Macro,
857 detail: "macro_rules! foo",
858 },
859 CompletionItem {
860 label: "foo()",
861 source_range: 50..50,
862 delete: 50..50,
863 insert: "foo()$0",
864 kind: Function,
865 lookup: "foo",
866 detail: "fn foo()",
867 },
868 ]
869 "###
870 ); 440 );
871 } 441 }
872 442
873 #[test] 443 #[test]
874 fn completes_macros_as_type() { 444 fn completes_macros_as_type() {
875 assert_debug_snapshot!( 445 check(
876 do_reference_completion( 446 r#"
877 " 447macro_rules! foo { () => {} }
878 //- /main.rs 448fn main() { let x: <|> }
879 macro_rules! foo { 449"#,
880 () => {} 450 expect![[r#"
881 } 451 ma foo!(…) macro_rules! foo
882 452 fn main() fn main()
883 fn main() { 453 "#]],
884 let x: <|>
885 }
886 "
887 ),
888 @r###"
889 [
890 CompletionItem {
891 label: "foo!(…)",
892 source_range: 58..58,
893 delete: 58..58,
894 insert: "foo!($0)",
895 kind: Macro,
896 detail: "macro_rules! foo",
897 },
898 CompletionItem {
899 label: "main()",
900 source_range: 58..58,
901 delete: 58..58,
902 insert: "main()$0",
903 kind: Function,
904 lookup: "main",
905 detail: "fn main()",
906 },
907 ]
908 "###
909 ); 454 );
910 } 455 }
911 456
912 #[test] 457 #[test]
913 fn completes_macros_as_stmt() { 458 fn completes_macros_as_stmt() {
914 assert_debug_snapshot!( 459 check(
915 do_reference_completion( 460 r#"
916 " 461macro_rules! foo { () => {} }
917 //- /main.rs 462fn main() { <|> }
918 macro_rules! foo { 463"#,
919 () => {} 464 expect![[r#"
920 } 465 ma foo!(…) macro_rules! foo
921 466 fn main() fn main()
922 fn main() { 467 "#]],
923 <|>
924 }
925 "
926 ),
927 @r###"
928 [
929 CompletionItem {
930 label: "foo!(…)",
931 source_range: 51..51,
932 delete: 51..51,
933 insert: "foo!($0)",
934 kind: Macro,
935 detail: "macro_rules! foo",
936 },
937 CompletionItem {
938 label: "main()",
939 source_range: 51..51,
940 delete: 51..51,
941 insert: "main()$0",
942 kind: Function,
943 lookup: "main",
944 detail: "fn main()",
945 },
946 ]
947 "###
948 ); 468 );
949 } 469 }
950 470
951 #[test] 471 #[test]
952 fn completes_local_item() { 472 fn completes_local_item() {
953 assert_debug_snapshot!( 473 check(
954 do_reference_completion( 474 r#"
955 " 475fn main() {
956 //- /main.rs 476 return f<|>;
957 fn main() { 477 fn frobnicate() {}
958 return f<|>; 478}
959 fn frobnicate() {} 479"#,
960 } 480 expect![[r#"
961 " 481 fn frobnicate() fn frobnicate()
962 ), 482 fn main() fn main()
963 @r###" 483 "#]],
964 [ 484 );
965 CompletionItem {
966 label: "frobnicate()",
967 source_range: 23..24,
968 delete: 23..24,
969 insert: "frobnicate()$0",
970 kind: Function,
971 lookup: "frobnicate",
972 detail: "fn frobnicate()",
973 },
974 CompletionItem {
975 label: "main()",
976 source_range: 23..24,
977 delete: 23..24,
978 insert: "main()$0",
979 kind: Function,
980 lookup: "main",
981 detail: "fn main()",
982 },
983 ]
984 "###
985 )
986 } 485 }
987 486
988 #[test] 487 #[test]
989 fn completes_in_simple_macro_1() { 488 fn completes_in_simple_macro_1() {
990 assert_debug_snapshot!( 489 check(
991 do_reference_completion( 490 r#"
992 r" 491macro_rules! m { ($e:expr) => { $e } }
993 macro_rules! m { ($e:expr) => { $e } } 492fn quux(x: i32) {
994 fn quux(x: i32) { 493 let y = 92;
995 let y = 92; 494 m!(<|>);
996 m!(<|>); 495}
997 } 496"#,
998 " 497 expect![[r#"
999 ), 498 ma m!(…) macro_rules! m
1000 @r###" 499 fn quux(…) fn quux(x: i32)
1001 [ 500 bn x i32
1002 CompletionItem { 501 bn y i32
1003 label: "m!(…)", 502 "#]],
1004 source_range: 80..80,
1005 delete: 80..80,
1006 insert: "m!($0)",
1007 kind: Macro,
1008 detail: "macro_rules! m",
1009 },
1010 CompletionItem {
1011 label: "quux(…)",
1012 source_range: 80..80,
1013 delete: 80..80,
1014 insert: "quux(${1:x})$0",
1015 kind: Function,
1016 lookup: "quux",
1017 detail: "fn quux(x: i32)",
1018 trigger_call_info: true,
1019 },
1020 CompletionItem {
1021 label: "x",
1022 source_range: 80..80,
1023 delete: 80..80,
1024 insert: "x",
1025 kind: Binding,
1026 detail: "i32",
1027 },
1028 CompletionItem {
1029 label: "y",
1030 source_range: 80..80,
1031 delete: 80..80,
1032 insert: "y",
1033 kind: Binding,
1034 detail: "i32",
1035 },
1036 ]
1037 "###
1038 ); 503 );
1039 } 504 }
1040 505
1041 #[test] 506 #[test]
1042 fn completes_in_simple_macro_2() { 507 fn completes_in_simple_macro_2() {
1043 assert_debug_snapshot!( 508 check(
1044 do_reference_completion( 509 r"
1045 r" 510macro_rules! m { ($e:expr) => { $e } }
1046 macro_rules! m { ($e:expr) => { $e } } 511fn quux(x: i32) {
1047 fn quux(x: i32) { 512 let y = 92;
1048 let y = 92; 513 m!(x<|>);
1049 m!(x<|>); 514}
1050 } 515",
1051 " 516 expect![[r#"
1052 ), 517 ma m!(…) macro_rules! m
1053 @r###" 518 fn quux(…) fn quux(x: i32)
1054 [ 519 bn x i32
1055 CompletionItem { 520 bn y i32
1056 label: "m!(…)", 521 "#]],
1057 source_range: 80..81,
1058 delete: 80..81,
1059 insert: "m!($0)",
1060 kind: Macro,
1061 detail: "macro_rules! m",
1062 },
1063 CompletionItem {
1064 label: "quux(…)",
1065 source_range: 80..81,
1066 delete: 80..81,
1067 insert: "quux(${1:x})$0",
1068 kind: Function,
1069 lookup: "quux",
1070 detail: "fn quux(x: i32)",
1071 trigger_call_info: true,
1072 },
1073 CompletionItem {
1074 label: "x",
1075 source_range: 80..81,
1076 delete: 80..81,
1077 insert: "x",
1078 kind: Binding,
1079 detail: "i32",
1080 },
1081 CompletionItem {
1082 label: "y",
1083 source_range: 80..81,
1084 delete: 80..81,
1085 insert: "y",
1086 kind: Binding,
1087 detail: "i32",
1088 },
1089 ]
1090 "###
1091 ); 522 );
1092 } 523 }
1093 524
1094 #[test] 525 #[test]
1095 fn completes_in_simple_macro_without_closing_parens() { 526 fn completes_in_simple_macro_without_closing_parens() {
1096 assert_debug_snapshot!( 527 check(
1097 do_reference_completion( 528 r#"
1098 r" 529macro_rules! m { ($e:expr) => { $e } }
1099 macro_rules! m { ($e:expr) => { $e } } 530fn quux(x: i32) {
1100 fn quux(x: i32) { 531 let y = 92;
1101 let y = 92; 532 m!(x<|>
1102 m!(x<|> 533}
1103 } 534"#,
1104 " 535 expect![[r#"
1105 ), 536 ma m!(…) macro_rules! m
1106 @r###" 537 fn quux(…) fn quux(x: i32)
1107 [ 538 bn x i32
1108 CompletionItem { 539 bn y i32
1109 label: "m!(…)", 540 "#]],
1110 source_range: 80..81,
1111 delete: 80..81,
1112 insert: "m!($0)",
1113 kind: Macro,
1114 detail: "macro_rules! m",
1115 },
1116 CompletionItem {
1117 label: "quux(…)",
1118 source_range: 80..81,
1119 delete: 80..81,
1120 insert: "quux(${1:x})$0",
1121 kind: Function,
1122 lookup: "quux",
1123 detail: "fn quux(x: i32)",
1124 trigger_call_info: true,
1125 },
1126 CompletionItem {
1127 label: "x",
1128 source_range: 80..81,
1129 delete: 80..81,
1130 insert: "x",
1131 kind: Binding,
1132 detail: "i32",
1133 },
1134 CompletionItem {
1135 label: "y",
1136 source_range: 80..81,
1137 delete: 80..81,
1138 insert: "y",
1139 kind: Binding,
1140 detail: "i32",
1141 },
1142 ]
1143 "###
1144 ); 541 );
1145 } 542 }
1146 543
1147 #[test] 544 #[test]
1148 fn completes_unresolved_uses() { 545 fn completes_unresolved_uses() {
1149 assert_debug_snapshot!( 546 check(
1150 do_reference_completion( 547 r#"
1151 r" 548use spam::Quux;
1152 use spam::Quux; 549
1153 550fn main() { <|> }
1154 fn main() { 551"#,
1155 <|> 552 expect![[r#"
1156 } 553 ?? Quux
1157 " 554 fn main() fn main()
1158 ), 555 "#]],
1159 @r###"
1160 [
1161 CompletionItem {
1162 label: "Quux",
1163 source_range: 33..33,
1164 delete: 33..33,
1165 insert: "Quux",
1166 },
1167 CompletionItem {
1168 label: "main()",
1169 source_range: 33..33,
1170 delete: 33..33,
1171 insert: "main()$0",
1172 kind: Function,
1173 lookup: "main",
1174 detail: "fn main()",
1175 },
1176 ]
1177 "###
1178 ); 556 );
1179 } 557 }
1180 #[test] 558 #[test]
1181 fn completes_enum_variant_matcharm() { 559 fn completes_enum_variant_matcharm() {
1182 assert_debug_snapshot!( 560 check(
1183 do_reference_completion( 561 r#"
1184 r" 562enum Foo { Bar, Baz, Quux }
1185 enum Foo {
1186 Bar,
1187 Baz,
1188 Quux
1189 }
1190
1191 fn main() {
1192 let foo = Foo::Quux;
1193 563
1194 match foo { 564fn main() {
1195 Qu<|> 565 let foo = Foo::Quux;
1196 } 566 match foo { Qu<|> }
1197 } 567}
1198 " 568"#,
1199 ), 569 expect![[r#"
1200 @r###" 570 en Foo
1201 [ 571 ev Foo::Bar ()
1202 CompletionItem { 572 ev Foo::Baz ()
1203 label: "Foo", 573 ev Foo::Quux ()
1204 source_range: 103..105, 574 "#]],
1205 delete: 103..105,
1206 insert: "Foo",
1207 kind: Enum,
1208 },
1209 CompletionItem {
1210 label: "Foo::Bar",
1211 source_range: 103..105,
1212 delete: 103..105,
1213 insert: "Foo::Bar",
1214 kind: EnumVariant,
1215 lookup: "Bar",
1216 detail: "()",
1217 },
1218 CompletionItem {
1219 label: "Foo::Baz",
1220 source_range: 103..105,
1221 delete: 103..105,
1222 insert: "Foo::Baz",
1223 kind: EnumVariant,
1224 lookup: "Baz",
1225 detail: "()",
1226 },
1227 CompletionItem {
1228 label: "Foo::Quux",
1229 source_range: 103..105,
1230 delete: 103..105,
1231 insert: "Foo::Quux",
1232 kind: EnumVariant,
1233 lookup: "Quux",
1234 detail: "()",
1235 },
1236 ]
1237 "###
1238 ) 575 )
1239 } 576 }
1240 577
1241 #[test] 578 #[test]
1242 fn completes_enum_variant_iflet() { 579 fn completes_enum_variant_iflet() {
1243 assert_debug_snapshot!( 580 check(
1244 do_reference_completion( 581 r#"
1245 r" 582enum Foo { Bar, Baz, Quux }
1246 enum Foo {
1247 Bar,
1248 Baz,
1249 Quux
1250 }
1251 583
1252 fn main() { 584fn main() {
1253 let foo = Foo::Quux; 585 let foo = Foo::Quux;
1254 586 if let Qu<|> = foo { }
1255 if let Qu<|> = foo { 587}
1256 588"#,
1257 } 589 expect![[r#"
1258 } 590 en Foo
1259 " 591 ev Foo::Bar ()
1260 ), 592 ev Foo::Baz ()
1261 @r###" 593 ev Foo::Quux ()
1262 [ 594 "#]],
1263 CompletionItem {
1264 label: "Foo",
1265 source_range: 90..92,
1266 delete: 90..92,
1267 insert: "Foo",
1268 kind: Enum,
1269 },
1270 CompletionItem {
1271 label: "Foo::Bar",
1272 source_range: 90..92,
1273 delete: 90..92,
1274 insert: "Foo::Bar",
1275 kind: EnumVariant,
1276 lookup: "Bar",
1277 detail: "()",
1278 },
1279 CompletionItem {
1280 label: "Foo::Baz",
1281 source_range: 90..92,
1282 delete: 90..92,
1283 insert: "Foo::Baz",
1284 kind: EnumVariant,
1285 lookup: "Baz",
1286 detail: "()",
1287 },
1288 CompletionItem {
1289 label: "Foo::Quux",
1290 source_range: 90..92,
1291 delete: 90..92,
1292 insert: "Foo::Quux",
1293 kind: EnumVariant,
1294 lookup: "Quux",
1295 detail: "()",
1296 },
1297 ]
1298 "###
1299 ) 595 )
1300 } 596 }
1301 597
1302 #[test] 598 #[test]
1303 fn completes_enum_variant_basic_expr() { 599 fn completes_enum_variant_basic_expr() {
1304 assert_debug_snapshot!( 600 check(
1305 do_reference_completion( 601 r#"
1306 r" 602enum Foo { Bar, Baz, Quux }
1307 enum Foo { 603fn main() { let foo: Foo = Q<|> }
1308 Bar, 604"#,
1309 Baz, 605 expect![[r#"
1310 Quux 606 en Foo
1311 } 607 ev Foo::Bar ()
1312 608 ev Foo::Baz ()
1313 fn main() { 609 ev Foo::Quux ()
1314 let foo: Foo = Q<|> 610 fn main() fn main()
1315 } 611 "#]],
1316 "
1317 ),
1318 @r###"
1319 [
1320 CompletionItem {
1321 label: "Foo",
1322 source_range: 72..73,
1323 delete: 72..73,
1324 insert: "Foo",
1325 kind: Enum,
1326 },
1327 CompletionItem {
1328 label: "Foo::Bar",
1329 source_range: 72..73,
1330 delete: 72..73,
1331 insert: "Foo::Bar",
1332 kind: EnumVariant,
1333 lookup: "Bar",
1334 detail: "()",
1335 },
1336 CompletionItem {
1337 label: "Foo::Baz",
1338 source_range: 72..73,
1339 delete: 72..73,
1340 insert: "Foo::Baz",
1341 kind: EnumVariant,
1342 lookup: "Baz",
1343 detail: "()",
1344 },
1345 CompletionItem {
1346 label: "Foo::Quux",
1347 source_range: 72..73,
1348 delete: 72..73,
1349 insert: "Foo::Quux",
1350 kind: EnumVariant,
1351 lookup: "Quux",
1352 detail: "()",
1353 },
1354 CompletionItem {
1355 label: "main()",
1356 source_range: 72..73,
1357 delete: 72..73,
1358 insert: "main()$0",
1359 kind: Function,
1360 lookup: "main",
1361 detail: "fn main()",
1362 },
1363 ]
1364 "###
1365 ) 612 )
1366 } 613 }
1367 614
1368 #[test] 615 #[test]
1369 fn completes_enum_variant_from_module() { 616 fn completes_enum_variant_from_module() {
1370 assert_debug_snapshot!( 617 check(
1371 do_reference_completion( 618 r#"
1372 r" 619mod m { pub enum E { V } }
1373 mod m { pub enum E { V } } 620fn f() -> m::E { V<|> }
1374 621"#,
1375 fn f() -> m::E { 622 expect![[r#"
1376 V<|> 623 fn f() fn f() -> m::E
1377 } 624 md m
1378 " 625 ev m::E::V ()
1379 ), 626 "#]],
1380 @r###"
1381 [
1382 CompletionItem {
1383 label: "f()",
1384 source_range: 49..50,
1385 delete: 49..50,
1386 insert: "f()$0",
1387 kind: Function,
1388 lookup: "f",
1389 detail: "fn f() -> m::E",
1390 },
1391 CompletionItem {
1392 label: "m",
1393 source_range: 49..50,
1394 delete: 49..50,
1395 insert: "m",
1396 kind: Module,
1397 },
1398 CompletionItem {
1399 label: "m::E::V",
1400 source_range: 49..50,
1401 delete: 49..50,
1402 insert: "m::E::V",
1403 kind: EnumVariant,
1404 lookup: "V",
1405 detail: "()",
1406 },
1407 ]
1408 "###
1409 ) 627 )
1410 } 628 }
1411 629
1412 #[test] 630 #[test]
1413 fn dont_complete_attr() { 631 fn dont_complete_attr() {
1414 assert_debug_snapshot!( 632 check(
1415 do_reference_completion( 633 r#"
1416 r" 634struct Foo;
1417 struct Foo; 635#[<|>]
1418 #[<|>] 636fn f() {}
1419 fn f() {} 637"#,
1420 " 638 expect![[""]],
1421 ), 639 )
1422 @r###"[]"### 640 }
641
642 #[test]
643 fn completes_type_or_trait_in_impl_block() {
644 check(
645 r#"
646trait MyTrait {}
647struct MyStruct {}
648
649impl My<|>
650"#,
651 expect![[r#"
652 st MyStruct
653 tt MyTrait
654 tp Self
655 "#]],
1423 ) 656 )
1424 } 657 }
1425} 658}
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 560fb19e6..2113abbb2 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -24,6 +24,7 @@ use test_utils::mark;
24#[derive(Debug)] 24#[derive(Debug)]
25pub(crate) struct CompletionContext<'a> { 25pub(crate) struct CompletionContext<'a> {
26 pub(super) sema: Semantics<'a, RootDatabase>, 26 pub(super) sema: Semantics<'a, RootDatabase>,
27 pub(super) scope: SemanticsScope<'a>,
27 pub(super) db: &'a RootDatabase, 28 pub(super) db: &'a RootDatabase,
28 pub(super) config: &'a CompletionConfig, 29 pub(super) config: &'a CompletionConfig,
29 pub(super) offset: TextSize, 30 pub(super) offset: TextSize,
@@ -34,12 +35,12 @@ pub(crate) struct CompletionContext<'a> {
34 pub(super) krate: Option<hir::Crate>, 35 pub(super) krate: Option<hir::Crate>,
35 pub(super) expected_type: Option<Type>, 36 pub(super) expected_type: Option<Type>,
36 pub(super) name_ref_syntax: Option<ast::NameRef>, 37 pub(super) name_ref_syntax: Option<ast::NameRef>,
37 pub(super) function_syntax: Option<ast::FnDef>, 38 pub(super) function_syntax: Option<ast::Fn>,
38 pub(super) use_item_syntax: Option<ast::UseItem>, 39 pub(super) use_item_syntax: Option<ast::Use>,
39 pub(super) record_lit_syntax: Option<ast::RecordLit>, 40 pub(super) record_lit_syntax: Option<ast::RecordExpr>,
40 pub(super) record_pat_syntax: Option<ast::RecordPat>, 41 pub(super) record_pat_syntax: Option<ast::RecordPat>,
41 pub(super) record_field_syntax: Option<ast::RecordField>, 42 pub(super) record_field_syntax: Option<ast::RecordExprField>,
42 pub(super) impl_def: Option<ast::ImplDef>, 43 pub(super) impl_def: Option<ast::Impl>,
43 /// FIXME: `ActiveParameter` is string-based, which is very very wrong 44 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
44 pub(super) active_parameter: Option<ActiveParameter>, 45 pub(super) active_parameter: Option<ActiveParameter>,
45 pub(super) is_param: bool, 46 pub(super) is_param: bool,
@@ -53,6 +54,8 @@ pub(crate) struct CompletionContext<'a> {
53 pub(super) after_if: bool, 54 pub(super) after_if: bool,
54 /// `true` if we are a statement or a last expr in the block. 55 /// `true` if we are a statement or a last expr in the block.
55 pub(super) can_be_stmt: bool, 56 pub(super) can_be_stmt: bool,
57 /// `true` if we expect an expression at the cursor position.
58 pub(super) is_expr: bool,
56 /// Something is typed at the "top" level, in module or impl/trait. 59 /// Something is typed at the "top" level, in module or impl/trait.
57 pub(super) is_new_item: bool, 60 pub(super) is_new_item: bool,
58 /// The receiver if this is a field or method access, i.e. writing something.<|> 61 /// The receiver if this is a field or method access, i.e. writing something.<|>
@@ -60,6 +63,8 @@ pub(crate) struct CompletionContext<'a> {
60 pub(super) dot_receiver_is_ambiguous_float_literal: bool, 63 pub(super) dot_receiver_is_ambiguous_float_literal: bool,
61 /// If this is a call (method or function) in particular, i.e. the () are already there. 64 /// If this is a call (method or function) in particular, i.e. the () are already there.
62 pub(super) is_call: bool, 65 pub(super) is_call: bool,
66 /// Like `is_call`, but for tuple patterns.
67 pub(super) is_pattern_call: bool,
63 /// If this is a macro call, i.e. the () are already there. 68 /// If this is a macro call, i.e. the () are already there.
64 pub(super) is_macro_call: bool, 69 pub(super) is_macro_call: bool,
65 pub(super) is_path_type: bool, 70 pub(super) is_path_type: bool,
@@ -104,8 +109,10 @@ impl<'a> CompletionContext<'a> {
104 let original_token = 109 let original_token =
105 original_file.syntax().token_at_offset(position.offset).left_biased()?; 110 original_file.syntax().token_at_offset(position.offset).left_biased()?;
106 let token = sema.descend_into_macros(original_token.clone()); 111 let token = sema.descend_into_macros(original_token.clone());
112 let scope = sema.scope_at_offset(&token.parent(), position.offset);
107 let mut ctx = CompletionContext { 113 let mut ctx = CompletionContext {
108 sema, 114 sema,
115 scope,
109 db, 116 db,
110 config, 117 config,
111 original_token, 118 original_token,
@@ -127,9 +134,11 @@ impl<'a> CompletionContext<'a> {
127 path_prefix: None, 134 path_prefix: None,
128 after_if: false, 135 after_if: false,
129 can_be_stmt: false, 136 can_be_stmt: false,
137 is_expr: false,
130 is_new_item: false, 138 is_new_item: false,
131 dot_receiver: None, 139 dot_receiver: None,
132 is_call: false, 140 is_call: false,
141 is_pattern_call: false,
133 is_macro_call: false, 142 is_macro_call: false,
134 is_path_type: false, 143 is_path_type: false,
135 has_type_args: false, 144 has_type_args: false,
@@ -196,30 +205,17 @@ impl<'a> CompletionContext<'a> {
196 // The range of the identifier that is being completed. 205 // The range of the identifier that is being completed.
197 pub(crate) fn source_range(&self) -> TextRange { 206 pub(crate) fn source_range(&self) -> TextRange {
198 // check kind of macro-expanded token, but use range of original token 207 // check kind of macro-expanded token, but use range of original token
199 match self.token.kind() { 208 if self.token.kind() == IDENT || self.token.kind().is_keyword() {
200 // workaroud when completion is triggered by trigger characters. 209 mark::hit!(completes_if_prefix_is_keyword);
201 IDENT => self.original_token.text_range(), 210 self.original_token.text_range()
202 _ => { 211 } else {
203 // If we haven't characters between keyword and our cursor we take the keyword start range to edit 212 TextRange::empty(self.offset)
204 if self.token.kind().is_keyword()
205 && self.offset == self.original_token.text_range().end()
206 {
207 mark::hit!(completes_bindings_from_for_with_in_prefix);
208 TextRange::empty(self.original_token.text_range().start())
209 } else {
210 TextRange::empty(self.offset)
211 }
212 }
213 } 213 }
214 } 214 }
215 215
216 pub(crate) fn scope(&self) -> SemanticsScope<'_, RootDatabase> {
217 self.sema.scope_at_offset(&self.token.parent(), self.offset)
218 }
219
220 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { 216 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
221 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); 217 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
222 let syntax_element = NodeOrToken::Token(fake_ident_token.clone()); 218 let syntax_element = NodeOrToken::Token(fake_ident_token);
223 self.block_expr_parent = has_block_expr_parent(syntax_element.clone()); 219 self.block_expr_parent = has_block_expr_parent(syntax_element.clone());
224 self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone()); 220 self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone());
225 self.if_is_prev = if_is_prev(syntax_element.clone()); 221 self.if_is_prev = if_is_prev(syntax_element.clone());
@@ -232,7 +228,7 @@ impl<'a> CompletionContext<'a> {
232 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); 228 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone());
233 self.is_match_arm = is_match_arm(syntax_element.clone()); 229 self.is_match_arm = is_match_arm(syntax_element.clone());
234 self.has_item_list_or_source_file_parent = 230 self.has_item_list_or_source_file_parent =
235 has_item_list_or_source_file_parent(syntax_element.clone()); 231 has_item_list_or_source_file_parent(syntax_element);
236 } 232 }
237 233
238 fn fill( 234 fn fill(
@@ -320,7 +316,7 @@ impl<'a> CompletionContext<'a> {
320 self.name_ref_syntax = 316 self.name_ref_syntax =
321 find_node_at_offset(&original_file, name_ref.syntax().text_range().start()); 317 find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
322 let name_range = name_ref.syntax().text_range(); 318 let name_range = name_ref.syntax().text_range();
323 if ast::RecordField::for_field_name(&name_ref).is_some() { 319 if ast::RecordExprField::for_field_name(&name_ref).is_some() {
324 self.record_lit_syntax = 320 self.record_lit_syntax =
325 self.sema.find_node_at_offset_with_macros(&original_file, offset); 321 self.sema.find_node_at_offset_with_macros(&original_file, offset);
326 } 322 }
@@ -329,7 +325,7 @@ impl<'a> CompletionContext<'a> {
329 .sema 325 .sema
330 .ancestors_with_macros(self.token.parent()) 326 .ancestors_with_macros(self.token.parent())
331 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) 327 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
332 .find_map(ast::ImplDef::cast); 328 .find_map(ast::Impl::cast);
333 329
334 let top_node = name_ref 330 let top_node = name_ref
335 .syntax() 331 .syntax()
@@ -347,13 +343,13 @@ impl<'a> CompletionContext<'a> {
347 } 343 }
348 344
349 self.use_item_syntax = 345 self.use_item_syntax =
350 self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::UseItem::cast); 346 self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::Use::cast);
351 347
352 self.function_syntax = self 348 self.function_syntax = self
353 .sema 349 .sema
354 .ancestors_with_macros(self.token.parent()) 350 .ancestors_with_macros(self.token.parent())
355 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) 351 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
356 .find_map(ast::FnDef::cast); 352 .find_map(ast::Fn::cast);
357 353
358 self.record_field_syntax = self 354 self.record_field_syntax = self
359 .sema 355 .sema
@@ -361,7 +357,7 @@ impl<'a> CompletionContext<'a> {
361 .take_while(|it| { 357 .take_while(|it| {
362 it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR 358 it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR
363 }) 359 })
364 .find_map(ast::RecordField::cast); 360 .find_map(ast::RecordExprField::cast);
365 361
366 let parent = match name_ref.syntax().parent() { 362 let parent = match name_ref.syntax().parent() {
367 Some(it) => it, 363 Some(it) => it,
@@ -377,6 +373,8 @@ impl<'a> CompletionContext<'a> {
377 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) 373 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
378 .is_some(); 374 .is_some();
379 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); 375 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
376 self.is_pattern_call =
377 path.syntax().parent().and_then(ast::TupleStructPat::cast).is_some();
380 378
381 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 379 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
382 self.has_type_args = segment.type_arg_list().is_some(); 380 self.has_type_args = segment.type_arg_list().is_some();
@@ -412,6 +410,7 @@ impl<'a> CompletionContext<'a> {
412 None 410 None
413 }) 411 })
414 .unwrap_or(false); 412 .unwrap_or(false);
413 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
415 414
416 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { 415 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
417 if let Some(if_expr) = 416 if let Some(if_expr) =
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index 98348b349..7bdda316c 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -58,7 +58,7 @@ pub struct CompletionItem {
58 score: Option<CompletionScore>, 58 score: Option<CompletionScore>,
59} 59}
60 60
61// We use custom debug for CompletionItem to make `insta`'s diffs more readable. 61// We use custom debug for CompletionItem to make snapshot tests more readable.
62impl fmt::Debug for CompletionItem { 62impl fmt::Debug for CompletionItem {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 let mut s = f.debug_struct("CompletionItem"); 64 let mut s = f.debug_struct("CompletionItem");
@@ -95,7 +95,7 @@ impl fmt::Debug for CompletionItem {
95 } 95 }
96} 96}
97 97
98#[derive(Debug, Clone, Copy)] 98#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
99pub enum CompletionScore { 99pub enum CompletionScore {
100 /// If only type match 100 /// If only type match
101 TypeMatch, 101 TypeMatch,
@@ -123,30 +123,32 @@ pub enum CompletionItemKind {
123 TypeParam, 123 TypeParam,
124 Macro, 124 Macro,
125 Attribute, 125 Attribute,
126 UnresolvedReference,
126} 127}
127 128
128impl CompletionItemKind { 129impl CompletionItemKind {
129 #[cfg(test)] 130 #[cfg(test)]
130 pub(crate) fn tag(&self) -> &'static str { 131 pub(crate) fn tag(&self) -> &'static str {
131 match self { 132 match self {
132 CompletionItemKind::Snippet => "sn", 133 CompletionItemKind::Attribute => "at",
133 CompletionItemKind::Keyword => "kw", 134 CompletionItemKind::Binding => "bn",
134 CompletionItemKind::Module => "md",
135 CompletionItemKind::Function => "fn",
136 CompletionItemKind::BuiltinType => "bt", 135 CompletionItemKind::BuiltinType => "bt",
137 CompletionItemKind::Struct => "st", 136 CompletionItemKind::Const => "ct",
138 CompletionItemKind::Enum => "en", 137 CompletionItemKind::Enum => "en",
139 CompletionItemKind::EnumVariant => "ev", 138 CompletionItemKind::EnumVariant => "ev",
140 CompletionItemKind::Binding => "bn",
141 CompletionItemKind::Field => "fd", 139 CompletionItemKind::Field => "fd",
140 CompletionItemKind::Function => "fn",
141 CompletionItemKind::Keyword => "kw",
142 CompletionItemKind::Macro => "ma",
143 CompletionItemKind::Method => "me",
144 CompletionItemKind::Module => "md",
145 CompletionItemKind::Snippet => "sn",
142 CompletionItemKind::Static => "sc", 146 CompletionItemKind::Static => "sc",
143 CompletionItemKind::Const => "ct", 147 CompletionItemKind::Struct => "st",
144 CompletionItemKind::Trait => "tt", 148 CompletionItemKind::Trait => "tt",
145 CompletionItemKind::TypeAlias => "ta", 149 CompletionItemKind::TypeAlias => "ta",
146 CompletionItemKind::Method => "me",
147 CompletionItemKind::TypeParam => "tp", 150 CompletionItemKind::TypeParam => "tp",
148 CompletionItemKind::Macro => "ma", 151 CompletionItemKind::UnresolvedReference => "??",
149 CompletionItemKind::Attribute => "at",
150 } 152 }
151 } 153 }
152} 154}
diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs
index b2fe13280..a68861e1c 100644
--- a/crates/ra_ide/src/completion/patterns.rs
+++ b/crates/ra_ide/src/completion/patterns.rs
@@ -13,9 +13,9 @@ use crate::completion::test_utils::check_pattern_is_applicable;
13 13
14pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool { 14pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool {
15 not_same_range_ancestor(element) 15 not_same_range_ancestor(element)
16 .filter(|it| it.kind() == ITEM_LIST) 16 .filter(|it| it.kind() == ASSOC_ITEM_LIST)
17 .and_then(|it| it.parent()) 17 .and_then(|it| it.parent())
18 .filter(|it| it.kind() == TRAIT_DEF) 18 .filter(|it| it.kind() == TRAIT)
19 .is_some() 19 .is_some()
20} 20}
21#[test] 21#[test]
@@ -25,9 +25,9 @@ fn test_has_trait_parent() {
25 25
26pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool { 26pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool {
27 not_same_range_ancestor(element) 27 not_same_range_ancestor(element)
28 .filter(|it| it.kind() == ITEM_LIST) 28 .filter(|it| it.kind() == ASSOC_ITEM_LIST)
29 .and_then(|it| it.parent()) 29 .and_then(|it| it.parent())
30 .filter(|it| it.kind() == IMPL_DEF) 30 .filter(|it| it.kind() == IMPL)
31 .is_some() 31 .is_some()
32} 32}
33#[test] 33#[test]
@@ -73,7 +73,7 @@ pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> boo
73#[test] 73#[test]
74fn test_has_item_list_or_source_file_parent() { 74fn test_has_item_list_or_source_file_parent() {
75 check_pattern_is_applicable(r"i<|>", has_item_list_or_source_file_parent); 75 check_pattern_is_applicable(r"i<|>", has_item_list_or_source_file_parent);
76 check_pattern_is_applicable(r"impl { f<|> }", has_item_list_or_source_file_parent); 76 check_pattern_is_applicable(r"mod foo { f<|> }", has_item_list_or_source_file_parent);
77} 77}
78 78
79pub(crate) fn is_match_arm(element: SyntaxElement) -> bool { 79pub(crate) fn is_match_arm(element: SyntaxElement) -> bool {
@@ -113,7 +113,7 @@ fn test_if_is_prev() {
113} 113}
114 114
115pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool { 115pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool {
116 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT_DEF).is_some() 116 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT).is_some()
117} 117}
118#[test] 118#[test]
119fn test_has_trait_as_prev_sibling() { 119fn test_has_trait_as_prev_sibling() {
@@ -121,7 +121,7 @@ fn test_has_trait_as_prev_sibling() {
121} 121}
122 122
123pub(crate) fn has_impl_as_prev_sibling(element: SyntaxElement) -> bool { 123pub(crate) fn has_impl_as_prev_sibling(element: SyntaxElement) -> bool {
124 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == IMPL_DEF).is_some() 124 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == IMPL).is_some()
125} 125}
126#[test] 126#[test]
127fn test_has_impl_as_prev_sibling() { 127fn test_has_impl_as_prev_sibling() {
@@ -134,7 +134,7 @@ pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
134 NodeOrToken::Token(token) => token.parent(), 134 NodeOrToken::Token(token) => token.parent(),
135 }; 135 };
136 for node in leaf.ancestors() { 136 for node in leaf.ancestors() {
137 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { 137 if node.kind() == FN || node.kind() == LAMBDA_EXPR {
138 break; 138 break;
139 } 139 }
140 let loop_body = match_ast! { 140 let loop_body = match_ast! {
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 4fdc2f0bb..9a94ff476 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -1,4 +1,5 @@
1//! This modules takes care of rendering various definitions as completion items. 1//! This modules takes care of rendering various definitions as completion items.
2//! It also handles scoring (sorting) completions.
2 3
3use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type}; 4use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type};
4use ra_syntax::ast::NameOwner; 5use ra_syntax::ast::NameOwner;
@@ -10,7 +11,7 @@ use crate::{
10 completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind, 11 completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind,
11 CompletionKind, Completions, 12 CompletionKind, Completions,
12 }, 13 },
13 display::{const_label, macro_label, type_label, FunctionSignature}, 14 display::{const_label, function_declaration, macro_label, type_label},
14 CompletionScore, RootDatabase, 15 CompletionScore, RootDatabase,
15}; 16};
16 17
@@ -78,11 +79,10 @@ impl Completions {
78 return self.add_macro(ctx, Some(local_name), *mac); 79 return self.add_macro(ctx, Some(local_name), *mac);
79 } 80 }
80 ScopeDef::Unknown => { 81 ScopeDef::Unknown => {
81 return self.add(CompletionItem::new( 82 return self.add(
82 CompletionKind::Reference, 83 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), local_name)
83 ctx.source_range(), 84 .kind(CompletionItemKind::UnresolvedReference),
84 local_name, 85 );
85 ));
86 } 86 }
87 }; 87 };
88 88
@@ -173,6 +173,7 @@ impl Completions {
173 builder 173 builder
174 .insert_snippet(cap, format!("{}!{}$0{}", name, bra, ket)) 174 .insert_snippet(cap, format!("{}!{}$0{}", name, bra, ket))
175 .label(format!("{}!{}…{}", name, bra, ket)) 175 .label(format!("{}!{}…{}", name, bra, ket))
176 .lookup_by(format!("{}!", name))
176 } 177 }
177 None if needs_bang => builder.insert_text(format!("{}!", name)), 178 None if needs_bang => builder.insert_text(format!("{}!", name)),
178 _ => { 179 _ => {
@@ -194,7 +195,6 @@ impl Completions {
194 195
195 let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string()); 196 let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string());
196 let ast_node = func.source(ctx.db).value; 197 let ast_node = func.source(ctx.db).value;
197 let function_signature = FunctionSignature::from(&ast_node);
198 198
199 let mut builder = 199 let mut builder =
200 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone()) 200 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone())
@@ -205,13 +205,14 @@ impl Completions {
205 }) 205 })
206 .set_documentation(func.docs(ctx.db)) 206 .set_documentation(func.docs(ctx.db))
207 .set_deprecated(is_deprecated(func, ctx.db)) 207 .set_deprecated(is_deprecated(func, ctx.db))
208 .detail(function_signature.to_string()); 208 .detail(function_declaration(&ast_node));
209 209
210 let params = function_signature 210 let params = ast_node
211 .parameter_names 211 .param_list()
212 .iter() 212 .into_iter()
213 .skip(if function_signature.has_self_param { 1 } else { 0 }) 213 .flat_map(|it| it.params())
214 .map(|name| name.trim_start_matches('_').into()) 214 .flat_map(|it| it.pat())
215 .map(|pat| pat.to_string().trim_start_matches('_').into())
215 .collect(); 216 .collect();
216 217
217 builder = builder.add_call_parens(ctx, name, Params::Named(params)); 218 builder = builder.add_call_parens(ctx, name, Params::Named(params));
@@ -314,6 +315,7 @@ impl Completions {
314 } 315 }
315 316
316 if variant_kind == StructKind::Tuple { 317 if variant_kind == StructKind::Tuple {
318 mark::hit!(inserts_parens_for_tuple_enums);
317 let params = Params::Anonymous(variant.fields(ctx.db).len()); 319 let params = Params::Anonymous(variant.fields(ctx.db).len());
318 res = res.add_call_parens(ctx, qualified_name, params) 320 res = res.add_call_parens(ctx, qualified_name, params)
319 } 321 }
@@ -327,17 +329,12 @@ pub(crate) fn compute_score(
327 ty: &Type, 329 ty: &Type,
328 name: &str, 330 name: &str,
329) -> Option<CompletionScore> { 331) -> Option<CompletionScore> {
330 // FIXME: this should not fall back to string equality.
331 let ty = &ty.display(ctx.db).to_string();
332 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { 332 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
333 mark::hit!(test_struct_field_completion_in_record_lit); 333 mark::hit!(record_field_type_match);
334 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?; 334 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
335 ( 335 (struct_field.name(ctx.db).to_string(), struct_field.signature_ty(ctx.db))
336 struct_field.name(ctx.db).to_string(),
337 struct_field.signature_ty(ctx.db).display(ctx.db).to_string(),
338 )
339 } else if let Some(active_parameter) = &ctx.active_parameter { 336 } else if let Some(active_parameter) = &ctx.active_parameter {
340 mark::hit!(test_struct_field_completion_in_func_call); 337 mark::hit!(active_param_type_match);
341 (active_parameter.name.clone(), active_parameter.ty.clone()) 338 (active_parameter.name.clone(), active_parameter.ty.clone())
342 } else { 339 } else {
343 return None; 340 return None;
@@ -382,13 +379,22 @@ impl Builder {
382 if !ctx.config.add_call_parenthesis { 379 if !ctx.config.add_call_parenthesis {
383 return self; 380 return self;
384 } 381 }
385 if ctx.use_item_syntax.is_some() || ctx.is_call { 382 if ctx.use_item_syntax.is_some() {
383 mark::hit!(no_parens_in_use_item);
384 return self;
385 }
386 if ctx.is_pattern_call {
387 mark::hit!(dont_duplicate_pattern_parens);
388 return self;
389 }
390 if ctx.is_call {
386 return self; 391 return self;
387 } 392 }
388 393
389 // Don't add parentheses if the expected type is some function reference. 394 // Don't add parentheses if the expected type is some function reference.
390 if let Some(ty) = &ctx.expected_type { 395 if let Some(ty) = &ctx.expected_type {
391 if ty.is_fn() { 396 if ty.is_fn() {
397 mark::hit!(no_call_parens_if_fn_ptr_needed);
392 return self; 398 return self;
393 } 399 }
394 } 400 }
@@ -413,7 +419,10 @@ impl Builder {
413 .sep_by(", "); 419 .sep_by(", ");
414 format!("{}({})$0", name, function_params_snippet) 420 format!("{}({})$0", name, function_params_snippet)
415 } 421 }
416 _ => format!("{}($0)", name), 422 _ => {
423 mark::hit!(suppress_arg_snippets);
424 format!("{}($0)", name)
425 }
417 }; 426 };
418 427
419 (snippet, format!("{}(…)", name)) 428 (snippet, format!("{}(…)", name))
@@ -456,1064 +465,766 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s
456 465
457#[cfg(test)] 466#[cfg(test)]
458mod tests { 467mod tests {
459 use insta::assert_debug_snapshot; 468 use std::cmp::Reverse;
469
470 use expect::{expect, Expect};
460 use test_utils::mark; 471 use test_utils::mark;
461 472
462 use crate::completion::{ 473 use crate::{
463 test_utils::{do_completion, do_completion_with_options}, 474 completion::{
464 CompletionConfig, CompletionItem, CompletionKind, 475 test_utils::{
476 check_edit, check_edit_with_config, do_completion, get_all_completion_items,
477 },
478 CompletionConfig, CompletionKind,
479 },
480 CompletionScore,
465 }; 481 };
466 482
467 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> { 483 fn check(ra_fixture: &str, expect: Expect) {
468 do_completion(ra_fixture, CompletionKind::Reference) 484 let actual = do_completion(ra_fixture, CompletionKind::Reference);
485 expect.assert_debug_eq(&actual);
469 } 486 }
470 487
471 fn do_reference_completion_with_options( 488 fn check_scores(ra_fixture: &str, expect: Expect) {
472 ra_fixture: &str, 489 fn display_score(score: Option<CompletionScore>) -> &'static str {
473 options: CompletionConfig, 490 match score {
474 ) -> Vec<CompletionItem> { 491 Some(CompletionScore::TypeMatch) => "[type]",
475 do_completion_with_options(ra_fixture, CompletionKind::Reference, &options) 492 Some(CompletionScore::TypeAndNameMatch) => "[type+name]",
493 None => "[]".into(),
494 }
495 }
496
497 let mut completions = get_all_completion_items(CompletionConfig::default(), ra_fixture);
498 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string()));
499 let actual = completions
500 .into_iter()
501 .filter(|it| it.completion_kind == CompletionKind::Reference)
502 .map(|it| {
503 let tag = it.kind().unwrap().tag();
504 let score = display_score(it.score());
505 format!("{} {} {}\n", tag, it.label(), score)
506 })
507 .collect::<String>();
508 expect.assert_eq(&actual);
476 } 509 }
477 510
478 #[test] 511 #[test]
479 fn enum_detail_includes_names_for_record() { 512 fn enum_detail_includes_record_fields() {
480 assert_debug_snapshot!( 513 check(
481 do_reference_completion(
482 r#" 514 r#"
483 enum Foo { 515enum Foo { Foo { x: i32, y: i32 } }
484 Foo {x: i32, y: i32} 516
485 } 517fn main() { Foo::Fo<|> }
486 518"#,
487 fn main() { Foo::Fo<|> } 519 expect![[r#"
488 "#, 520 [
489 ), 521 CompletionItem {
490 @r###" 522 label: "Foo",
491 [ 523 source_range: 54..56,
492 CompletionItem { 524 delete: 54..56,
493 label: "Foo", 525 insert: "Foo",
494 source_range: 56..58, 526 kind: EnumVariant,
495 delete: 56..58, 527 detail: "{ x: i32, y: i32 }",
496 insert: "Foo", 528 },
497 kind: EnumVariant, 529 ]
498 detail: "{ x: i32, y: i32 }", 530 "#]],
499 },
500 ]
501 "###
502 ); 531 );
503 } 532 }
504 533
505 #[test] 534 #[test]
506 fn enum_detail_doesnt_include_names_for_tuple() { 535 fn enum_detail_doesnt_include_tuple_fields() {
507 assert_debug_snapshot!( 536 check(
508 do_reference_completion(
509 r#" 537 r#"
510 enum Foo { 538enum Foo { Foo (i32, i32) }
511 Foo (i32, i32) 539
512 } 540fn main() { Foo::Fo<|> }
513 541"#,
514 fn main() { Foo::Fo<|> } 542 expect![[r#"
515 "#, 543 [
516 ), 544 CompletionItem {
517 @r###" 545 label: "Foo(…)",
518 [ 546 source_range: 46..48,
519 CompletionItem { 547 delete: 46..48,
520 label: "Foo(…)", 548 insert: "Foo($0)",
521 source_range: 50..52, 549 kind: EnumVariant,
522 delete: 50..52, 550 lookup: "Foo",
523 insert: "Foo($0)", 551 detail: "(i32, i32)",
524 kind: EnumVariant, 552 trigger_call_info: true,
525 lookup: "Foo", 553 },
526 detail: "(i32, i32)", 554 ]
527 trigger_call_info: true, 555 "#]],
528 },
529 ]
530 "###
531 ); 556 );
532 } 557 }
533 558
534 #[test] 559 #[test]
535 fn enum_detail_just_parentheses_for_unit() { 560 fn enum_detail_just_parentheses_for_unit() {
536 assert_debug_snapshot!( 561 check(
537 do_reference_completion(
538 r#" 562 r#"
539 enum Foo { 563enum Foo { Foo }
540 Foo 564
541 } 565fn main() { Foo::Fo<|> }
542 566"#,
543 fn main() { Foo::Fo<|> } 567 expect![[r#"
544 "#, 568 [
545 ), 569 CompletionItem {
546 @r###" 570 label: "Foo",
547 [ 571 source_range: 35..37,
548 CompletionItem { 572 delete: 35..37,
549 label: "Foo", 573 insert: "Foo",
550 source_range: 39..41, 574 kind: EnumVariant,
551 delete: 39..41, 575 detail: "()",
552 insert: "Foo", 576 },
553 kind: EnumVariant, 577 ]
554 detail: "()", 578 "#]],
555 },
556 ]
557 "###
558 ); 579 );
559 } 580 }
560 581
561 #[test] 582 #[test]
562 fn sets_deprecated_flag_in_completion_items() { 583 fn sets_deprecated_flag_in_completion_items() {
563 assert_debug_snapshot!( 584 check(
564 do_reference_completion( 585 r#"
565 r#" 586#[deprecated]
566 #[deprecated] 587fn something_deprecated() {}
567 fn something_deprecated() {} 588#[deprecated(since = "1.0.0")]
568 589fn something_else_deprecated() {}
569 #[deprecated(since = "1.0.0")] 590
570 fn something_else_deprecated() {} 591fn main() { som<|> }
571 592"#,
572 fn main() { som<|> } 593 expect![[r#"
573 "#, 594 [
574 ), 595 CompletionItem {
575 @r###" 596 label: "main()",
576 [ 597 source_range: 121..124,
577 CompletionItem { 598 delete: 121..124,
578 label: "main()", 599 insert: "main()$0",
579 source_range: 122..125, 600 kind: Function,
580 delete: 122..125, 601 lookup: "main",
581 insert: "main()$0", 602 detail: "fn main()",
582 kind: Function, 603 },
583 lookup: "main", 604 CompletionItem {
584 detail: "fn main()", 605 label: "something_deprecated()",
585 }, 606 source_range: 121..124,
586 CompletionItem { 607 delete: 121..124,
587 label: "something_deprecated()", 608 insert: "something_deprecated()$0",
588 source_range: 122..125, 609 kind: Function,
589 delete: 122..125, 610 lookup: "something_deprecated",
590 insert: "something_deprecated()$0", 611 detail: "fn something_deprecated()",
591 kind: Function, 612 deprecated: true,
592 lookup: "something_deprecated", 613 },
593 detail: "fn something_deprecated()", 614 CompletionItem {
594 deprecated: true, 615 label: "something_else_deprecated()",
595 }, 616 source_range: 121..124,
596 CompletionItem { 617 delete: 121..124,
597 label: "something_else_deprecated()", 618 insert: "something_else_deprecated()$0",
598 source_range: 122..125, 619 kind: Function,
599 delete: 122..125, 620 lookup: "something_else_deprecated",
600 insert: "something_else_deprecated()$0", 621 detail: "fn something_else_deprecated()",
601 kind: Function, 622 deprecated: true,
602 lookup: "something_else_deprecated", 623 },
603 detail: "fn something_else_deprecated()", 624 ]
604 deprecated: true, 625 "#]],
605 }, 626 );
606 ] 627
607 "### 628 check(
629 r#"
630struct A { #[deprecated] the_field: u32 }
631fn foo() { A { the<|> } }
632"#,
633 expect![[r#"
634 [
635 CompletionItem {
636 label: "the_field",
637 source_range: 57..60,
638 delete: 57..60,
639 insert: "the_field",
640 kind: Field,
641 detail: "u32",
642 deprecated: true,
643 },
644 ]
645 "#]],
646 );
647 }
648
649 #[test]
650 fn renders_docs() {
651 check(
652 r#"
653struct S {
654 /// Field docs
655 foo:
656}
657impl S {
658 /// Method docs
659 fn bar(self) { self.<|> }
660}"#,
661 expect![[r#"
662 [
663 CompletionItem {
664 label: "bar()",
665 source_range: 94..94,
666 delete: 94..94,
667 insert: "bar()$0",
668 kind: Method,
669 lookup: "bar",
670 detail: "fn bar(self)",
671 documentation: Documentation(
672 "Method docs",
673 ),
674 },
675 CompletionItem {
676 label: "foo",
677 source_range: 94..94,
678 delete: 94..94,
679 insert: "foo",
680 kind: Field,
681 detail: "{unknown}",
682 documentation: Documentation(
683 "Field docs",
684 ),
685 },
686 ]
687 "#]],
608 ); 688 );
689
690 check(
691 r#"
692use self::my<|>;
693
694/// mod docs
695mod my { }
696
697/// enum docs
698enum E {
699 /// variant docs
700 V
701}
702use self::E::*;
703"#,
704 expect![[r#"
705 [
706 CompletionItem {
707 label: "E",
708 source_range: 10..12,
709 delete: 10..12,
710 insert: "E",
711 kind: Enum,
712 documentation: Documentation(
713 "enum docs",
714 ),
715 },
716 CompletionItem {
717 label: "V",
718 source_range: 10..12,
719 delete: 10..12,
720 insert: "V",
721 kind: EnumVariant,
722 detail: "()",
723 documentation: Documentation(
724 "variant docs",
725 ),
726 },
727 CompletionItem {
728 label: "my",
729 source_range: 10..12,
730 delete: 10..12,
731 insert: "my",
732 kind: Module,
733 documentation: Documentation(
734 "mod docs",
735 ),
736 },
737 ]
738 "#]],
739 )
740 }
741
742 #[test]
743 fn dont_render_attrs() {
744 check(
745 r#"
746struct S;
747impl S {
748 #[inline]
749 fn the_method(&self) { }
750}
751fn foo(s: S) { s.<|> }
752"#,
753 expect![[r#"
754 [
755 CompletionItem {
756 label: "the_method()",
757 source_range: 81..81,
758 delete: 81..81,
759 insert: "the_method()$0",
760 kind: Method,
761 lookup: "the_method",
762 detail: "fn the_method(&self)",
763 },
764 ]
765 "#]],
766 )
609 } 767 }
610 768
611 #[test] 769 #[test]
612 fn inserts_parens_for_function_calls() { 770 fn inserts_parens_for_function_calls() {
613 mark::check!(inserts_parens_for_function_calls); 771 mark::check!(inserts_parens_for_function_calls);
614 assert_debug_snapshot!( 772 check_edit(
615 do_reference_completion( 773 "no_args",
616 r" 774 r#"
617 fn no_args() {} 775fn no_args() {}
618 fn main() { no_<|> } 776fn main() { no_<|> }
619 " 777"#,
620 ), 778 r#"
621 @r###" 779fn no_args() {}
622 [ 780fn main() { no_args()$0 }
623 CompletionItem { 781"#,
624 label: "main()",
625 source_range: 28..31,
626 delete: 28..31,
627 insert: "main()$0",
628 kind: Function,
629 lookup: "main",
630 detail: "fn main()",
631 },
632 CompletionItem {
633 label: "no_args()",
634 source_range: 28..31,
635 delete: 28..31,
636 insert: "no_args()$0",
637 kind: Function,
638 lookup: "no_args",
639 detail: "fn no_args()",
640 },
641 ]
642 "###
643 );
644 assert_debug_snapshot!(
645 do_reference_completion(
646 r"
647 fn with_args(x: i32, y: String) {}
648 fn main() { with_<|> }
649 "
650 ),
651 @r###"
652 [
653 CompletionItem {
654 label: "main()",
655 source_range: 47..52,
656 delete: 47..52,
657 insert: "main()$0",
658 kind: Function,
659 lookup: "main",
660 detail: "fn main()",
661 },
662 CompletionItem {
663 label: "with_args(…)",
664 source_range: 47..52,
665 delete: 47..52,
666 insert: "with_args(${1:x}, ${2:y})$0",
667 kind: Function,
668 lookup: "with_args",
669 detail: "fn with_args(x: i32, y: String)",
670 trigger_call_info: true,
671 },
672 ]
673 "###
674 ); 782 );
675 assert_debug_snapshot!( 783
676 do_reference_completion( 784 check_edit(
677 r" 785 "with_args",
678 fn with_ignored_args(_foo: i32, ___bar: bool, ho_ge_: String) {} 786 r#"
679 fn main() { with_<|> } 787fn with_args(x: i32, y: String) {}
680 " 788fn main() { with_<|> }
681 ), 789"#,
682 @r###" 790 r#"
683 [ 791fn with_args(x: i32, y: String) {}
684 CompletionItem { 792fn main() { with_args(${1:x}, ${2:y})$0 }
685 label: "main()", 793"#,
686 source_range: 77..82,
687 delete: 77..82,
688 insert: "main()$0",
689 kind: Function,
690 lookup: "main",
691 detail: "fn main()",
692 },
693 CompletionItem {
694 label: "with_ignored_args(…)",
695 source_range: 77..82,
696 delete: 77..82,
697 insert: "with_ignored_args(${1:foo}, ${2:bar}, ${3:ho_ge_})$0",
698 kind: Function,
699 lookup: "with_ignored_args",
700 detail: "fn with_ignored_args(_foo: i32, ___bar: bool, ho_ge_: String)",
701 trigger_call_info: true,
702 },
703 ]
704 "###
705 ); 794 );
706 assert_debug_snapshot!( 795
707 do_reference_completion( 796 check_edit(
708 r" 797 "foo",
709 struct S {} 798 r#"
710 impl S { 799struct S;
711 fn foo(&self) {} 800impl S {
712 } 801 fn foo(&self) {}
713 fn bar(s: &S) { 802}
714 s.f<|> 803fn bar(s: &S) { s.f<|> }
715 } 804"#,
716 " 805 r#"
717 ), 806struct S;
718 @r###" 807impl S {
719 [ 808 fn foo(&self) {}
720 CompletionItem { 809}
721 label: "foo()", 810fn bar(s: &S) { s.foo()$0 }
722 source_range: 66..67, 811"#,
723 delete: 66..67,
724 insert: "foo()$0",
725 kind: Method,
726 lookup: "foo",
727 detail: "fn foo(&self)",
728 },
729 ]
730 "###
731 ); 812 );
732 assert_debug_snapshot!( 813
733 do_reference_completion( 814 check_edit(
734 r" 815 "foo",
735 struct S {} 816 r#"
736 impl S { 817struct S {}
737 fn foo_ignored_args(&self, _a: bool, b: i32) {} 818impl S {
738 } 819 fn foo(&self, x: i32) {}
739 fn bar(s: &S) { 820}
740 s.f<|> 821fn bar(s: &S) {
741 } 822 s.f<|>
742 " 823}
743 ), 824"#,
744 @r###" 825 r#"
745 [ 826struct S {}
746 CompletionItem { 827impl S {
747 label: "foo_ignored_args(…)", 828 fn foo(&self, x: i32) {}
748 source_range: 97..98, 829}
749 delete: 97..98, 830fn bar(s: &S) {
750 insert: "foo_ignored_args(${1:a}, ${2:b})$0", 831 s.foo(${1:x})$0
751 kind: Method, 832}
752 lookup: "foo_ignored_args", 833"#,
753 detail: "fn foo_ignored_args(&self, _a: bool, b: i32)",
754 trigger_call_info: true,
755 },
756 ]
757 "###
758 ); 834 );
759 } 835 }
760 836
761 #[test] 837 #[test]
762 fn inserts_parens_for_tuple_enums() { 838 fn suppress_arg_snippets() {
763 assert_debug_snapshot!( 839 mark::check!(suppress_arg_snippets);
764 do_reference_completion( 840 check_edit_with_config(
765 r" 841 CompletionConfig { add_call_argument_snippets: false, ..CompletionConfig::default() },
766 enum Option<T> { Some(T), None } 842 "with_args",
767 use Option::*; 843 r#"
768 fn main() -> Option<i32> { 844fn with_args(x: i32, y: String) {}
769 Som<|> 845fn main() { with_<|> }
770 } 846"#,
771 " 847 r#"
772 ), 848fn with_args(x: i32, y: String) {}
773 @r###" 849fn main() { with_args($0) }
774 [ 850"#,
775 CompletionItem {
776 label: "None",
777 source_range: 79..82,
778 delete: 79..82,
779 insert: "None",
780 kind: EnumVariant,
781 detail: "()",
782 },
783 CompletionItem {
784 label: "Option",
785 source_range: 79..82,
786 delete: 79..82,
787 insert: "Option",
788 kind: Enum,
789 },
790 CompletionItem {
791 label: "Some(…)",
792 source_range: 79..82,
793 delete: 79..82,
794 insert: "Some($0)",
795 kind: EnumVariant,
796 lookup: "Some",
797 detail: "(T)",
798 trigger_call_info: true,
799 },
800 CompletionItem {
801 label: "main()",
802 source_range: 79..82,
803 delete: 79..82,
804 insert: "main()$0",
805 kind: Function,
806 lookup: "main",
807 detail: "fn main() -> Option<i32>",
808 },
809 ]
810 "###
811 );
812 assert_debug_snapshot!(
813 do_reference_completion(
814 r"
815 enum Option<T> { Some(T), None }
816 use Option::*;
817 fn main(value: Option<i32>) {
818 match value {
819 Som<|>
820 }
821 }
822 "
823 ),
824 @r###"
825 [
826 CompletionItem {
827 label: "None",
828 source_range: 104..107,
829 delete: 104..107,
830 insert: "None",
831 kind: EnumVariant,
832 detail: "()",
833 },
834 CompletionItem {
835 label: "Option",
836 source_range: 104..107,
837 delete: 104..107,
838 insert: "Option",
839 kind: Enum,
840 },
841 CompletionItem {
842 label: "Some(…)",
843 source_range: 104..107,
844 delete: 104..107,
845 insert: "Some($0)",
846 kind: EnumVariant,
847 lookup: "Some",
848 detail: "(T)",
849 trigger_call_info: true,
850 },
851 ]
852 "###
853 ); 851 );
854 } 852 }
855 853
856 #[test] 854 #[test]
857 fn no_call_parens_if_fn_ptr_needed() { 855 fn strips_underscores_from_args() {
858 assert_debug_snapshot!( 856 check_edit(
859 do_reference_completion( 857 "foo",
860 r" 858 r#"
861 fn somefn(with: u8, a: u8, lot: u8, of: u8, args: u8) {} 859fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
862 860fn main() { f<|> }
863 struct ManualVtable { 861"#,
864 method: fn(u8, u8, u8, u8, u8), 862 r#"
865 } 863fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
864fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
865"#,
866 );
867 }
866 868
867 fn main() -> ManualVtable { 869 #[test]
868 ManualVtable { 870 fn inserts_parens_for_tuple_enums() {
869 method: some<|> 871 mark::check!(inserts_parens_for_tuple_enums);
870 } 872 check_edit(
871 } 873 "Some",
872 " 874 r#"
873 ), 875enum Option<T> { Some(T), None }
874 @r###" 876use Option::*;
875 [ 877fn main() -> Option<i32> {
876 CompletionItem { 878 Som<|>
877 label: "ManualVtable", 879}
878 source_range: 182..186, 880"#,
879 delete: 182..186, 881 r#"
880 insert: "ManualVtable", 882enum Option<T> { Some(T), None }
881 kind: Struct, 883use Option::*;
882 }, 884fn main() -> Option<i32> {
883 CompletionItem { 885 Some($0)
884 label: "main", 886}
885 source_range: 182..186, 887"#,
886 delete: 182..186, 888 );
887 insert: "main", 889 check_edit(
888 kind: Function, 890 "Some",
889 detail: "fn main() -> ManualVtable", 891 r#"
890 }, 892enum Option<T> { Some(T), None }
891 CompletionItem { 893use Option::*;
892 label: "somefn", 894fn main(value: Option<i32>) {
893 source_range: 182..186, 895 match value {
894 delete: 182..186, 896 Som<|>
895 insert: "somefn", 897 }
896 kind: Function, 898}
897 detail: "fn somefn(with: u8, a: u8, lot: u8, of: u8, args: u8)", 899"#,
898 }, 900 r#"
899 ] 901enum Option<T> { Some(T), None }
900 "### 902use Option::*;
903fn main(value: Option<i32>) {
904 match value {
905 Some($0)
906 }
907}
908"#,
901 ); 909 );
902 } 910 }
903 911
904 #[test] 912 #[test]
905 fn arg_snippets_for_method_call() { 913 fn dont_duplicate_pattern_parens() {
906 assert_debug_snapshot!( 914 mark::check!(dont_duplicate_pattern_parens);
907 do_reference_completion( 915 check_edit(
908 r" 916 "Var",
909 struct S {} 917 r#"
910 impl S { 918enum E { Var(i32) }
911 fn foo(&self, x: i32) {} 919fn main() {
912 } 920 match E::Var(92) {
913 fn bar(s: &S) { 921 E::<|>(92) => (),
914 s.f<|> 922 }
915 } 923}
916 " 924"#,
917 ), 925 r#"
918 @r###" 926enum E { Var(i32) }
919 [ 927fn main() {
920 CompletionItem { 928 match E::Var(92) {
921 label: "foo(…)", 929 E::Var(92) => (),
922 source_range: 74..75, 930 }
923 delete: 74..75, 931}
924 insert: "foo(${1:x})$0", 932"#,
925 kind: Method, 933 );
926 lookup: "foo",
927 detail: "fn foo(&self, x: i32)",
928 trigger_call_info: true,
929 },
930 ]
931 "###
932 )
933 } 934 }
934 935
935 #[test] 936 #[test]
936 fn no_arg_snippets_for_method_call() { 937 fn no_call_parens_if_fn_ptr_needed() {
937 assert_debug_snapshot!( 938 mark::check!(no_call_parens_if_fn_ptr_needed);
938 do_reference_completion_with_options( 939 check_edit(
939 r" 940 "foo",
940 struct S {} 941 r#"
941 impl S { 942fn foo(foo: u8, bar: u8) {}
942 fn foo(&self, x: i32) {} 943struct ManualVtable { f: fn(u8, u8) }
943 } 944
944 fn bar(s: &S) { 945fn main() -> ManualVtable {
945 s.f<|> 946 ManualVtable { f: f<|> }
946 } 947}
947 ", 948"#,
948 CompletionConfig { 949 r#"
949 add_call_argument_snippets: false, 950fn foo(foo: u8, bar: u8) {}
950 .. Default::default() 951struct ManualVtable { f: fn(u8, u8) }
951 } 952
952 ), 953fn main() -> ManualVtable {
953 @r###" 954 ManualVtable { f: foo }
954 [ 955}
955 CompletionItem { 956"#,
956 label: "foo(…)", 957 );
957 source_range: 74..75,
958 delete: 74..75,
959 insert: "foo($0)",
960 kind: Method,
961 lookup: "foo",
962 detail: "fn foo(&self, x: i32)",
963 trigger_call_info: true,
964 },
965 ]
966 "###
967 )
968 } 958 }
969 959
970 #[test] 960 #[test]
971 fn dont_render_function_parens_in_use_item() { 961 fn no_parens_in_use_item() {
972 assert_debug_snapshot!( 962 mark::check!(no_parens_in_use_item);
973 do_reference_completion( 963 check_edit(
974 " 964 "foo",
975 //- /lib.rs 965 r#"
976 mod m { pub fn foo() {} } 966mod m { pub fn foo() {} }
977 use crate::m::f<|>; 967use crate::m::f<|>;
978 " 968"#,
979 ), 969 r#"
980 @r###" 970mod m { pub fn foo() {} }
981 [ 971use crate::m::foo;
982 CompletionItem { 972"#,
983 label: "foo",
984 source_range: 40..41,
985 delete: 40..41,
986 insert: "foo",
987 kind: Function,
988 detail: "pub fn foo()",
989 },
990 ]
991 "###
992 ); 973 );
993 } 974 }
994 975
995 #[test] 976 #[test]
996 fn dont_render_function_parens_if_already_call() { 977 fn no_parens_in_call() {
997 assert_debug_snapshot!( 978 check_edit(
998 do_reference_completion( 979 "foo",
999 " 980 r#"
1000 //- /lib.rs 981fn foo(x: i32) {}
1001 fn frobnicate() {} 982fn main() { f<|>(); }
1002 fn main() { 983"#,
1003 frob<|>(); 984 r#"
1004 } 985fn foo(x: i32) {}
1005 " 986fn main() { foo(); }
1006 ), 987"#,
1007 @r###"
1008 [
1009 CompletionItem {
1010 label: "frobnicate",
1011 source_range: 35..39,
1012 delete: 35..39,
1013 insert: "frobnicate",
1014 kind: Function,
1015 detail: "fn frobnicate()",
1016 },
1017 CompletionItem {
1018 label: "main",
1019 source_range: 35..39,
1020 delete: 35..39,
1021 insert: "main",
1022 kind: Function,
1023 detail: "fn main()",
1024 },
1025 ]
1026 "###
1027 ); 988 );
1028 assert_debug_snapshot!( 989 check_edit(
1029 do_reference_completion( 990 "foo",
1030 " 991 r#"
1031 //- /lib.rs 992struct Foo;
1032 struct Foo {} 993impl Foo { fn foo(&self){} }
1033 impl Foo { fn new() -> Foo {} } 994fn f(foo: &Foo) { foo.f<|>(); }
1034 fn main() { 995"#,
1035 Foo::ne<|>(); 996 r#"
1036 } 997struct Foo;
1037 " 998impl Foo { fn foo(&self){} }
1038 ), 999fn f(foo: &Foo) { foo.foo(); }
1039 @r###" 1000"#,
1040 [
1041 CompletionItem {
1042 label: "new",
1043 source_range: 67..69,
1044 delete: 67..69,
1045 insert: "new",
1046 kind: Function,
1047 detail: "fn new() -> Foo",
1048 },
1049 ]
1050 "###
1051 ); 1001 );
1052 } 1002 }
1053 1003
1054 #[test] 1004 #[test]
1055 fn inserts_angle_brackets_for_generics() { 1005 fn inserts_angle_brackets_for_generics() {
1056 mark::check!(inserts_angle_brackets_for_generics); 1006 mark::check!(inserts_angle_brackets_for_generics);
1057 assert_debug_snapshot!( 1007 check_edit(
1058 do_reference_completion( 1008 "Vec",
1059 r" 1009 r#"
1060 struct Vec<T> {} 1010struct Vec<T> {}
1061 fn foo(xs: Ve<|>) 1011fn foo(xs: Ve<|>)
1062 " 1012"#,
1063 ), 1013 r#"
1064 @r###" 1014struct Vec<T> {}
1065 [ 1015fn foo(xs: Vec<$0>)
1066 CompletionItem { 1016"#,
1067 label: "Vec<…>",
1068 source_range: 28..30,
1069 delete: 28..30,
1070 insert: "Vec<$0>",
1071 kind: Struct,
1072 lookup: "Vec",
1073 },
1074 CompletionItem {
1075 label: "foo(…)",
1076 source_range: 28..30,
1077 delete: 28..30,
1078 insert: "foo(${1:xs})$0",
1079 kind: Function,
1080 lookup: "foo",
1081 detail: "fn foo(xs: Ve)",
1082 trigger_call_info: true,
1083 },
1084 ]
1085 "###
1086 ); 1017 );
1087 assert_debug_snapshot!( 1018 check_edit(
1088 do_reference_completion( 1019 "Vec",
1089 r" 1020 r#"
1090 type Vec<T> = (T,); 1021type Vec<T> = (T,);
1091 fn foo(xs: Ve<|>) 1022fn foo(xs: Ve<|>)
1092 " 1023"#,
1093 ), 1024 r#"
1094 @r###" 1025type Vec<T> = (T,);
1095 [ 1026fn foo(xs: Vec<$0>)
1096 CompletionItem { 1027"#,
1097 label: "Vec<…>",
1098 source_range: 31..33,
1099 delete: 31..33,
1100 insert: "Vec<$0>",
1101 kind: TypeAlias,
1102 lookup: "Vec",
1103 },
1104 CompletionItem {
1105 label: "foo(…)",
1106 source_range: 31..33,
1107 delete: 31..33,
1108 insert: "foo(${1:xs})$0",
1109 kind: Function,
1110 lookup: "foo",
1111 detail: "fn foo(xs: Ve)",
1112 trigger_call_info: true,
1113 },
1114 ]
1115 "###
1116 ); 1028 );
1117 assert_debug_snapshot!( 1029 check_edit(
1118 do_reference_completion( 1030 "Vec",
1119 r" 1031 r#"
1120 struct Vec<T = i128> {} 1032struct Vec<T = i128> {}
1121 fn foo(xs: Ve<|>) 1033fn foo(xs: Ve<|>)
1122 " 1034"#,
1123 ), 1035 r#"
1124 @r###" 1036struct Vec<T = i128> {}
1125 [ 1037fn foo(xs: Vec)
1126 CompletionItem { 1038"#,
1127 label: "Vec",
1128 source_range: 35..37,
1129 delete: 35..37,
1130 insert: "Vec",
1131 kind: Struct,
1132 },
1133 CompletionItem {
1134 label: "foo(…)",
1135 source_range: 35..37,
1136 delete: 35..37,
1137 insert: "foo(${1:xs})$0",
1138 kind: Function,
1139 lookup: "foo",
1140 detail: "fn foo(xs: Ve)",
1141 trigger_call_info: true,
1142 },
1143 ]
1144 "###
1145 ); 1039 );
1146 assert_debug_snapshot!( 1040 check_edit(
1147 do_reference_completion( 1041 "Vec",
1148 r" 1042 r#"
1149 struct Vec<T> {} 1043struct Vec<T> {}
1150 fn foo(xs: Ve<|><i128>) 1044fn foo(xs: Ve<|><i128>)
1151 " 1045"#,
1152 ), 1046 r#"
1153 @r###" 1047struct Vec<T> {}
1154 [ 1048fn foo(xs: Vec<i128>)
1155 CompletionItem { 1049"#,
1156 label: "Vec",
1157 source_range: 28..30,
1158 delete: 28..30,
1159 insert: "Vec",
1160 kind: Struct,
1161 },
1162 CompletionItem {
1163 label: "foo(…)",
1164 source_range: 28..30,
1165 delete: 28..30,
1166 insert: "foo(${1:xs})$0",
1167 kind: Function,
1168 lookup: "foo",
1169 detail: "fn foo(xs: Ve<i128>)",
1170 trigger_call_info: true,
1171 },
1172 ]
1173 "###
1174 ); 1050 );
1175 } 1051 }
1176 1052
1177 #[test] 1053 #[test]
1178 fn dont_insert_macro_call_parens_unncessary() { 1054 fn dont_insert_macro_call_parens_unncessary() {
1179 mark::check!(dont_insert_macro_call_parens_unncessary); 1055 mark::check!(dont_insert_macro_call_parens_unncessary);
1180 assert_debug_snapshot!( 1056 check_edit(
1181 do_reference_completion( 1057 "frobnicate!",
1182 r" 1058 r#"
1183 //- /main.rs 1059//- /main.rs
1184 use foo::<|>; 1060use foo::<|>;
1185 1061//- /foo/lib.rs
1186 //- /foo/lib.rs 1062#[macro_export]
1187 #[macro_export] 1063macro_rules frobnicate { () => () }
1188 macro_rules frobnicate { 1064"#,
1189 () => () 1065 r#"
1190 } 1066use foo::frobnicate;
1191 " 1067"#,
1192 ),
1193 @r###"
1194 [
1195 CompletionItem {
1196 label: "frobnicate!",
1197 source_range: 9..9,
1198 delete: 9..9,
1199 insert: "frobnicate",
1200 kind: Macro,
1201 detail: "#[macro_export]\nmacro_rules! frobnicate",
1202 },
1203 ]
1204 "###
1205 ); 1068 );
1206 1069
1207 assert_debug_snapshot!( 1070 check_edit(
1208 do_reference_completion( 1071 "frobnicate!",
1209 r" 1072 r#"
1210 //- /main.rs 1073macro_rules frobnicate { () => () }
1211 macro_rules frobnicate { 1074fn main() { frob<|>!(); }
1212 () => () 1075"#,
1213 } 1076 r#"
1214 fn main() { 1077macro_rules frobnicate { () => () }
1215 frob<|>!(); 1078fn main() { frobnicate!(); }
1216 } 1079"#,
1217 "
1218 ),
1219 @r###"
1220 [
1221 CompletionItem {
1222 label: "frobnicate!",
1223 source_range: 56..60,
1224 delete: 56..60,
1225 insert: "frobnicate",
1226 kind: Macro,
1227 detail: "macro_rules! frobnicate",
1228 },
1229 CompletionItem {
1230 label: "main()",
1231 source_range: 56..60,
1232 delete: 56..60,
1233 insert: "main()$0",
1234 kind: Function,
1235 lookup: "main",
1236 detail: "fn main()",
1237 },
1238 ]
1239 "###
1240 ); 1080 );
1241 } 1081 }
1242 1082
1243 #[test] 1083 #[test]
1244 fn test_struct_field_completion_in_func_call() { 1084 fn active_param_score() {
1245 mark::check!(test_struct_field_completion_in_func_call); 1085 mark::check!(active_param_type_match);
1246 assert_debug_snapshot!( 1086 check_scores(
1247 do_reference_completion( 1087 r#"
1248 r" 1088struct S { foo: i64, bar: u32, baz: u32 }
1249 struct A { another_field: i64, the_field: u32, my_string: String } 1089fn test(bar: u32) { }
1250 fn test(my_param: u32) -> u32 { my_param } 1090fn foo(s: S) { test(s.<|>) }
1251 fn foo(a: A) { 1091"#,
1252 test(a.<|>) 1092 expect![[r#"
1253 } 1093 fd bar [type+name]
1254 ", 1094 fd baz [type]
1255 ), 1095 fd foo []
1256 @r###" 1096 "#]],
1257 [
1258 CompletionItem {
1259 label: "another_field",
1260 source_range: 136..136,
1261 delete: 136..136,
1262 insert: "another_field",
1263 kind: Field,
1264 detail: "i64",
1265 },
1266 CompletionItem {
1267 label: "my_string",
1268 source_range: 136..136,
1269 delete: 136..136,
1270 insert: "my_string",
1271 kind: Field,
1272 detail: "{unknown}",
1273 },
1274 CompletionItem {
1275 label: "the_field",
1276 source_range: 136..136,
1277 delete: 136..136,
1278 insert: "the_field",
1279 kind: Field,
1280 detail: "u32",
1281 score: TypeMatch,
1282 },
1283 ]
1284 "###
1285 ); 1097 );
1286 } 1098 }
1287 1099
1288 #[test] 1100 #[test]
1289 fn test_struct_field_completion_in_func_call_with_type_and_name() { 1101 fn record_field_scores() {
1290 assert_debug_snapshot!( 1102 mark::check!(record_field_type_match);
1291 do_reference_completion( 1103 check_scores(
1292 r" 1104 r#"
1293 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 1105struct A { foo: i64, bar: u32, baz: u32 }
1294 fn test(the_field: u32) -> u32 { the_field } 1106struct B { x: (), y: f32, bar: u32 }
1295 fn foo(a: A) { 1107fn foo(a: A) { B { bar: a.<|> }; }
1296 test(a.<|>) 1108"#,
1297 } 1109 expect![[r#"
1298 ", 1110 fd bar [type+name]
1299 ), 1111 fd baz [type]
1300 @r###" 1112 fd foo []
1301 [ 1113 "#]],
1302 CompletionItem { 1114 )
1303 label: "another_field",
1304 source_range: 143..143,
1305 delete: 143..143,
1306 insert: "another_field",
1307 kind: Field,
1308 detail: "i64",
1309 },
1310 CompletionItem {
1311 label: "another_good_type",
1312 source_range: 143..143,
1313 delete: 143..143,
1314 insert: "another_good_type",
1315 kind: Field,
1316 detail: "u32",
1317 score: TypeMatch,
1318 },
1319 CompletionItem {
1320 label: "the_field",
1321 source_range: 143..143,
1322 delete: 143..143,
1323 insert: "the_field",
1324 kind: Field,
1325 detail: "u32",
1326 score: TypeAndNameMatch,
1327 },
1328 ]
1329 "###
1330 );
1331 } 1115 }
1332 1116
1333 #[test] 1117 #[test]
1334 fn test_struct_field_completion_in_record_lit() { 1118 fn record_field_and_call_scores() {
1335 mark::check!(test_struct_field_completion_in_record_lit); 1119 check_scores(
1336 assert_debug_snapshot!( 1120 r#"
1337 do_reference_completion( 1121struct A { foo: i64, bar: u32, baz: u32 }
1338 r" 1122struct B { x: (), y: f32, bar: u32 }
1339 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 1123fn f(foo: i64) { }
1340 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } 1124fn foo(a: A) { B { bar: f(a.<|>) }; }
1341 fn foo(a: A) { 1125"#,
1342 let b = B { 1126 expect![[r#"
1343 the_field: a.<|> 1127 fd foo [type+name]
1344 }; 1128 fd bar []
1345 } 1129 fd baz []
1346 ", 1130 "#]],
1347 ), 1131 );
1348 @r###" 1132 check_scores(
1349 [ 1133 r#"
1350 CompletionItem { 1134struct A { foo: i64, bar: u32, baz: u32 }
1351 label: "another_field", 1135struct B { x: (), y: f32, bar: u32 }
1352 source_range: 189..189, 1136fn f(foo: i64) { }
1353 delete: 189..189, 1137fn foo(a: A) { f(B { bar: a.<|> }); }
1354 insert: "another_field", 1138"#,
1355 kind: Field, 1139 expect![[r#"
1356 detail: "i64", 1140 fd bar [type+name]
1357 }, 1141 fd baz [type]
1358 CompletionItem { 1142 fd foo []
1359 label: "another_good_type", 1143 "#]],
1360 source_range: 189..189,
1361 delete: 189..189,
1362 insert: "another_good_type",
1363 kind: Field,
1364 detail: "u32",
1365 score: TypeMatch,
1366 },
1367 CompletionItem {
1368 label: "the_field",
1369 source_range: 189..189,
1370 delete: 189..189,
1371 insert: "the_field",
1372 kind: Field,
1373 detail: "u32",
1374 score: TypeAndNameMatch,
1375 },
1376 ]
1377 "###
1378 ); 1144 );
1379 } 1145 }
1380 1146
1381 #[test] 1147 #[test]
1382 fn test_struct_field_completion_in_record_lit_and_fn_call() { 1148 fn prioritize_exact_ref_match() {
1383 assert_debug_snapshot!( 1149 check_scores(
1384 do_reference_completion( 1150 r#"
1385 r" 1151struct WorldSnapshot { _f: () };
1386 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 1152fn go(world: &WorldSnapshot) { go(w<|>) }
1387 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } 1153"#,
1388 fn test(the_field: i64) -> i64 { the_field } 1154 expect![[r#"
1389 fn foo(a: A) { 1155 bn world [type+name]
1390 let b = B { 1156 st WorldSnapshot []
1391 the_field: test(a.<|>) 1157 fn go(…) []
1392 }; 1158 "#]],
1393 }
1394 ",
1395 ),
1396 @r###"
1397 [
1398 CompletionItem {
1399 label: "another_field",
1400 source_range: 239..239,
1401 delete: 239..239,
1402 insert: "another_field",
1403 kind: Field,
1404 detail: "i64",
1405 score: TypeMatch,
1406 },
1407 CompletionItem {
1408 label: "another_good_type",
1409 source_range: 239..239,
1410 delete: 239..239,
1411 insert: "another_good_type",
1412 kind: Field,
1413 detail: "u32",
1414 },
1415 CompletionItem {
1416 label: "the_field",
1417 source_range: 239..239,
1418 delete: 239..239,
1419 insert: "the_field",
1420 kind: Field,
1421 detail: "u32",
1422 },
1423 ]
1424 "###
1425 ); 1159 );
1426 } 1160 }
1427 1161
1428 #[test] 1162 #[test]
1429 fn test_struct_field_completion_in_fn_call_and_record_lit() { 1163 fn too_many_arguments() {
1430 assert_debug_snapshot!( 1164 mark::check!(too_many_arguments);
1431 do_reference_completion( 1165 check_scores(
1432 r" 1166 r#"
1433 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 1167struct Foo;
1434 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } 1168fn f(foo: &Foo) { f(foo, w<|>) }
1435 fn test(the_field: i64) -> i64 { the_field } 1169"#,
1436 fn foo(a: A) { 1170 expect![[r#"
1437 test(B { 1171 st Foo []
1438 the_field: a.<|> 1172 fn f(…) []
1439 }); 1173 bn foo []
1440 } 1174 "#]],
1441 ",
1442 ),
1443 @r###"
1444 [
1445 CompletionItem {
1446 label: "another_field",
1447 source_range: 231..231,
1448 delete: 231..231,
1449 insert: "another_field",
1450 kind: Field,
1451 detail: "i64",
1452 },
1453 CompletionItem {
1454 label: "another_good_type",
1455 source_range: 231..231,
1456 delete: 231..231,
1457 insert: "another_good_type",
1458 kind: Field,
1459 detail: "u32",
1460 score: TypeMatch,
1461 },
1462 CompletionItem {
1463 label: "the_field",
1464 source_range: 231..231,
1465 delete: 231..231,
1466 insert: "the_field",
1467 kind: Field,
1468 detail: "u32",
1469 score: TypeAndNameMatch,
1470 },
1471 ]
1472 "###
1473 ); 1175 );
1474 } 1176 }
1475 1177
1476 #[test] 1178 #[test]
1477 fn prioritize_exact_ref_match() { 1179 fn guesses_macro_braces() {
1478 assert_debug_snapshot!( 1180 check_edit(
1479 do_reference_completion( 1181 "vec!",
1480 r" 1182 r#"
1481 struct WorldSnapshot { _f: () }; 1183/// Creates a [`Vec`] containing the arguments.
1482 fn go(world: &WorldSnapshot) { 1184///
1483 go(w<|>) 1185/// ```
1484 } 1186/// let v = vec![1, 2, 3];
1485 ", 1187/// assert_eq!(v[0], 1);
1486 ), 1188/// assert_eq!(v[1], 2);
1487 @r###" 1189/// assert_eq!(v[2], 3);
1488 [ 1190/// ```
1489 CompletionItem { 1191macro_rules! vec { () => {} }
1490 label: "WorldSnapshot", 1192
1491 source_range: 71..72, 1193fn fn main() { v<|> }
1492 delete: 71..72, 1194"#,
1493 insert: "WorldSnapshot", 1195 r#"
1494 kind: Struct, 1196/// Creates a [`Vec`] containing the arguments.
1495 }, 1197///
1496 CompletionItem { 1198/// ```
1497 label: "go(…)", 1199/// let v = vec![1, 2, 3];
1498 source_range: 71..72, 1200/// assert_eq!(v[0], 1);
1499 delete: 71..72, 1201/// assert_eq!(v[1], 2);
1500 insert: "go(${1:world})$0", 1202/// assert_eq!(v[2], 3);
1501 kind: Function, 1203/// ```
1502 lookup: "go", 1204macro_rules! vec { () => {} }
1503 detail: "fn go(world: &WorldSnapshot)", 1205
1504 trigger_call_info: true, 1206fn fn main() { vec![$0] }
1505 }, 1207"#,
1506 CompletionItem {
1507 label: "world",
1508 source_range: 71..72,
1509 delete: 71..72,
1510 insert: "world",
1511 kind: Binding,
1512 detail: "&WorldSnapshot",
1513 score: TypeAndNameMatch,
1514 },
1515 ]
1516 "###
1517 ); 1208 );
1209
1210 check_edit(
1211 "foo!",
1212 r#"
1213/// Foo
1214///
1215/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
1216/// call as `let _=foo! { hello world };`
1217macro_rules! foo { () => {} }
1218fn main() { <|> }
1219"#,
1220 r#"
1221/// Foo
1222///
1223/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
1224/// call as `let _=foo! { hello world };`
1225macro_rules! foo { () => {} }
1226fn main() { foo! {$0} }
1227"#,
1228 )
1518 } 1229 }
1519} 1230}
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
index 5c01654cc..919177745 100644
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -1,7 +1,10 @@
1//! Runs completion for testing purposes. 1//! Runs completion for testing purposes.
2 2
3use hir::Semantics; 3use hir::Semantics;
4use itertools::Itertools;
4use ra_syntax::{AstNode, NodeOrToken, SyntaxElement}; 5use ra_syntax::{AstNode, NodeOrToken, SyntaxElement};
6use stdx::{format_to, trim_indent};
7use test_utils::assert_eq_text;
5 8
6use crate::{ 9use crate::{
7 completion::{completion_item::CompletionKind, CompletionConfig}, 10 completion::{completion_item::CompletionKind, CompletionConfig},
@@ -10,15 +13,15 @@ use crate::{
10}; 13};
11 14
12pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { 15pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
13 do_completion_with_options(code, kind, &CompletionConfig::default()) 16 do_completion_with_config(CompletionConfig::default(), code, kind)
14} 17}
15 18
16pub(crate) fn do_completion_with_options( 19pub(crate) fn do_completion_with_config(
20 config: CompletionConfig,
17 code: &str, 21 code: &str,
18 kind: CompletionKind, 22 kind: CompletionKind,
19 options: &CompletionConfig,
20) -> Vec<CompletionItem> { 23) -> Vec<CompletionItem> {
21 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(code, options) 24 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(config, code)
22 .into_iter() 25 .into_iter()
23 .filter(|c| c.completion_kind == kind) 26 .filter(|c| c.completion_kind == kind)
24 .collect(); 27 .collect();
@@ -27,25 +30,69 @@ pub(crate) fn do_completion_with_options(
27} 30}
28 31
29pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String { 32pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String {
30 completion_list_with_options(code, kind, &CompletionConfig::default()) 33 completion_list_with_config(CompletionConfig::default(), code, kind)
31} 34}
32 35
33pub(crate) fn completion_list_with_options( 36pub(crate) fn completion_list_with_config(
37 config: CompletionConfig,
34 code: &str, 38 code: &str,
35 kind: CompletionKind, 39 kind: CompletionKind,
36 options: &CompletionConfig,
37) -> String { 40) -> String {
38 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(code, options) 41 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(config, code)
39 .into_iter() 42 .into_iter()
40 .filter(|c| c.completion_kind == kind) 43 .filter(|c| c.completion_kind == kind)
41 .collect(); 44 .collect();
42 kind_completions.sort_by_key(|c| c.label().to_owned()); 45 kind_completions.sort_by_key(|c| c.label().to_owned());
46 let label_width = kind_completions
47 .iter()
48 .map(|it| monospace_width(it.label()))
49 .max()
50 .unwrap_or_default()
51 .min(16);
43 kind_completions 52 kind_completions
44 .into_iter() 53 .into_iter()
45 .map(|it| format!("{} {}\n", it.kind().unwrap().tag(), it.label())) 54 .map(|it| {
55 let tag = it.kind().unwrap().tag();
56 let var_name = format!("{} {}", tag, it.label());
57 let mut buf = var_name;
58 if let Some(detail) = it.detail() {
59 let width = label_width.saturating_sub(monospace_width(it.label()));
60 format_to!(buf, "{:width$} {}", "", detail, width = width);
61 }
62 format_to!(buf, "\n");
63 buf
64 })
46 .collect() 65 .collect()
47} 66}
48 67
68fn monospace_width(s: &str) -> usize {
69 s.chars().count()
70}
71
72pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
73 check_edit_with_config(CompletionConfig::default(), what, ra_fixture_before, ra_fixture_after)
74}
75
76pub(crate) fn check_edit_with_config(
77 config: CompletionConfig,
78 what: &str,
79 ra_fixture_before: &str,
80 ra_fixture_after: &str,
81) {
82 let ra_fixture_after = trim_indent(ra_fixture_after);
83 let (analysis, position) = analysis_and_position(ra_fixture_before);
84 let completions: Vec<CompletionItem> =
85 analysis.completions(&config, position).unwrap().unwrap().into();
86 let (completion,) = completions
87 .iter()
88 .filter(|it| it.lookup() == what)
89 .collect_tuple()
90 .unwrap_or_else(|| panic!("can't find {:?} completion in {:#?}", what, completions));
91 let mut actual = analysis.file_text(position.file_id).unwrap().to_string();
92 completion.text_edit().apply(&mut actual);
93 assert_eq_text!(&ra_fixture_after, &actual)
94}
95
49pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) { 96pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) {
50 let (analysis, pos) = analysis_and_position(code); 97 let (analysis, pos) = analysis_and_position(code);
51 analysis 98 analysis
@@ -58,7 +105,10 @@ pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -
58 .unwrap(); 105 .unwrap();
59} 106}
60 107
61fn get_all_completion_items(code: &str, options: &CompletionConfig) -> Vec<CompletionItem> { 108pub(crate) fn get_all_completion_items(
109 config: CompletionConfig,
110 code: &str,
111) -> Vec<CompletionItem> {
62 let (analysis, position) = analysis_and_position(code); 112 let (analysis, position) = analysis_and_position(code);
63 analysis.completions(options, position).unwrap().unwrap().into() 113 analysis.completions(&config, position).unwrap().unwrap().into()
64} 114}
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 05fb799d6..dd8a7ffd9 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -7,7 +7,7 @@
7use std::cell::RefCell; 7use std::cell::RefCell;
8 8
9use hir::{ 9use hir::{
10 diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}, 10 diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSinkBuilder},
11 HasSource, HirDisplay, Semantics, VariantDef, 11 HasSource, HirDisplay, Semantics, VariantDef,
12}; 12};
13use itertools::Itertools; 13use itertools::Itertools;
@@ -29,13 +29,18 @@ pub enum Severity {
29 WeakWarning, 29 WeakWarning,
30} 30}
31 31
32pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> { 32pub(crate) fn diagnostics(
33 db: &RootDatabase,
34 file_id: FileId,
35 enable_experimental: bool,
36) -> Vec<Diagnostic> {
33 let _p = profile("diagnostics"); 37 let _p = profile("diagnostics");
34 let sema = Semantics::new(db); 38 let sema = Semantics::new(db);
35 let parse = db.parse(file_id); 39 let parse = db.parse(file_id);
36 let mut res = Vec::new(); 40 let mut res = Vec::new();
37 41
38 res.extend(parse.errors().iter().map(|err| Diagnostic { 42 // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
43 res.extend(parse.errors().iter().take(128).map(|err| Diagnostic {
39 range: err.range(), 44 range: err.range(),
40 message: format!("Syntax Error: {}", err), 45 message: format!("Syntax Error: {}", err),
41 severity: Severity::Error, 46 severity: Severity::Error,
@@ -47,87 +52,85 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
47 check_struct_shorthand_initialization(&mut res, file_id, &node); 52 check_struct_shorthand_initialization(&mut res, file_id, &node);
48 } 53 }
49 let res = RefCell::new(res); 54 let res = RefCell::new(res);
50 let mut sink = DiagnosticSink::new(|d| { 55 let mut sink = DiagnosticSinkBuilder::new()
51 res.borrow_mut().push(Diagnostic { 56 .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
52 message: d.message(), 57 let original_file = d.source().file_id.original_file(db);
53 range: sema.diagnostics_range(d).range, 58 let fix = Fix::new(
54 severity: Severity::Error, 59 "Create module",
55 fix: None, 60 FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }
56 }) 61 .into(),
57 }) 62 );
58 .on::<hir::diagnostics::UnresolvedModule, _>(|d| { 63 res.borrow_mut().push(Diagnostic {
59 let original_file = d.source().file_id.original_file(db); 64 range: sema.diagnostics_range(d).range,
60 let fix = Fix::new( 65 message: d.message(),
61 "Create module", 66 severity: Severity::Error,
62 FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }.into(), 67 fix: Some(fix),
63 ); 68 })
64 res.borrow_mut().push(Diagnostic {
65 range: sema.diagnostics_range(d).range,
66 message: d.message(),
67 severity: Severity::Error,
68 fix: Some(fix),
69 }) 69 })
70 }) 70 .on::<hir::diagnostics::MissingFields, _>(|d| {
71 .on::<hir::diagnostics::MissingFields, _>(|d| { 71 // Note that although we could add a diagnostics to
72 // Note that although we could add a diagnostics to 72 // fill the missing tuple field, e.g :
73 // fill the missing tuple field, e.g : 73 // `struct A(usize);`
74 // `struct A(usize);` 74 // `let a = A { 0: () }`
75 // `let a = A { 0: () }` 75 // but it is uncommon usage and it should not be encouraged.
76 // but it is uncommon usage and it should not be encouraged. 76 let fix = if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
77 let fix = if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) { 77 None
78 None 78 } else {
79 } else { 79 let mut field_list = d.ast(db);
80 let mut field_list = d.ast(db); 80 for f in d.missed_fields.iter() {
81 for f in d.missed_fields.iter() { 81 let field =
82 let field = 82 make::record_field(make::name_ref(&f.to_string()), Some(make::expr_unit()));
83 make::record_field(make::name_ref(&f.to_string()), Some(make::expr_unit())); 83 field_list = field_list.append_field(&field);
84 field_list = field_list.append_field(&field); 84 }
85 }
86 85
87 let edit = { 86 let edit = {
88 let mut builder = TextEditBuilder::default(); 87 let mut builder = TextEditBuilder::default();
89 algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); 88 algo::diff(&d.ast(db).syntax(), &field_list.syntax())
90 builder.finish() 89 .into_text_edit(&mut builder);
90 builder.finish()
91 };
92 Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()))
91 }; 93 };
92 Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()))
93 };
94 94
95 res.borrow_mut().push(Diagnostic { 95 res.borrow_mut().push(Diagnostic {
96 range: sema.diagnostics_range(d).range, 96 range: sema.diagnostics_range(d).range,
97 message: d.message(), 97 message: d.message(),
98 severity: Severity::Error, 98 severity: Severity::Error,
99 fix, 99 fix,
100 }) 100 })
101 })
102 .on::<hir::diagnostics::MissingMatchArms, _>(|d| {
103 res.borrow_mut().push(Diagnostic {
104 range: sema.diagnostics_range(d).range,
105 message: d.message(),
106 severity: Severity::Error,
107 fix: None,
108 }) 101 })
109 }) 102 .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
110 .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| { 103 let node = d.ast(db);
111 let node = d.ast(db); 104 let replacement = format!("Ok({})", node.syntax());
112 let replacement = format!("Ok({})", node.syntax()); 105 let edit = TextEdit::replace(node.syntax().text_range(), replacement);
113 let edit = TextEdit::replace(node.syntax().text_range(), replacement); 106 let source_change = SourceFileEdit { file_id, edit }.into();
114 let source_change = SourceFileEdit { file_id, edit }.into(); 107 let fix = Fix::new("Wrap with ok", source_change);
115 let fix = Fix::new("Wrap with ok", source_change); 108 res.borrow_mut().push(Diagnostic {
116 res.borrow_mut().push(Diagnostic { 109 range: sema.diagnostics_range(d).range,
117 range: sema.diagnostics_range(d).range, 110 message: d.message(),
118 message: d.message(), 111 severity: Severity::Error,
119 severity: Severity::Error, 112 fix: Some(fix),
120 fix: Some(fix), 113 })
121 }) 114 })
122 }) 115 .on::<hir::diagnostics::NoSuchField, _>(|d| {
123 .on::<hir::diagnostics::NoSuchField, _>(|d| { 116 res.borrow_mut().push(Diagnostic {
124 res.borrow_mut().push(Diagnostic { 117 range: sema.diagnostics_range(d).range,
125 range: sema.diagnostics_range(d).range, 118 message: d.message(),
126 message: d.message(), 119 severity: Severity::Error,
127 severity: Severity::Error, 120 fix: missing_struct_field_fix(&sema, file_id, d),
128 fix: missing_struct_field_fix(&sema, file_id, d), 121 })
129 }) 122 })
130 }); 123 // Only collect experimental diagnostics when they're enabled.
124 .filter(|diag| !diag.is_experimental() || enable_experimental)
125 // Diagnostics not handled above get no fix and default treatment.
126 .build(|d| {
127 res.borrow_mut().push(Diagnostic {
128 message: d.message(),
129 range: sema.diagnostics_range(d).range,
130 severity: Severity::Error,
131 fix: None,
132 })
133 });
131 134
132 if let Some(m) = sema.to_module_def(file_id) { 135 if let Some(m) = sema.to_module_def(file_id) {
133 m.diagnostics(db, &mut sink); 136 m.diagnostics(db, &mut sink);
@@ -138,33 +141,38 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
138 141
139fn missing_struct_field_fix( 142fn missing_struct_field_fix(
140 sema: &Semantics<RootDatabase>, 143 sema: &Semantics<RootDatabase>,
141 file_id: FileId, 144 usage_file_id: FileId,
142 d: &hir::diagnostics::NoSuchField, 145 d: &hir::diagnostics::NoSuchField,
143) -> Option<Fix> { 146) -> Option<Fix> {
144 let record_expr = sema.ast(d); 147 let record_expr = sema.ast(d);
145 148
146 let record_lit = ast::RecordLit::cast(record_expr.syntax().parent()?.parent()?)?; 149 let record_lit = ast::RecordExpr::cast(record_expr.syntax().parent()?.parent()?)?;
147 let def_id = sema.resolve_variant(record_lit)?; 150 let def_id = sema.resolve_variant(record_lit)?;
148 let module; 151 let module;
152 let def_file_id;
149 let record_fields = match VariantDef::from(def_id) { 153 let record_fields = match VariantDef::from(def_id) {
150 VariantDef::Struct(s) => { 154 VariantDef::Struct(s) => {
151 module = s.module(sema.db); 155 module = s.module(sema.db);
152 let source = s.source(sema.db); 156 let source = s.source(sema.db);
153 let fields = source.value.field_def_list()?; 157 def_file_id = source.file_id;
154 record_field_def_list(fields)? 158 let fields = source.value.field_list()?;
159 record_field_list(fields)?
155 } 160 }
156 VariantDef::Union(u) => { 161 VariantDef::Union(u) => {
157 module = u.module(sema.db); 162 module = u.module(sema.db);
158 let source = u.source(sema.db); 163 let source = u.source(sema.db);
159 source.value.record_field_def_list()? 164 def_file_id = source.file_id;
165 source.value.record_field_list()?
160 } 166 }
161 VariantDef::EnumVariant(e) => { 167 VariantDef::EnumVariant(e) => {
162 module = e.module(sema.db); 168 module = e.module(sema.db);
163 let source = e.source(sema.db); 169 let source = e.source(sema.db);
164 let fields = source.value.field_def_list()?; 170 def_file_id = source.file_id;
165 record_field_def_list(fields)? 171 let fields = source.value.field_list()?;
172 record_field_list(fields)?
166 } 173 }
167 }; 174 };
175 let def_file_id = def_file_id.original_file(sema.db);
168 176
169 let new_field_type = sema.type_of_expr(&record_expr.expr()?)?; 177 let new_field_type = sema.type_of_expr(&record_expr.expr()?)?;
170 if new_field_type.is_unknown() { 178 if new_field_type.is_unknown() {
@@ -179,24 +187,28 @@ fn missing_struct_field_fix(
179 let last_field_syntax = last_field.syntax(); 187 let last_field_syntax = last_field.syntax();
180 let indent = IndentLevel::from_node(last_field_syntax); 188 let indent = IndentLevel::from_node(last_field_syntax);
181 189
182 let mut new_field = format!("\n{}{}", indent, new_field); 190 let mut new_field = new_field.to_string();
191 if usage_file_id != def_file_id {
192 new_field = format!("pub(crate) {}", new_field);
193 }
194 new_field = format!("\n{}{}", indent, new_field);
183 195
184 let needs_comma = !last_field_syntax.to_string().ends_with(","); 196 let needs_comma = !last_field_syntax.to_string().ends_with(',');
185 if needs_comma { 197 if needs_comma {
186 new_field = format!(",{}", new_field); 198 new_field = format!(",{}", new_field);
187 } 199 }
188 200
189 let source_change = SourceFileEdit { 201 let source_change = SourceFileEdit {
190 file_id, 202 file_id: def_file_id,
191 edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field), 203 edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field),
192 }; 204 };
193 let fix = Fix::new("Create field", source_change.into()); 205 let fix = Fix::new("Create field", source_change.into());
194 return Some(fix); 206 return Some(fix);
195 207
196 fn record_field_def_list(field_def_list: ast::FieldDefList) -> Option<ast::RecordFieldDefList> { 208 fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> {
197 match field_def_list { 209 match field_def_list {
198 ast::FieldDefList::RecordFieldDefList(it) => Some(it), 210 ast::FieldList::RecordFieldList(it) => Some(it),
199 ast::FieldDefList::TupleFieldDefList(_) => None, 211 ast::FieldList::TupleFieldList(_) => None,
200 } 212 }
201 } 213 }
202} 214}
@@ -251,8 +263,8 @@ fn check_struct_shorthand_initialization(
251 file_id: FileId, 263 file_id: FileId,
252 node: &SyntaxNode, 264 node: &SyntaxNode,
253) -> Option<()> { 265) -> Option<()> {
254 let record_lit = ast::RecordLit::cast(node.clone())?; 266 let record_lit = ast::RecordExpr::cast(node.clone())?;
255 let record_field_list = record_lit.record_field_list()?; 267 let record_field_list = record_lit.record_expr_field_list()?;
256 for record_field in record_field_list.fields() { 268 for record_field in record_field_list.fields() {
257 if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) { 269 if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) {
258 let field_name = name_ref.syntax().text().to_string(); 270 let field_name = name_ref.syntax().text().to_string();
@@ -281,54 +293,22 @@ fn check_struct_shorthand_initialization(
281 293
282#[cfg(test)] 294#[cfg(test)]
283mod tests { 295mod tests {
284 use insta::assert_debug_snapshot;
285 use ra_syntax::SourceFile;
286 use stdx::trim_indent; 296 use stdx::trim_indent;
287 use test_utils::assert_eq_text; 297 use test_utils::assert_eq_text;
288 298
289 use crate::mock_analysis::{analysis_and_position, single_file}; 299 use crate::mock_analysis::{analysis_and_position, single_file, MockAnalysis};
290 300 use expect::{expect, Expect};
291 use super::*;
292
293 type DiagnosticChecker = fn(&mut Vec<Diagnostic>, FileId, &SyntaxNode) -> Option<()>;
294
295 fn check_not_applicable(code: &str, func: DiagnosticChecker) {
296 let parse = SourceFile::parse(code);
297 let mut diagnostics = Vec::new();
298 for node in parse.tree().syntax().descendants() {
299 func(&mut diagnostics, FileId(0), &node);
300 }
301 assert!(diagnostics.is_empty());
302 }
303
304 fn check_apply(before: &str, after: &str, func: DiagnosticChecker) {
305 let parse = SourceFile::parse(before);
306 let mut diagnostics = Vec::new();
307 for node in parse.tree().syntax().descendants() {
308 func(&mut diagnostics, FileId(0), &node);
309 }
310 let diagnostic =
311 diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
312 let mut fix = diagnostic.fix.unwrap();
313 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
314 let actual = {
315 let mut actual = before.to_string();
316 edit.apply(&mut actual);
317 actual
318 };
319 assert_eq_text!(after, &actual);
320 }
321 301
322 /// Takes a multi-file input fixture with annotated cursor positions, 302 /// Takes a multi-file input fixture with annotated cursor positions,
323 /// and checks that: 303 /// and checks that:
324 /// * a diagnostic is produced 304 /// * a diagnostic is produced
325 /// * this diagnostic touches the input cursor position 305 /// * this diagnostic touches the input cursor position
326 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied 306 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
327 fn check_apply_diagnostic_fix_from_position(fixture: &str, after: &str) { 307 fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
328 let after = trim_indent(after); 308 let after = trim_indent(ra_fixture_after);
329 309
330 let (analysis, file_position) = analysis_and_position(fixture); 310 let (analysis, file_position) = analysis_and_position(ra_fixture_before);
331 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); 311 let diagnostic = analysis.diagnostics(file_position.file_id, true).unwrap().pop().unwrap();
332 let mut fix = diagnostic.fix.unwrap(); 312 let mut fix = diagnostic.fix.unwrap();
333 let edit = fix.source_change.source_file_edits.pop().unwrap().edit; 313 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
334 let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); 314 let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
@@ -348,16 +328,20 @@ mod tests {
348 ); 328 );
349 } 329 }
350 330
351 fn check_apply_diagnostic_fix(ra_fixture_before: &str, ra_fixture_after: &str) { 331 /// Checks that a diagnostic applies to the file containing the `<|>` cursor marker
332 /// which has a fix that can apply to other files.
333 fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) {
352 let ra_fixture_after = &trim_indent(ra_fixture_after); 334 let ra_fixture_after = &trim_indent(ra_fixture_after);
353 let (analysis, file_id) = single_file(ra_fixture_before); 335 let (analysis, file_pos) = analysis_and_position(ra_fixture_before);
354 let before = analysis.file_text(file_id).unwrap(); 336 let current_file_id = file_pos.file_id;
355 let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); 337 let diagnostic = analysis.diagnostics(current_file_id, true).unwrap().pop().unwrap();
356 let mut fix = diagnostic.fix.unwrap(); 338 let mut fix = diagnostic.fix.unwrap();
357 let edit = fix.source_change.source_file_edits.pop().unwrap().edit; 339 let edit = fix.source_change.source_file_edits.pop().unwrap();
340 let changed_file_id = edit.file_id;
341 let before = analysis.file_text(changed_file_id).unwrap();
358 let actual = { 342 let actual = {
359 let mut actual = before.to_string(); 343 let mut actual = before.to_string();
360 edit.apply(&mut actual); 344 edit.edit.apply(&mut actual);
361 actual 345 actual
362 }; 346 };
363 assert_eq_text!(ra_fixture_after, &actual); 347 assert_eq_text!(ra_fixture_after, &actual);
@@ -365,498 +349,411 @@ mod tests {
365 349
366 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics 350 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
367 /// apply to the file containing the cursor. 351 /// apply to the file containing the cursor.
368 fn check_no_diagnostic_for_target_file(fixture: &str) { 352 fn check_no_diagnostics(ra_fixture: &str) {
369 let (analysis, file_position) = analysis_and_position(fixture); 353 let mock = MockAnalysis::with_files(ra_fixture);
370 let diagnostics = analysis.diagnostics(file_position.file_id).unwrap(); 354 let files = mock.files().map(|(it, _)| it).collect::<Vec<_>>();
371 assert_eq!(diagnostics.len(), 0); 355 let analysis = mock.analysis();
356 let diagnostics = files
357 .into_iter()
358 .flat_map(|file_id| analysis.diagnostics(file_id, true).unwrap())
359 .collect::<Vec<_>>();
360 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
372 } 361 }
373 362
374 fn check_no_diagnostic(content: &str) { 363 fn check_expect(ra_fixture: &str, expect: Expect) {
375 let (analysis, file_id) = single_file(content); 364 let (analysis, file_id) = single_file(ra_fixture);
376 let diagnostics = analysis.diagnostics(file_id).unwrap(); 365 let diagnostics = analysis.diagnostics(file_id, true).unwrap();
377 assert_eq!(diagnostics.len(), 0, "expected no diagnostic, found one"); 366 expect.assert_debug_eq(&diagnostics)
378 } 367 }
379 368
380 #[test] 369 #[test]
381 fn test_wrap_return_type() { 370 fn test_wrap_return_type() {
382 let before = r#" 371 check_fix(
383 //- /main.rs 372 r#"
384 use core::result::Result::{self, Ok, Err}; 373//- /main.rs
374use core::result::Result::{self, Ok, Err};
385 375
386 fn div(x: i32, y: i32) -> Result<i32, ()> { 376fn div(x: i32, y: i32) -> Result<i32, ()> {
387 if y == 0 { 377 if y == 0 {
388 return Err(()); 378 return Err(());
389 } 379 }
390 x / y<|> 380 x / y<|>
391 } 381}
392 //- /core/lib.rs 382//- /core/lib.rs
393 pub mod result { 383pub mod result {
394 pub enum Result<T, E> { Ok(T), Err(E) } 384 pub enum Result<T, E> { Ok(T), Err(E) }
395 } 385}
396 "#; 386"#,
397 let after = r#" 387 r#"
398 use core::result::Result::{self, Ok, Err}; 388use core::result::Result::{self, Ok, Err};
399 389
400 fn div(x: i32, y: i32) -> Result<i32, ()> { 390fn div(x: i32, y: i32) -> Result<i32, ()> {
401 if y == 0 { 391 if y == 0 {
402 return Err(()); 392 return Err(());
403 } 393 }
404 Ok(x / y) 394 Ok(x / y)
405 } 395}
406 "#; 396"#,
407 check_apply_diagnostic_fix_from_position(before, after); 397 );
408 } 398 }
409 399
410 #[test] 400 #[test]
411 fn test_wrap_return_type_handles_generic_functions() { 401 fn test_wrap_return_type_handles_generic_functions() {
412 let before = r#" 402 check_fix(
413 //- /main.rs 403 r#"
414 use core::result::Result::{self, Ok, Err}; 404//- /main.rs
405use core::result::Result::{self, Ok, Err};
415 406
416 fn div<T>(x: T) -> Result<T, i32> { 407fn div<T>(x: T) -> Result<T, i32> {
417 if x == 0 { 408 if x == 0 {
418 return Err(7); 409 return Err(7);
419 } 410 }
420 <|>x 411 <|>x
421 } 412}
422 //- /core/lib.rs 413//- /core/lib.rs
423 pub mod result { 414pub mod result {
424 pub enum Result<T, E> { Ok(T), Err(E) } 415 pub enum Result<T, E> { Ok(T), Err(E) }
425 } 416}
426 "#; 417"#,
427 let after = r#" 418 r#"
428 use core::result::Result::{self, Ok, Err}; 419use core::result::Result::{self, Ok, Err};
429 420
430 fn div<T>(x: T) -> Result<T, i32> { 421fn div<T>(x: T) -> Result<T, i32> {
431 if x == 0 { 422 if x == 0 {
432 return Err(7); 423 return Err(7);
433 } 424 }
434 Ok(x) 425 Ok(x)
435 } 426}
436 "#; 427"#,
437 check_apply_diagnostic_fix_from_position(before, after); 428 );
438 } 429 }
439 430
440 #[test] 431 #[test]
441 fn test_wrap_return_type_handles_type_aliases() { 432 fn test_wrap_return_type_handles_type_aliases() {
442 let before = r#" 433 check_fix(
443 //- /main.rs 434 r#"
444 use core::result::Result::{self, Ok, Err}; 435//- /main.rs
436use core::result::Result::{self, Ok, Err};
445 437
446 type MyResult<T> = Result<T, ()>; 438type MyResult<T> = Result<T, ()>;
447 439
448 fn div(x: i32, y: i32) -> MyResult<i32> { 440fn div(x: i32, y: i32) -> MyResult<i32> {
449 if y == 0 { 441 if y == 0 {
450 return Err(()); 442 return Err(());
451 } 443 }
452 x <|>/ y 444 x <|>/ y
453 } 445}
454 //- /core/lib.rs 446//- /core/lib.rs
455 pub mod result { 447pub mod result {
456 pub enum Result<T, E> { Ok(T), Err(E) } 448 pub enum Result<T, E> { Ok(T), Err(E) }
457 } 449}
458 "#; 450"#,
459 let after = r#" 451 r#"
460 use core::result::Result::{self, Ok, Err}; 452use core::result::Result::{self, Ok, Err};
461 453
462 type MyResult<T> = Result<T, ()>; 454type MyResult<T> = Result<T, ()>;
463 455
464 fn div(x: i32, y: i32) -> MyResult<i32> { 456fn div(x: i32, y: i32) -> MyResult<i32> {
465 if y == 0 { 457 if y == 0 {
466 return Err(()); 458 return Err(());
467 } 459 }
468 Ok(x / y) 460 Ok(x / y)
469 } 461}
470 "#; 462"#,
471 check_apply_diagnostic_fix_from_position(before, after); 463 );
472 } 464 }
473 465
474 #[test] 466 #[test]
475 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { 467 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
476 let content = r#" 468 check_no_diagnostics(
477 //- /main.rs 469 r#"
478 use core::result::Result::{self, Ok, Err}; 470//- /main.rs
471use core::result::Result::{self, Ok, Err};
479 472
480 fn foo() -> Result<(), i32> { 473fn foo() -> Result<(), i32> { 0 }
481 0<|>
482 }
483 474
484 //- /core/lib.rs 475//- /core/lib.rs
485 pub mod result { 476pub mod result {
486 pub enum Result<T, E> { Ok(T), Err(E) } 477 pub enum Result<T, E> { Ok(T), Err(E) }
487 } 478}
488 "#; 479"#,
489 check_no_diagnostic_for_target_file(content); 480 );
490 } 481 }
491 482
492 #[test] 483 #[test]
493 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { 484 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
494 let content = r#" 485 check_no_diagnostics(
495 //- /main.rs 486 r#"
496 use core::result::Result::{self, Ok, Err}; 487//- /main.rs
488use core::result::Result::{self, Ok, Err};
497 489
498 enum SomeOtherEnum { 490enum SomeOtherEnum { Ok(i32), Err(String) }
499 Ok(i32),
500 Err(String),
501 }
502 491
503 fn foo() -> SomeOtherEnum { 492fn foo() -> SomeOtherEnum { 0 }
504 0<|>
505 }
506 493
507 //- /core/lib.rs 494//- /core/lib.rs
508 pub mod result { 495pub mod result {
509 pub enum Result<T, E> { Ok(T), Err(E) } 496 pub enum Result<T, E> { Ok(T), Err(E) }
510 } 497}
511 "#; 498"#,
512 check_no_diagnostic_for_target_file(content); 499 );
513 } 500 }
514 501
515 #[test] 502 #[test]
516 fn test_fill_struct_fields_empty() { 503 fn test_fill_struct_fields_empty() {
517 let before = r" 504 check_fix(
518 struct TestStruct { 505 r#"
519 one: i32, 506struct TestStruct { one: i32, two: i64 }
520 two: i64,
521 }
522 507
523 fn test_fn() { 508fn test_fn() {
524 let s = TestStruct{}; 509 let s = TestStruct {<|>};
525 } 510}
526 "; 511"#,
527 let after = r" 512 r#"
528 struct TestStruct { 513struct TestStruct { one: i32, two: i64 }
529 one: i32,
530 two: i64,
531 }
532 514
533 fn test_fn() { 515fn test_fn() {
534 let s = TestStruct{ one: (), two: ()}; 516 let s = TestStruct { one: (), two: ()};
535 } 517}
536 "; 518"#,
537 check_apply_diagnostic_fix(before, after); 519 );
538 } 520 }
539 521
540 #[test] 522 #[test]
541 fn test_fill_struct_fields_self() { 523 fn test_fill_struct_fields_self() {
542 let before = r" 524 check_fix(
543 struct TestStruct { 525 r#"
544 one: i32, 526struct TestStruct { one: i32 }
545 }
546 527
547 impl TestStruct { 528impl TestStruct {
548 fn test_fn() { 529 fn test_fn() { let s = Self {<|>}; }
549 let s = Self {}; 530}
550 } 531"#,
551 } 532 r#"
552 "; 533struct TestStruct { one: i32 }
553 let after = r"
554 struct TestStruct {
555 one: i32,
556 }
557 534
558 impl TestStruct { 535impl TestStruct {
559 fn test_fn() { 536 fn test_fn() { let s = Self { one: ()}; }
560 let s = Self { one: ()}; 537}
561 } 538"#,
562 } 539 );
563 ";
564 check_apply_diagnostic_fix(before, after);
565 } 540 }
566 541
567 #[test] 542 #[test]
568 fn test_fill_struct_fields_enum() { 543 fn test_fill_struct_fields_enum() {
569 let before = r" 544 check_fix(
570 enum Expr { 545 r#"
571 Bin { lhs: Box<Expr>, rhs: Box<Expr> } 546enum Expr {
572 } 547 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
548}
573 549
574 impl Expr { 550impl Expr {
575 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { 551 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
576 Expr::Bin { } 552 Expr::Bin {<|> }
577 } 553 }
578 } 554}
579 "; 555"#,
580 let after = r" 556 r#"
581 enum Expr { 557enum Expr {
582 Bin { lhs: Box<Expr>, rhs: Box<Expr> } 558 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
583 } 559}
584 560
585 impl Expr { 561impl Expr {
586 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { 562 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
587 Expr::Bin { lhs: (), rhs: () } 563 Expr::Bin { lhs: (), rhs: () }
588 } 564 }
589 } 565}
590 "; 566"#,
591 check_apply_diagnostic_fix(before, after); 567 );
592 } 568 }
593 569
594 #[test] 570 #[test]
595 fn test_fill_struct_fields_partial() { 571 fn test_fill_struct_fields_partial() {
596 let before = r" 572 check_fix(
597 struct TestStruct { 573 r#"
598 one: i32, 574struct TestStruct { one: i32, two: i64 }
599 two: i64,
600 }
601 575
602 fn test_fn() { 576fn test_fn() {
603 let s = TestStruct{ two: 2 }; 577 let s = TestStruct{ two: 2<|> };
604 } 578}
605 "; 579"#,
606 let after = r" 580 r"
607 struct TestStruct { 581struct TestStruct { one: i32, two: i64 }
608 one: i32,
609 two: i64,
610 }
611 582
612 fn test_fn() { 583fn test_fn() {
613 let s = TestStruct{ two: 2, one: () }; 584 let s = TestStruct{ two: 2, one: () };
614 } 585}
615 "; 586",
616 check_apply_diagnostic_fix(before, after); 587 );
617 } 588 }
618 589
619 #[test] 590 #[test]
620 fn test_fill_struct_fields_no_diagnostic() { 591 fn test_fill_struct_fields_no_diagnostic() {
621 let content = r" 592 check_no_diagnostics(
622 struct TestStruct { 593 r"
623 one: i32, 594 struct TestStruct { one: i32, two: i64 }
624 two: i64,
625 }
626 595
627 fn test_fn() { 596 fn test_fn() {
628 let one = 1; 597 let one = 1;
629 let s = TestStruct{ one, two: 2 }; 598 let s = TestStruct{ one, two: 2 };
630 } 599 }
631 "; 600 ",
632 601 );
633 check_no_diagnostic(content);
634 } 602 }
635 603
636 #[test] 604 #[test]
637 fn test_fill_struct_fields_no_diagnostic_on_spread() { 605 fn test_fill_struct_fields_no_diagnostic_on_spread() {
638 let content = r" 606 check_no_diagnostics(
639 struct TestStruct { 607 r"
640 one: i32, 608 struct TestStruct { one: i32, two: i64 }
641 two: i64,
642 }
643 609
644 fn test_fn() { 610 fn test_fn() {
645 let one = 1; 611 let one = 1;
646 let s = TestStruct{ ..a }; 612 let s = TestStruct{ ..a };
647 } 613 }
648 "; 614 ",
649 615 );
650 check_no_diagnostic(content);
651 } 616 }
652 617
653 #[test] 618 #[test]
654 fn test_unresolved_module_diagnostic() { 619 fn test_unresolved_module_diagnostic() {
655 let (analysis, file_id) = single_file("mod foo;"); 620 check_expect(
656 let diagnostics = analysis.diagnostics(file_id).unwrap(); 621 r#"mod foo;"#,
657 assert_debug_snapshot!(diagnostics, @r###" 622 expect![[r#"
658 [ 623 [
659 Diagnostic { 624 Diagnostic {
660 message: "unresolved module", 625 message: "unresolved module",
661 range: 0..8, 626 range: 0..8,
662 severity: Error, 627 severity: Error,
663 fix: Some( 628 fix: Some(
664 Fix { 629 Fix {
665 label: "Create module", 630 label: "Create module",
666 source_change: SourceChange { 631 source_change: SourceChange {
667 source_file_edits: [], 632 source_file_edits: [],
668 file_system_edits: [ 633 file_system_edits: [
669 CreateFile { 634 CreateFile {
670 anchor: FileId( 635 anchor: FileId(
671 1, 636 1,
672 ), 637 ),
673 dst: "foo.rs", 638 dst: "foo.rs",
639 },
640 ],
641 is_snippet: false,
674 }, 642 },
675 ], 643 },
676 is_snippet: false, 644 ),
677 },
678 }, 645 },
679 ), 646 ]
680 }, 647 "#]],
681 ] 648 );
682 "###);
683 } 649 }
684 650
685 #[test] 651 #[test]
686 fn range_mapping_out_of_macros() { 652 fn range_mapping_out_of_macros() {
687 let (analysis, file_id) = single_file( 653 // FIXME: this is very wrong, but somewhat tricky to fix.
688 r" 654 check_fix(
689 fn some() {} 655 r#"
690 fn items() {} 656fn some() {}
691 fn here() {} 657fn items() {}
658fn here() {}
692 659
693 macro_rules! id { 660macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
694 ($($tt:tt)*) => { $($tt)*};
695 }
696 661
697 fn main() { 662fn main() {
698 let _x = id![Foo { a: 42 }]; 663 let _x = id![Foo { a: <|>42 }];
699 } 664}
700 665
701 pub struct Foo { 666pub struct Foo { pub a: i32, pub b: i32 }
702 pub a: i32, 667"#,
703 pub b: i32, 668 r#"
704 } 669fn {a:42, b: ()} {}
705 ", 670fn items() {}
671fn here() {}
672
673macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
674
675fn main() {
676 let _x = id![Foo { a: 42 }];
677}
678
679pub struct Foo { pub a: i32, pub b: i32 }
680"#,
706 ); 681 );
707 let diagnostics = analysis.diagnostics(file_id).unwrap();
708 assert_debug_snapshot!(diagnostics, @r###"
709 [
710 Diagnostic {
711 message: "Missing structure fields:\n- b\n",
712 range: 127..136,
713 severity: Error,
714 fix: Some(
715 Fix {
716 label: "Fill struct fields",
717 source_change: SourceChange {
718 source_file_edits: [
719 SourceFileEdit {
720 file_id: FileId(
721 1,
722 ),
723 edit: TextEdit {
724 indels: [
725 Indel {
726 insert: "{a:42, b: ()}",
727 delete: 3..9,
728 },
729 ],
730 },
731 },
732 ],
733 file_system_edits: [],
734 is_snippet: false,
735 },
736 },
737 ),
738 },
739 ]
740 "###);
741 } 682 }
742 683
743 #[test] 684 #[test]
744 fn test_check_unnecessary_braces_in_use_statement() { 685 fn test_check_unnecessary_braces_in_use_statement() {
745 check_not_applicable( 686 check_no_diagnostics(
746 " 687 r#"
747 use a; 688use a;
748 use a::{c, d::e}; 689use a::{c, d::e};
749 ", 690"#,
750 check_unnecessary_braces_in_use_statement,
751 );
752 check_apply("use {b};", "use b;", check_unnecessary_braces_in_use_statement);
753 check_apply("use a::{c};", "use a::c;", check_unnecessary_braces_in_use_statement);
754 check_apply("use a::{self};", "use a;", check_unnecessary_braces_in_use_statement);
755 check_apply(
756 "use a::{c, d::{e}};",
757 "use a::{c, d::e};",
758 check_unnecessary_braces_in_use_statement,
759 ); 691 );
692 check_fix(r#"use {<|>b};"#, r#"use b;"#);
693 check_fix(r#"use {b<|>};"#, r#"use b;"#);
694 check_fix(r#"use a::{c<|>};"#, r#"use a::c;"#);
695 check_fix(r#"use a::{self<|>};"#, r#"use a;"#);
696 check_fix(r#"use a::{c, d::{e<|>}};"#, r#"use a::{c, d::e};"#);
760 } 697 }
761 698
762 #[test] 699 #[test]
763 fn test_check_struct_shorthand_initialization() { 700 fn test_check_struct_shorthand_initialization() {
764 check_not_applicable( 701 check_no_diagnostics(
765 r#" 702 r#"
766 struct A { 703struct A { a: &'static str }
767 a: &'static str 704fn main() { A { a: "hello" } }
768 } 705"#,
769
770 fn main() {
771 A {
772 a: "hello"
773 }
774 }
775 "#,
776 check_struct_shorthand_initialization,
777 ); 706 );
778 check_not_applicable( 707 check_no_diagnostics(
779 r#" 708 r#"
780 struct A(usize); 709struct A(usize);
781 710fn main() { A { 0: 0 } }
782 fn main() { 711"#,
783 A {
784 0: 0
785 }
786 }
787 "#,
788 check_struct_shorthand_initialization,
789 ); 712 );
790 713
791 check_apply( 714 check_fix(
792 r#" 715 r#"
793struct A { 716struct A { a: &'static str }
794 a: &'static str
795}
796
797fn main() { 717fn main() {
798 let a = "haha"; 718 let a = "haha";
799 A { 719 A { a<|>: a }
800 a: a
801 }
802} 720}
803 "#, 721"#,
804 r#" 722 r#"
805struct A { 723struct A { a: &'static str }
806 a: &'static str
807}
808
809fn main() { 724fn main() {
810 let a = "haha"; 725 let a = "haha";
811 A { 726 A { a }
812 a
813 }
814} 727}
815 "#, 728"#,
816 check_struct_shorthand_initialization,
817 ); 729 );
818 730
819 check_apply( 731 check_fix(
820 r#" 732 r#"
821struct A { 733struct A { a: &'static str, b: &'static str }
822 a: &'static str,
823 b: &'static str
824}
825
826fn main() { 734fn main() {
827 let a = "haha"; 735 let a = "haha";
828 let b = "bb"; 736 let b = "bb";
829 A { 737 A { a<|>: a, b }
830 a: a,
831 b
832 }
833} 738}
834 "#, 739"#,
835 r#" 740 r#"
836struct A { 741struct A { a: &'static str, b: &'static str }
837 a: &'static str,
838 b: &'static str
839}
840
841fn main() { 742fn main() {
842 let a = "haha"; 743 let a = "haha";
843 let b = "bb"; 744 let b = "bb";
844 A { 745 A { a, b }
845 a,
846 b
847 }
848} 746}
849 "#, 747"#,
850 check_struct_shorthand_initialization,
851 ); 748 );
852 } 749 }
853 750
854 #[test] 751 #[test]
855 fn test_add_field_from_usage() { 752 fn test_add_field_from_usage() {
856 check_apply_diagnostic_fix( 753 check_fix(
857 r" 754 r"
858fn main() { 755fn main() {
859 Foo { bar: 3, baz: false}; 756 Foo { bar: 3, baz<|>: false};
860} 757}
861struct Foo { 758struct Foo {
862 bar: i32 759 bar: i32
@@ -873,4 +770,28 @@ struct Foo {
873", 770",
874 ) 771 )
875 } 772 }
773
774 #[test]
775 fn test_add_field_in_other_file_from_usage() {
776 check_apply_diagnostic_fix_in_other_file(
777 r"
778 //- /main.rs
779 mod foo;
780
781 fn main() {
782 <|>foo::Foo { bar: 3, baz: false};
783 }
784 //- /foo.rs
785 struct Foo {
786 bar: i32
787 }
788 ",
789 r"
790 struct Foo {
791 bar: i32,
792 pub(crate) baz: bool
793 }
794 ",
795 )
796 }
876} 797}
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
index 827c094e7..fd42aa435 100644
--- a/crates/ra_ide/src/display.rs
+++ b/crates/ra_ide/src/display.rs
@@ -1,31 +1,60 @@
1//! This module contains utilities for turning SyntaxNodes and HIR types 1//! This module contains utilities for turning SyntaxNodes and HIR types
2//! into types that may be used to render in a UI. 2//! into types that may be used to render in a UI.
3 3
4mod function_signature;
5mod navigation_target; 4mod navigation_target;
6mod structure;
7mod short_label; 5mod short_label;
8 6
9use std::fmt::Display;
10
11use ra_syntax::{ 7use ra_syntax::{
12 ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, 8 ast::{self, AstNode, AttrsOwner, GenericParamsOwner, NameOwner},
13 SyntaxKind::{ATTR, COMMENT}, 9 SyntaxKind::{ATTR, COMMENT},
14}; 10};
11
12use ast::VisibilityOwner;
15use stdx::format_to; 13use stdx::format_to;
16 14
17pub use function_signature::FunctionSignature;
18pub use navigation_target::NavigationTarget; 15pub use navigation_target::NavigationTarget;
19pub use structure::{file_structure, StructureNode};
20
21pub(crate) use navigation_target::{ToNav, TryToNav}; 16pub(crate) use navigation_target::{ToNav, TryToNav};
22pub(crate) use short_label::ShortLabel; 17pub(crate) use short_label::ShortLabel;
23 18
24pub(crate) fn function_label(node: &ast::FnDef) -> String { 19pub(crate) fn function_declaration(node: &ast::Fn) -> String {
25 FunctionSignature::from(node).to_string() 20 let mut buf = String::new();
21 if let Some(vis) = node.visibility() {
22 format_to!(buf, "{} ", vis);
23 }
24 if node.async_token().is_some() {
25 format_to!(buf, "async ");
26 }
27 if node.const_token().is_some() {
28 format_to!(buf, "const ");
29 }
30 if node.unsafe_token().is_some() {
31 format_to!(buf, "unsafe ");
32 }
33 if let Some(abi) = node.abi() {
34 // Keyword `extern` is included in the string.
35 format_to!(buf, "{} ", abi);
36 }
37 if let Some(name) = node.name() {
38 format_to!(buf, "fn {}", name)
39 }
40 if let Some(type_params) = node.generic_param_list() {
41 format_to!(buf, "{}", type_params);
42 }
43 if let Some(param_list) = node.param_list() {
44 format_to!(buf, "{}", param_list);
45 }
46 if let Some(ret_type) = node.ret_type() {
47 if ret_type.ty().is_some() {
48 format_to!(buf, " {}", ret_type);
49 }
50 }
51 if let Some(where_clause) = node.where_clause() {
52 format_to!(buf, "\n{}", where_clause);
53 }
54 buf
26} 55}
27 56
28pub(crate) fn const_label(node: &ast::ConstDef) -> String { 57pub(crate) fn const_label(node: &ast::Const) -> String {
29 let label: String = node 58 let label: String = node
30 .syntax() 59 .syntax()
31 .children_with_tokens() 60 .children_with_tokens()
@@ -36,7 +65,7 @@ pub(crate) fn const_label(node: &ast::ConstDef) -> String {
36 label.trim().to_owned() 65 label.trim().to_owned()
37} 66}
38 67
39pub(crate) fn type_label(node: &ast::TypeAliasDef) -> String { 68pub(crate) fn type_label(node: &ast::TypeAlias) -> String {
40 let label: String = node 69 let label: String = node
41 .syntax() 70 .syntax()
42 .children_with_tokens() 71 .children_with_tokens()
@@ -47,51 +76,8 @@ pub(crate) fn type_label(node: &ast::TypeAliasDef) -> String {
47 label.trim().to_owned() 76 label.trim().to_owned()
48} 77}
49 78
50pub(crate) fn generic_parameters<N: TypeParamsOwner>(node: &N) -> Vec<String> {
51 let mut res = vec![];
52 if let Some(type_params) = node.type_param_list() {
53 res.extend(type_params.lifetime_params().map(|p| p.syntax().text().to_string()));
54 res.extend(type_params.type_params().map(|p| p.syntax().text().to_string()));
55 }
56 res
57}
58
59pub(crate) fn where_predicates<N: TypeParamsOwner>(node: &N) -> Vec<String> {
60 let mut res = vec![];
61 if let Some(clause) = node.where_clause() {
62 res.extend(clause.predicates().map(|p| p.syntax().text().to_string()));
63 }
64 res
65}
66
67pub(crate) fn macro_label(node: &ast::MacroCall) -> String { 79pub(crate) fn macro_label(node: &ast::MacroCall) -> String {
68 let name = node.name().map(|name| name.syntax().text().to_string()).unwrap_or_default(); 80 let name = node.name().map(|name| name.syntax().text().to_string()).unwrap_or_default();
69 let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" }; 81 let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" };
70 format!("{}macro_rules! {}", vis, name) 82 format!("{}macro_rules! {}", vis, name)
71} 83}
72
73pub(crate) fn rust_code_markup(code: &impl Display) -> String {
74 rust_code_markup_with_doc(code, None, None)
75}
76
77pub(crate) fn rust_code_markup_with_doc(
78 code: &impl Display,
79 doc: Option<&str>,
80 mod_path: Option<&str>,
81) -> String {
82 let mut buf = String::new();
83
84 if let Some(mod_path) = mod_path {
85 if !mod_path.is_empty() {
86 format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
87 }
88 }
89 format_to!(buf, "```rust\n{}\n```", code);
90
91 if let Some(doc) = doc {
92 format_to!(buf, "\n___");
93 format_to!(buf, "\n\n{}", doc);
94 }
95
96 buf
97}
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
deleted file mode 100644
index a98264fb3..000000000
--- a/crates/ra_ide/src/display/function_signature.rs
+++ /dev/null
@@ -1,334 +0,0 @@
1//! FIXME: write short doc here
2
3// FIXME: this modules relies on strings and AST way too much, and it should be
4// rewritten (matklad 2020-05-07)
5use std::{
6 convert::From,
7 fmt::{self, Display},
8};
9
10use hir::{Docs, Documentation, HasSource, HirDisplay};
11use ra_ide_db::RootDatabase;
12use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
13use stdx::{split_delim, SepBy};
14
15use crate::display::{generic_parameters, where_predicates};
16
17#[derive(Debug)]
18pub enum CallableKind {
19 Function,
20 StructConstructor,
21 VariantConstructor,
22 Macro,
23}
24
25/// Contains information about a function signature
26#[derive(Debug)]
27pub struct FunctionSignature {
28 pub kind: CallableKind,
29 /// Optional visibility
30 pub visibility: Option<String>,
31 /// Qualifiers like `async`, `unsafe`, ...
32 pub qualifier: FunctionQualifier,
33 /// Name of the function
34 pub name: Option<String>,
35 /// Documentation for the function
36 pub doc: Option<Documentation>,
37 /// Generic parameters
38 pub generic_parameters: Vec<String>,
39 /// Parameters of the function
40 pub parameters: Vec<String>,
41 /// Parameter names of the function
42 pub parameter_names: Vec<String>,
43 /// Parameter types of the function
44 pub parameter_types: Vec<String>,
45 /// Optional return type
46 pub ret_type: Option<String>,
47 /// Where predicates
48 pub where_predicates: Vec<String>,
49 /// Self param presence
50 pub has_self_param: bool,
51}
52
53#[derive(Debug, Default)]
54pub struct FunctionQualifier {
55 // `async` and `const` are mutually exclusive. Do we need to enforcing it here?
56 pub is_async: bool,
57 pub is_const: bool,
58 pub is_unsafe: bool,
59 /// The string `extern ".."`
60 pub extern_abi: Option<String>,
61}
62
63impl FunctionSignature {
64 pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self {
65 self.doc = doc;
66 self
67 }
68
69 pub(crate) fn from_hir(db: &RootDatabase, function: hir::Function) -> Self {
70 let doc = function.docs(db);
71 let ast_node = function.source(db).value;
72 FunctionSignature::from(&ast_node).with_doc_opt(doc)
73 }
74
75 pub(crate) fn from_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
76 let node: ast::StructDef = st.source(db).value;
77 if let ast::StructKind::Record(_) = node.kind() {
78 return None;
79 };
80
81 let mut params = vec![];
82 let mut parameter_types = vec![];
83 for field in st.fields(db).into_iter() {
84 let ty = field.signature_ty(db);
85 let raw_param = format!("{}", ty.display(db));
86
87 if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) {
88 parameter_types.push(param_type.to_string());
89 } else {
90 // useful when you have tuple struct
91 parameter_types.push(raw_param.clone());
92 }
93 params.push(raw_param);
94 }
95
96 Some(
97 FunctionSignature {
98 kind: CallableKind::StructConstructor,
99 visibility: node.visibility().map(|n| n.syntax().text().to_string()),
100 // Do we need `const`?
101 qualifier: Default::default(),
102 name: node.name().map(|n| n.text().to_string()),
103 ret_type: node.name().map(|n| n.text().to_string()),
104 parameters: params,
105 parameter_names: vec![],
106 parameter_types,
107 generic_parameters: generic_parameters(&node),
108 where_predicates: where_predicates(&node),
109 doc: None,
110 has_self_param: false,
111 }
112 .with_doc_opt(st.docs(db)),
113 )
114 }
115
116 pub(crate) fn from_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
117 let node: ast::EnumVariant = variant.source(db).value;
118 match node.kind() {
119 ast::StructKind::Record(_) | ast::StructKind::Unit => return None,
120 _ => (),
121 };
122
123 let parent_name = variant.parent_enum(db).name(db).to_string();
124
125 let name = format!("{}::{}", parent_name, variant.name(db));
126
127 let mut params = vec![];
128 let mut parameter_types = vec![];
129 for field in variant.fields(db).into_iter() {
130 let ty = field.signature_ty(db);
131 let raw_param = format!("{}", ty.display(db));
132 if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) {
133 parameter_types.push(param_type.to_string());
134 } else {
135 // The unwrap_or_else is useful when you have tuple
136 parameter_types.push(raw_param);
137 }
138 let name = field.name(db);
139
140 params.push(format!("{}: {}", name, ty.display(db)));
141 }
142
143 Some(
144 FunctionSignature {
145 kind: CallableKind::VariantConstructor,
146 visibility: None,
147 // Do we need `const`?
148 qualifier: Default::default(),
149 name: Some(name),
150 ret_type: None,
151 parameters: params,
152 parameter_names: vec![],
153 parameter_types,
154 generic_parameters: vec![],
155 where_predicates: vec![],
156 doc: None,
157 has_self_param: false,
158 }
159 .with_doc_opt(variant.docs(db)),
160 )
161 }
162
163 pub(crate) fn from_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
164 let node: ast::MacroCall = macro_def.source(db).value;
165
166 let params = vec![];
167
168 Some(
169 FunctionSignature {
170 kind: CallableKind::Macro,
171 visibility: None,
172 qualifier: Default::default(),
173 name: node.name().map(|n| n.text().to_string()),
174 ret_type: None,
175 parameters: params,
176 parameter_names: vec![],
177 parameter_types: vec![],
178 generic_parameters: vec![],
179 where_predicates: vec![],
180 doc: None,
181 has_self_param: false,
182 }
183 .with_doc_opt(macro_def.docs(db)),
184 )
185 }
186}
187
188impl From<&'_ ast::FnDef> for FunctionSignature {
189 fn from(node: &ast::FnDef) -> FunctionSignature {
190 fn param_list(node: &ast::FnDef) -> (bool, Vec<String>, Vec<String>) {
191 let mut res = vec![];
192 let mut res_types = vec![];
193 let mut has_self_param = false;
194 if let Some(param_list) = node.param_list() {
195 if let Some(self_param) = param_list.self_param() {
196 has_self_param = true;
197 let raw_param = self_param.syntax().text().to_string();
198
199 res_types.push(
200 raw_param
201 .split(':')
202 .nth(1)
203 .and_then(|it| it.get(1..))
204 .unwrap_or_else(|| "Self")
205 .to_string(),
206 );
207 res.push(raw_param);
208 }
209
210 // macro-generated functions are missing whitespace
211 fn fmt_param(param: ast::Param) -> String {
212 let text = param.syntax().text().to_string();
213 match split_delim(&text, ':') {
214 Some((left, right)) => format!("{}: {}", left.trim(), right.trim()),
215 _ => text,
216 }
217 }
218
219 res.extend(param_list.params().map(fmt_param));
220 res_types.extend(param_list.params().map(|param| {
221 let param_text = param.syntax().text().to_string();
222 match param_text.split(':').nth(1).and_then(|it| it.get(1..)) {
223 Some(it) => it.to_string(),
224 None => param_text,
225 }
226 }));
227 }
228 (has_self_param, res, res_types)
229 }
230
231 fn param_name_list(node: &ast::FnDef) -> Vec<String> {
232 let mut res = vec![];
233 if let Some(param_list) = node.param_list() {
234 if let Some(self_param) = param_list.self_param() {
235 res.push(self_param.syntax().text().to_string())
236 }
237
238 res.extend(
239 param_list
240 .params()
241 .map(|param| {
242 Some(
243 param
244 .pat()?
245 .syntax()
246 .descendants()
247 .find_map(ast::Name::cast)?
248 .text()
249 .to_string(),
250 )
251 })
252 .map(|param| param.unwrap_or_default()),
253 );
254 }
255 res
256 }
257
258 let (has_self_param, parameters, parameter_types) = param_list(node);
259
260 FunctionSignature {
261 kind: CallableKind::Function,
262 visibility: node.visibility().map(|n| n.syntax().text().to_string()),
263 qualifier: FunctionQualifier {
264 is_async: node.async_token().is_some(),
265 is_const: node.const_token().is_some(),
266 is_unsafe: node.unsafe_token().is_some(),
267 extern_abi: node.abi().map(|n| n.to_string()),
268 },
269 name: node.name().map(|n| n.text().to_string()),
270 ret_type: node
271 .ret_type()
272 .and_then(|r| r.type_ref())
273 .map(|n| n.syntax().text().to_string()),
274 parameters,
275 parameter_names: param_name_list(node),
276 parameter_types,
277 generic_parameters: generic_parameters(node),
278 where_predicates: where_predicates(node),
279 // docs are processed separately
280 doc: None,
281 has_self_param,
282 }
283 }
284}
285
286impl Display for FunctionSignature {
287 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
288 if let Some(t) = &self.visibility {
289 write!(f, "{} ", t)?;
290 }
291
292 if self.qualifier.is_async {
293 write!(f, "async ")?;
294 }
295
296 if self.qualifier.is_const {
297 write!(f, "const ")?;
298 }
299
300 if self.qualifier.is_unsafe {
301 write!(f, "unsafe ")?;
302 }
303
304 if let Some(extern_abi) = &self.qualifier.extern_abi {
305 // Keyword `extern` is included in the string.
306 write!(f, "{} ", extern_abi)?;
307 }
308
309 if let Some(name) = &self.name {
310 match self.kind {
311 CallableKind::Function => write!(f, "fn {}", name)?,
312 CallableKind::StructConstructor => write!(f, "struct {}", name)?,
313 CallableKind::VariantConstructor => write!(f, "{}", name)?,
314 CallableKind::Macro => write!(f, "{}!", name)?,
315 }
316 }
317
318 if !self.generic_parameters.is_empty() {
319 write!(f, "{}", self.generic_parameters.iter().sep_by(", ").surround_with("<", ">"))?;
320 }
321
322 write!(f, "{}", self.parameters.iter().sep_by(", ").surround_with("(", ")"))?;
323
324 if let Some(t) = &self.ret_type {
325 write!(f, " -> {}", t)?;
326 }
327
328 if !self.where_predicates.is_empty() {
329 write!(f, "\nwhere {}", self.where_predicates.iter().sep_by(",\n "))?;
330 }
331
332 Ok(())
333 }
334}
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index 0b52b01ab..45fbc86ef 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11 TextRange, 11 TextRange,
12}; 12};
13 13
14use crate::{FileRange, FileSymbol}; 14use crate::FileSymbol;
15 15
16use super::short_label::ShortLabel; 16use super::short_label::ShortLabel;
17 17
@@ -22,15 +22,28 @@ use super::short_label::ShortLabel;
22/// code, like a function or a struct, but this is not strictly required. 22/// code, like a function or a struct, but this is not strictly required.
23#[derive(Debug, Clone, PartialEq, Eq, Hash)] 23#[derive(Debug, Clone, PartialEq, Eq, Hash)]
24pub struct NavigationTarget { 24pub struct NavigationTarget {
25 // FIXME: use FileRange? 25 pub file_id: FileId,
26 file_id: FileId, 26 /// Range which encompasses the whole element.
27 full_range: TextRange, 27 ///
28 name: SmolStr, 28 /// Should include body, doc comments, attributes, etc.
29 kind: SyntaxKind, 29 ///
30 focus_range: Option<TextRange>, 30 /// Clients should use this range to answer "is the cursor inside the
31 container_name: Option<SmolStr>, 31 /// element?" question.
32 description: Option<String>, 32 pub full_range: TextRange,
33 docs: Option<String>, 33 /// A "most interesting" range withing the `full_range`.
34 ///
35 /// Typically, `full_range` is the whole syntax node, including doc
36 /// comments, and `focus_range` is the range of the identifier. "Most
37 /// interesting" range within the full range, typically the range of
38 /// identifier.
39 ///
40 /// Clients should place the cursor on this range when navigating to this target.
41 pub focus_range: Option<TextRange>,
42 pub name: SmolStr,
43 pub kind: SyntaxKind,
44 pub container_name: Option<SmolStr>,
45 pub description: Option<String>,
46 pub docs: Option<String>,
34} 47}
35 48
36pub(crate) trait ToNav { 49pub(crate) trait ToNav {
@@ -42,52 +55,10 @@ pub(crate) trait TryToNav {
42} 55}
43 56
44impl NavigationTarget { 57impl NavigationTarget {
45 /// When `focus_range` is specified, returns it. otherwise 58 pub fn focus_or_full_range(&self) -> TextRange {
46 /// returns `full_range`
47 pub fn range(&self) -> TextRange {
48 self.focus_range.unwrap_or(self.full_range) 59 self.focus_range.unwrap_or(self.full_range)
49 } 60 }
50 61
51 pub fn name(&self) -> &SmolStr {
52 &self.name
53 }
54
55 pub fn container_name(&self) -> Option<&SmolStr> {
56 self.container_name.as_ref()
57 }
58
59 pub fn kind(&self) -> SyntaxKind {
60 self.kind
61 }
62
63 pub fn file_id(&self) -> FileId {
64 self.file_id
65 }
66
67 pub fn file_range(&self) -> FileRange {
68 FileRange { file_id: self.file_id, range: self.full_range }
69 }
70
71 pub fn full_range(&self) -> TextRange {
72 self.full_range
73 }
74
75 pub fn docs(&self) -> Option<&str> {
76 self.docs.as_deref()
77 }
78
79 pub fn description(&self) -> Option<&str> {
80 self.description.as_deref()
81 }
82
83 /// A "most interesting" range withing the `full_range`.
84 ///
85 /// Typically, `full_range` is the whole syntax node,
86 /// including doc comments, and `focus_range` is the range of the identifier.
87 pub fn focus_range(&self) -> Option<TextRange> {
88 self.focus_range
89 }
90
91 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { 62 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
92 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); 63 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
93 if let Some(src) = module.declaration_source(db) { 64 if let Some(src) = module.declaration_source(db) {
@@ -114,17 +85,12 @@ impl NavigationTarget {
114 85
115 #[cfg(test)] 86 #[cfg(test)]
116 pub(crate) fn debug_render(&self) -> String { 87 pub(crate) fn debug_render(&self) -> String {
117 let mut buf = format!( 88 let mut buf =
118 "{} {:?} {:?} {:?}", 89 format!("{} {:?} {:?} {:?}", self.name, self.kind, self.file_id, self.full_range);
119 self.name(), 90 if let Some(focus_range) = self.focus_range {
120 self.kind(),
121 self.file_id(),
122 self.full_range()
123 );
124 if let Some(focus_range) = self.focus_range() {
125 buf.push_str(&format!(" {:?}", focus_range)) 91 buf.push_str(&format!(" {:?}", focus_range))
126 } 92 }
127 if let Some(container_name) = self.container_name() { 93 if let Some(container_name) = &self.container_name {
128 buf.push_str(&format!(" {}", container_name)) 94 buf.push_str(&format!(" {}", container_name))
129 } 95 }
130 buf 96 buf
@@ -278,16 +244,22 @@ impl ToNav for hir::Module {
278impl ToNav for hir::ImplDef { 244impl ToNav for hir::ImplDef {
279 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 245 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
280 let src = self.source(db); 246 let src = self.source(db);
281 let frange = if let Some(item) = self.is_builtin_derive(db) { 247 let derive_attr = self.is_builtin_derive(db);
248 let frange = if let Some(item) = &derive_attr {
282 original_range(db, item.syntax()) 249 original_range(db, item.syntax())
283 } else { 250 } else {
284 original_range(db, src.as_ref().map(|it| it.syntax())) 251 original_range(db, src.as_ref().map(|it| it.syntax()))
285 }; 252 };
253 let focus_range = if derive_attr.is_some() {
254 None
255 } else {
256 src.value.target_type().map(|ty| original_range(db, src.with_value(ty.syntax())).range)
257 };
286 258
287 NavigationTarget::from_syntax( 259 NavigationTarget::from_syntax(
288 frange.file_id, 260 frange.file_id,
289 "impl".into(), 261 "impl".into(),
290 None, 262 focus_range,
291 frange.range, 263 frange.range,
292 src.value.syntax().kind(), 264 src.value.syntax().kind(),
293 ) 265 )
@@ -407,16 +379,16 @@ pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option
407 379
408 match_ast! { 380 match_ast! {
409 match node { 381 match node {
410 ast::FnDef(it) => it.doc_comment_text(), 382 ast::Fn(it) => it.doc_comment_text(),
411 ast::StructDef(it) => it.doc_comment_text(), 383 ast::Struct(it) => it.doc_comment_text(),
412 ast::EnumDef(it) => it.doc_comment_text(), 384 ast::Enum(it) => it.doc_comment_text(),
413 ast::TraitDef(it) => it.doc_comment_text(), 385 ast::Trait(it) => it.doc_comment_text(),
414 ast::Module(it) => it.doc_comment_text(), 386 ast::Module(it) => it.doc_comment_text(),
415 ast::TypeAliasDef(it) => it.doc_comment_text(), 387 ast::TypeAlias(it) => it.doc_comment_text(),
416 ast::ConstDef(it) => it.doc_comment_text(), 388 ast::Const(it) => it.doc_comment_text(),
417 ast::StaticDef(it) => it.doc_comment_text(), 389 ast::Static(it) => it.doc_comment_text(),
418 ast::RecordFieldDef(it) => it.doc_comment_text(), 390 ast::RecordField(it) => it.doc_comment_text(),
419 ast::EnumVariant(it) => it.doc_comment_text(), 391 ast::Variant(it) => it.doc_comment_text(),
420 ast::MacroCall(it) => it.doc_comment_text(), 392 ast::MacroCall(it) => it.doc_comment_text(),
421 _ => None, 393 _ => None,
422 } 394 }
@@ -432,17 +404,88 @@ pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) ->
432 404
433 match_ast! { 405 match_ast! {
434 match node { 406 match node {
435 ast::FnDef(it) => it.short_label(), 407 ast::Fn(it) => it.short_label(),
436 ast::StructDef(it) => it.short_label(), 408 ast::Struct(it) => it.short_label(),
437 ast::EnumDef(it) => it.short_label(), 409 ast::Enum(it) => it.short_label(),
438 ast::TraitDef(it) => it.short_label(), 410 ast::Trait(it) => it.short_label(),
439 ast::Module(it) => it.short_label(), 411 ast::Module(it) => it.short_label(),
440 ast::TypeAliasDef(it) => it.short_label(), 412 ast::TypeAlias(it) => it.short_label(),
441 ast::ConstDef(it) => it.short_label(), 413 ast::Const(it) => it.short_label(),
442 ast::StaticDef(it) => it.short_label(), 414 ast::Static(it) => it.short_label(),
443 ast::RecordFieldDef(it) => it.short_label(), 415 ast::RecordField(it) => it.short_label(),
444 ast::EnumVariant(it) => it.short_label(), 416 ast::Variant(it) => it.short_label(),
445 _ => None, 417 _ => None,
446 } 418 }
447 } 419 }
448} 420}
421
422#[cfg(test)]
423mod tests {
424 use expect::expect;
425
426 use crate::{mock_analysis::single_file, Query};
427
428 #[test]
429 fn test_nav_for_symbol() {
430 let (analysis, _) = single_file(
431 r#"
432enum FooInner { }
433fn foo() { enum FooInner { } }
434"#,
435 );
436
437 let navs = analysis.symbol_search(Query::new("FooInner".to_string())).unwrap();
438 expect![[r#"
439 [
440 NavigationTarget {
441 file_id: FileId(
442 1,
443 ),
444 full_range: 0..17,
445 focus_range: Some(
446 5..13,
447 ),
448 name: "FooInner",
449 kind: ENUM,
450 container_name: None,
451 description: Some(
452 "enum FooInner",
453 ),
454 docs: None,
455 },
456 NavigationTarget {
457 file_id: FileId(
458 1,
459 ),
460 full_range: 29..46,
461 focus_range: Some(
462 34..42,
463 ),
464 name: "FooInner",
465 kind: ENUM,
466 container_name: Some(
467 "foo",
468 ),
469 description: Some(
470 "enum FooInner",
471 ),
472 docs: None,
473 },
474 ]
475 "#]]
476 .assert_debug_eq(&navs);
477 }
478
479 #[test]
480 fn test_world_symbols_are_case_sensitive() {
481 let (analysis, _) = single_file(
482 r#"
483fn foo() {}
484struct Foo;
485"#,
486 );
487
488 let navs = analysis.symbol_search(Query::new("foo".to_string())).unwrap();
489 assert_eq!(navs.len(), 2)
490 }
491}
diff --git a/crates/ra_ide/src/display/short_label.rs b/crates/ra_ide/src/display/short_label.rs
index d37260e96..bddf1bd47 100644
--- a/crates/ra_ide/src/display/short_label.rs
+++ b/crates/ra_ide/src/display/short_label.rs
@@ -1,37 +1,37 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_syntax::ast::{self, AstNode, NameOwner, TypeAscriptionOwner, VisibilityOwner}; 3use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
4use stdx::format_to; 4use stdx::format_to;
5 5
6pub(crate) trait ShortLabel { 6pub(crate) trait ShortLabel {
7 fn short_label(&self) -> Option<String>; 7 fn short_label(&self) -> Option<String>;
8} 8}
9 9
10impl ShortLabel for ast::FnDef { 10impl ShortLabel for ast::Fn {
11 fn short_label(&self) -> Option<String> { 11 fn short_label(&self) -> Option<String> {
12 Some(crate::display::function_label(self)) 12 Some(crate::display::function_declaration(self))
13 } 13 }
14} 14}
15 15
16impl ShortLabel for ast::StructDef { 16impl ShortLabel for ast::Struct {
17 fn short_label(&self) -> Option<String> { 17 fn short_label(&self) -> Option<String> {
18 short_label_from_node(self, "struct ") 18 short_label_from_node(self, "struct ")
19 } 19 }
20} 20}
21 21
22impl ShortLabel for ast::UnionDef { 22impl ShortLabel for ast::Union {
23 fn short_label(&self) -> Option<String> { 23 fn short_label(&self) -> Option<String> {
24 short_label_from_node(self, "union ") 24 short_label_from_node(self, "union ")
25 } 25 }
26} 26}
27 27
28impl ShortLabel for ast::EnumDef { 28impl ShortLabel for ast::Enum {
29 fn short_label(&self) -> Option<String> { 29 fn short_label(&self) -> Option<String> {
30 short_label_from_node(self, "enum ") 30 short_label_from_node(self, "enum ")
31 } 31 }
32} 32}
33 33
34impl ShortLabel for ast::TraitDef { 34impl ShortLabel for ast::Trait {
35 fn short_label(&self) -> Option<String> { 35 fn short_label(&self) -> Option<String> {
36 if self.unsafe_token().is_some() { 36 if self.unsafe_token().is_some() {
37 short_label_from_node(self, "unsafe trait ") 37 short_label_from_node(self, "unsafe trait ")
@@ -47,43 +47,43 @@ impl ShortLabel for ast::Module {
47 } 47 }
48} 48}
49 49
50impl ShortLabel for ast::TypeAliasDef { 50impl ShortLabel for ast::TypeAlias {
51 fn short_label(&self) -> Option<String> { 51 fn short_label(&self) -> Option<String> {
52 short_label_from_node(self, "type ") 52 short_label_from_node(self, "type ")
53 } 53 }
54} 54}
55 55
56impl ShortLabel for ast::ConstDef { 56impl ShortLabel for ast::Const {
57 fn short_label(&self) -> Option<String> { 57 fn short_label(&self) -> Option<String> {
58 short_label_from_ascribed_node(self, "const ") 58 short_label_from_ty(self, self.ty(), "const ")
59 } 59 }
60} 60}
61 61
62impl ShortLabel for ast::StaticDef { 62impl ShortLabel for ast::Static {
63 fn short_label(&self) -> Option<String> { 63 fn short_label(&self) -> Option<String> {
64 short_label_from_ascribed_node(self, "static ") 64 short_label_from_ty(self, self.ty(), "static ")
65 } 65 }
66} 66}
67 67
68impl ShortLabel for ast::RecordFieldDef { 68impl ShortLabel for ast::RecordField {
69 fn short_label(&self) -> Option<String> { 69 fn short_label(&self) -> Option<String> {
70 short_label_from_ascribed_node(self, "") 70 short_label_from_ty(self, self.ty(), "")
71 } 71 }
72} 72}
73 73
74impl ShortLabel for ast::EnumVariant { 74impl ShortLabel for ast::Variant {
75 fn short_label(&self) -> Option<String> { 75 fn short_label(&self) -> Option<String> {
76 Some(self.name()?.text().to_string()) 76 Some(self.name()?.text().to_string())
77 } 77 }
78} 78}
79 79
80fn short_label_from_ascribed_node<T>(node: &T, prefix: &str) -> Option<String> 80fn short_label_from_ty<T>(node: &T, ty: Option<ast::TypeRef>, prefix: &str) -> Option<String>
81where 81where
82 T: NameOwner + VisibilityOwner + TypeAscriptionOwner, 82 T: NameOwner + VisibilityOwner,
83{ 83{
84 let mut buf = short_label_from_node(node, prefix)?; 84 let mut buf = short_label_from_node(node, prefix)?;
85 85
86 if let Some(type_ref) = node.ascribed_type() { 86 if let Some(type_ref) = ty {
87 format_to!(buf, ": {}", type_ref.syntax()); 87 format_to!(buf, ": {}", type_ref.syntax());
88 } 88 }
89 89
diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs
deleted file mode 100644
index aad5a8e4d..000000000
--- a/crates/ra_ide/src/display/structure.rs
+++ /dev/null
@@ -1,438 +0,0 @@
1use ra_syntax::{
2 ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner},
3 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, WalkEvent,
4};
5
6#[derive(Debug, Clone)]
7pub struct StructureNode {
8 pub parent: Option<usize>,
9 pub label: String,
10 pub navigation_range: TextRange,
11 pub node_range: TextRange,
12 pub kind: SyntaxKind,
13 pub detail: Option<String>,
14 pub deprecated: bool,
15}
16
17// Feature: File Structure
18//
19// Provides a tree of the symbols defined in the file. Can be used to
20//
21// * fuzzy search symbol in a file (super useful)
22// * draw breadcrumbs to describe the context around the cursor
23// * draw outline of the file
24//
25// |===
26// | Editor | Shortcut
27//
28// | VS Code | kbd:[Ctrl+Shift+O]
29// |===
30pub fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
31 let mut res = Vec::new();
32 let mut stack = Vec::new();
33
34 for event in file.syntax().preorder() {
35 match event {
36 WalkEvent::Enter(node) => {
37 if let Some(mut symbol) = structure_node(&node) {
38 symbol.parent = stack.last().copied();
39 stack.push(res.len());
40 res.push(symbol);
41 }
42 }
43 WalkEvent::Leave(node) => {
44 if structure_node(&node).is_some() {
45 stack.pop().unwrap();
46 }
47 }
48 }
49 }
50 res
51}
52
53fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
54 fn decl<N: NameOwner + AttrsOwner>(node: N) -> Option<StructureNode> {
55 decl_with_detail(node, None)
56 }
57
58 fn decl_with_ascription<N: NameOwner + AttrsOwner + TypeAscriptionOwner>(
59 node: N,
60 ) -> Option<StructureNode> {
61 let ty = node.ascribed_type();
62 decl_with_type_ref(node, ty)
63 }
64
65 fn decl_with_type_ref<N: NameOwner + AttrsOwner>(
66 node: N,
67 type_ref: Option<ast::TypeRef>,
68 ) -> Option<StructureNode> {
69 let detail = type_ref.map(|type_ref| {
70 let mut detail = String::new();
71 collapse_ws(type_ref.syntax(), &mut detail);
72 detail
73 });
74 decl_with_detail(node, detail)
75 }
76
77 fn decl_with_detail<N: NameOwner + AttrsOwner>(
78 node: N,
79 detail: Option<String>,
80 ) -> Option<StructureNode> {
81 let name = node.name()?;
82
83 Some(StructureNode {
84 parent: None,
85 label: name.text().to_string(),
86 navigation_range: name.syntax().text_range(),
87 node_range: node.syntax().text_range(),
88 kind: node.syntax().kind(),
89 detail,
90 deprecated: node.attrs().filter_map(|x| x.simple_name()).any(|x| x == "deprecated"),
91 })
92 }
93
94 fn collapse_ws(node: &SyntaxNode, output: &mut String) {
95 let mut can_insert_ws = false;
96 node.text().for_each_chunk(|chunk| {
97 for line in chunk.lines() {
98 let line = line.trim();
99 if line.is_empty() {
100 if can_insert_ws {
101 output.push(' ');
102 can_insert_ws = false;
103 }
104 } else {
105 output.push_str(line);
106 can_insert_ws = true;
107 }
108 }
109 })
110 }
111
112 match_ast! {
113 match node {
114 ast::FnDef(it) => {
115 let mut detail = String::from("fn");
116 if let Some(type_param_list) = it.type_param_list() {
117 collapse_ws(type_param_list.syntax(), &mut detail);
118 }
119 if let Some(param_list) = it.param_list() {
120 collapse_ws(param_list.syntax(), &mut detail);
121 }
122 if let Some(ret_type) = it.ret_type() {
123 detail.push_str(" ");
124 collapse_ws(ret_type.syntax(), &mut detail);
125 }
126
127 decl_with_detail(it, Some(detail))
128 },
129 ast::StructDef(it) => decl(it),
130 ast::EnumDef(it) => decl(it),
131 ast::EnumVariant(it) => decl(it),
132 ast::TraitDef(it) => decl(it),
133 ast::Module(it) => decl(it),
134 ast::TypeAliasDef(it) => {
135 let ty = it.type_ref();
136 decl_with_type_ref(it, ty)
137 },
138 ast::RecordFieldDef(it) => decl_with_ascription(it),
139 ast::ConstDef(it) => decl_with_ascription(it),
140 ast::StaticDef(it) => decl_with_ascription(it),
141 ast::ImplDef(it) => {
142 let target_type = it.target_type()?;
143 let target_trait = it.target_trait();
144 let label = match target_trait {
145 None => format!("impl {}", target_type.syntax().text()),
146 Some(t) => {
147 format!("impl {} for {}", t.syntax().text(), target_type.syntax().text(),)
148 }
149 };
150
151 let node = StructureNode {
152 parent: None,
153 label,
154 navigation_range: target_type.syntax().text_range(),
155 node_range: it.syntax().text_range(),
156 kind: it.syntax().kind(),
157 detail: None,
158 deprecated: false,
159 };
160 Some(node)
161 },
162 ast::MacroCall(it) => {
163 match it.path().and_then(|it| it.segment()).and_then(|it| it.name_ref()) {
164 Some(path_segment) if path_segment.text() == "macro_rules"
165 => decl(it),
166 _ => None,
167 }
168 },
169 _ => None,
170 }
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177 use insta::assert_debug_snapshot;
178
179 #[test]
180 fn test_file_structure() {
181 let file = SourceFile::parse(
182 r#"
183struct Foo {
184 x: i32
185}
186
187mod m {
188 fn bar1() {}
189 fn bar2<T>(t: T) -> T {}
190 fn bar3<A,
191 B>(a: A,
192 b: B) -> Vec<
193 u32
194 > {}
195}
196
197enum E { X, Y(i32) }
198type T = ();
199static S: i32 = 92;
200const C: i32 = 92;
201
202impl E {}
203
204impl fmt::Debug for E {}
205
206macro_rules! mc {
207 () => {}
208}
209
210#[macro_export]
211macro_rules! mcexp {
212 () => {}
213}
214
215/// Doc comment
216macro_rules! mcexp {
217 () => {}
218}
219
220#[deprecated]
221fn obsolete() {}
222
223#[deprecated(note = "for awhile")]
224fn very_obsolete() {}
225"#,
226 )
227 .ok()
228 .unwrap();
229 let structure = file_structure(&file);
230 assert_debug_snapshot!(structure,
231 @r###"
232 [
233 StructureNode {
234 parent: None,
235 label: "Foo",
236 navigation_range: 8..11,
237 node_range: 1..26,
238 kind: STRUCT_DEF,
239 detail: None,
240 deprecated: false,
241 },
242 StructureNode {
243 parent: Some(
244 0,
245 ),
246 label: "x",
247 navigation_range: 18..19,
248 node_range: 18..24,
249 kind: RECORD_FIELD_DEF,
250 detail: Some(
251 "i32",
252 ),
253 deprecated: false,
254 },
255 StructureNode {
256 parent: None,
257 label: "m",
258 navigation_range: 32..33,
259 node_range: 28..158,
260 kind: MODULE,
261 detail: None,
262 deprecated: false,
263 },
264 StructureNode {
265 parent: Some(
266 2,
267 ),
268 label: "bar1",
269 navigation_range: 43..47,
270 node_range: 40..52,
271 kind: FN_DEF,
272 detail: Some(
273 "fn()",
274 ),
275 deprecated: false,
276 },
277 StructureNode {
278 parent: Some(
279 2,
280 ),
281 label: "bar2",
282 navigation_range: 60..64,
283 node_range: 57..81,
284 kind: FN_DEF,
285 detail: Some(
286 "fn<T>(t: T) -> T",
287 ),
288 deprecated: false,
289 },
290 StructureNode {
291 parent: Some(
292 2,
293 ),
294 label: "bar3",
295 navigation_range: 89..93,
296 node_range: 86..156,
297 kind: FN_DEF,
298 detail: Some(
299 "fn<A, B>(a: A, b: B) -> Vec< u32 >",
300 ),
301 deprecated: false,
302 },
303 StructureNode {
304 parent: None,
305 label: "E",
306 navigation_range: 165..166,
307 node_range: 160..180,
308 kind: ENUM_DEF,
309 detail: None,
310 deprecated: false,
311 },
312 StructureNode {
313 parent: Some(
314 6,
315 ),
316 label: "X",
317 navigation_range: 169..170,
318 node_range: 169..170,
319 kind: ENUM_VARIANT,
320 detail: None,
321 deprecated: false,
322 },
323 StructureNode {
324 parent: Some(
325 6,
326 ),
327 label: "Y",
328 navigation_range: 172..173,
329 node_range: 172..178,
330 kind: ENUM_VARIANT,
331 detail: None,
332 deprecated: false,
333 },
334 StructureNode {
335 parent: None,
336 label: "T",
337 navigation_range: 186..187,
338 node_range: 181..193,
339 kind: TYPE_ALIAS_DEF,
340 detail: Some(
341 "()",
342 ),
343 deprecated: false,
344 },
345 StructureNode {
346 parent: None,
347 label: "S",
348 navigation_range: 201..202,
349 node_range: 194..213,
350 kind: STATIC_DEF,
351 detail: Some(
352 "i32",
353 ),
354 deprecated: false,
355 },
356 StructureNode {
357 parent: None,
358 label: "C",
359 navigation_range: 220..221,
360 node_range: 214..232,
361 kind: CONST_DEF,
362 detail: Some(
363 "i32",
364 ),
365 deprecated: false,
366 },
367 StructureNode {
368 parent: None,
369 label: "impl E",
370 navigation_range: 239..240,
371 node_range: 234..243,
372 kind: IMPL_DEF,
373 detail: None,
374 deprecated: false,
375 },
376 StructureNode {
377 parent: None,
378 label: "impl fmt::Debug for E",
379 navigation_range: 265..266,
380 node_range: 245..269,
381 kind: IMPL_DEF,
382 detail: None,
383 deprecated: false,
384 },
385 StructureNode {
386 parent: None,
387 label: "mc",
388 navigation_range: 284..286,
389 node_range: 271..303,
390 kind: MACRO_CALL,
391 detail: None,
392 deprecated: false,
393 },
394 StructureNode {
395 parent: None,
396 label: "mcexp",
397 navigation_range: 334..339,
398 node_range: 305..356,
399 kind: MACRO_CALL,
400 detail: None,
401 deprecated: false,
402 },
403 StructureNode {
404 parent: None,
405 label: "mcexp",
406 navigation_range: 387..392,
407 node_range: 358..409,
408 kind: MACRO_CALL,
409 detail: None,
410 deprecated: false,
411 },
412 StructureNode {
413 parent: None,
414 label: "obsolete",
415 navigation_range: 428..436,
416 node_range: 411..441,
417 kind: FN_DEF,
418 detail: Some(
419 "fn()",
420 ),
421 deprecated: true,
422 },
423 StructureNode {
424 parent: None,
425 label: "very_obsolete",
426 navigation_range: 481..494,
427 node_range: 443..499,
428 kind: FN_DEF,
429 detail: Some(
430 "fn()",
431 ),
432 deprecated: true,
433 },
434 ]
435 "###
436 );
437 }
438}
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs
index 54a47aac0..043515f54 100644
--- a/crates/ra_ide/src/expand_macro.rs
+++ b/crates/ra_ide/src/expand_macro.rs
@@ -2,7 +2,9 @@ use hir::Semantics;
2use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
3use ra_syntax::{ 3use ra_syntax::{
4 algo::{find_node_at_offset, SyntaxRewriter}, 4 algo::{find_node_at_offset, SyntaxRewriter},
5 ast, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent, T, 5 ast, AstNode, NodeOrToken, SyntaxKind,
6 SyntaxKind::*,
7 SyntaxNode, WalkEvent, T,
6}; 8};
7 9
8use crate::FilePosition; 10use crate::FilePosition;
@@ -65,8 +67,6 @@ fn expand_macro_recur(
65// FIXME: It would also be cool to share logic here and in the mbe tests, 67// FIXME: It would also be cool to share logic here and in the mbe tests,
66// which are pretty unreadable at the moment. 68// which are pretty unreadable at the moment.
67fn insert_whitespaces(syn: SyntaxNode) -> String { 69fn insert_whitespaces(syn: SyntaxNode) -> String {
68 use SyntaxKind::*;
69
70 let mut res = String::new(); 70 let mut res = String::new();
71 let mut token_iter = syn 71 let mut token_iter = syn
72 .preorder_with_tokens() 72 .preorder_with_tokens()
@@ -120,175 +120,164 @@ fn insert_whitespaces(syn: SyntaxNode) -> String {
120 120
121#[cfg(test)] 121#[cfg(test)]
122mod tests { 122mod tests {
123 use insta::assert_snapshot; 123 use expect::{expect, Expect};
124 124
125 use crate::mock_analysis::analysis_and_position; 125 use crate::mock_analysis::analysis_and_position;
126 126
127 use super::*; 127 fn check(ra_fixture: &str, expect: Expect) {
128 128 let (analysis, pos) = analysis_and_position(ra_fixture);
129 fn check_expand_macro(fixture: &str) -> ExpandedMacro { 129 let expansion = analysis.expand_macro(pos).unwrap().unwrap();
130 let (analysis, pos) = analysis_and_position(fixture); 130 let actual = format!("{}\n{}", expansion.name, expansion.expansion);
131 analysis.expand_macro(pos).unwrap().unwrap() 131 expect.assert_eq(&actual);
132 } 132 }
133 133
134 #[test] 134 #[test]
135 fn macro_expand_recursive_expansion() { 135 fn macro_expand_recursive_expansion() {
136 let res = check_expand_macro( 136 check(
137 r#" 137 r#"
138 //- /lib.rs 138macro_rules! bar {
139 macro_rules! bar { 139 () => { fn b() {} }
140 () => { fn b() {} } 140}
141 } 141macro_rules! foo {
142 macro_rules! foo { 142 () => { bar!(); }
143 () => { bar!(); } 143}
144 } 144macro_rules! baz {
145 macro_rules! baz { 145 () => { foo!(); }
146 () => { foo!(); } 146}
147 } 147f<|>oo!();
148 f<|>oo!(); 148"#,
149 "#, 149 expect![[r#"
150 foo
151 fn b(){}
152 "#]],
150 ); 153 );
151
152 assert_eq!(res.name, "foo");
153 assert_snapshot!(res.expansion, @r###"
154fn b(){}
155"###);
156 } 154 }
157 155
158 #[test] 156 #[test]
159 fn macro_expand_multiple_lines() { 157 fn macro_expand_multiple_lines() {
160 let res = check_expand_macro( 158 check(
161 r#" 159 r#"
162 //- /lib.rs 160macro_rules! foo {
163 macro_rules! foo { 161 () => {
164 () => { 162 fn some_thing() -> u32 {
165 fn some_thing() -> u32 { 163 let a = 0;
166 let a = 0; 164 a + 10
167 a + 10
168 }
169 }
170 } 165 }
171 f<|>oo!(); 166 }
167}
168f<|>oo!();
172 "#, 169 "#,
170 expect![[r#"
171 foo
172 fn some_thing() -> u32 {
173 let a = 0;
174 a+10
175 }"#]],
173 ); 176 );
174
175 assert_eq!(res.name, "foo");
176 assert_snapshot!(res.expansion, @r###"
177fn some_thing() -> u32 {
178 let a = 0;
179 a+10
180}
181"###);
182 } 177 }
183 178
184 #[test] 179 #[test]
185 fn macro_expand_match_ast() { 180 fn macro_expand_match_ast() {
186 let res = check_expand_macro( 181 check(
187 r#" 182 r#"
188 //- /lib.rs 183macro_rules! match_ast {
189 macro_rules! match_ast { 184 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
190 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; 185 (match ($node:expr) {
186 $( ast::$ast:ident($it:ident) => $res:block, )*
187 _ => $catch_all:expr $(,)?
188 }) => {{
189 $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )*
190 { $catch_all }
191 }};
192}
191 193
192 (match ($node:expr) { 194fn main() {
193 $( ast::$ast:ident($it:ident) => $res:block, )* 195 mat<|>ch_ast! {
194 _ => $catch_all:expr $(,)? 196 match container {
195 }) => {{ 197 ast::TraitDef(it) => {},
196 $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )* 198 ast::ImplDef(it) => {},
197 { $catch_all } 199 _ => { continue },
198 }};
199 } 200 }
200
201 fn main() {
202 mat<|>ch_ast! {
203 match container {
204 ast::TraitDef(it) => {},
205 ast::ImplDef(it) => {},
206 _ => { continue },
207 }
208 }
209 }
210 "#,
211 );
212
213 assert_eq!(res.name, "match_ast");
214 assert_snapshot!(res.expansion, @r###"
215{
216 if let Some(it) = ast::TraitDef::cast(container.clone()){}
217 else if let Some(it) = ast::ImplDef::cast(container.clone()){}
218 else {
219 {
220 continue
221 } 201 }
222 }
223} 202}
224"###); 203"#,
204 expect![[r#"
205 match_ast
206 {
207 if let Some(it) = ast::TraitDef::cast(container.clone()){}
208 else if let Some(it) = ast::ImplDef::cast(container.clone()){}
209 else {
210 {
211 continue
212 }
213 }
214 }"#]],
215 );
225 } 216 }
226 217
227 #[test] 218 #[test]
228 fn macro_expand_match_ast_inside_let_statement() { 219 fn macro_expand_match_ast_inside_let_statement() {
229 let res = check_expand_macro( 220 check(
230 r#" 221 r#"
231 //- /lib.rs 222macro_rules! match_ast {
232 macro_rules! match_ast { 223 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
233 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; 224 (match ($node:expr) {}) => {{}};
234 (match ($node:expr) {}) => {{}}; 225}
235 }
236 226
237 fn main() { 227fn main() {
238 let p = f(|it| { 228 let p = f(|it| {
239 let res = mat<|>ch_ast! { match c {}}; 229 let res = mat<|>ch_ast! { match c {}};
240 Some(res) 230 Some(res)
241 })?; 231 })?;
242 } 232}
243 "#, 233"#,
234 expect![[r#"
235 match_ast
236 {}
237 "#]],
244 ); 238 );
245
246 assert_eq!(res.name, "match_ast");
247 assert_snapshot!(res.expansion, @r###"{}"###);
248 } 239 }
249 240
250 #[test] 241 #[test]
251 fn macro_expand_inner_macro_fail_to_expand() { 242 fn macro_expand_inner_macro_fail_to_expand() {
252 let res = check_expand_macro( 243 check(
253 r#" 244 r#"
254 //- /lib.rs 245macro_rules! bar {
255 macro_rules! bar { 246 (BAD) => {};
256 (BAD) => {}; 247}
257 } 248macro_rules! foo {
258 macro_rules! foo { 249 () => {bar!()};
259 () => {bar!()}; 250}
260 }
261 251
262 fn main() { 252fn main() {
263 let res = fo<|>o!(); 253 let res = fo<|>o!();
264 } 254}
265 "#, 255"#,
256 expect![[r#"
257 foo
258 "#]],
266 ); 259 );
267
268 assert_eq!(res.name, "foo");
269 assert_snapshot!(res.expansion, @r###""###);
270 } 260 }
271 261
272 #[test] 262 #[test]
273 fn macro_expand_with_dollar_crate() { 263 fn macro_expand_with_dollar_crate() {
274 let res = check_expand_macro( 264 check(
275 r#" 265 r#"
276 //- /lib.rs 266#[macro_export]
277 #[macro_export] 267macro_rules! bar {
278 macro_rules! bar { 268 () => {0};
279 () => {0}; 269}
280 } 270macro_rules! foo {
281 macro_rules! foo { 271 () => {$crate::bar!()};
282 () => {$crate::bar!()}; 272}
283 }
284 273
285 fn main() { 274fn main() {
286 let res = fo<|>o!(); 275 let res = fo<|>o!();
287 } 276}
288 "#, 277"#,
278 expect![[r#"
279 foo
280 0 "#]],
289 ); 281 );
290
291 assert_eq!(res.name, "foo");
292 assert_snapshot!(res.expansion, @r###"0"###);
293 } 282 }
294} 283}
diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs
index 8a6b3ea99..fc81b48cc 100644
--- a/crates/ra_ide/src/extend_selection.rs
+++ b/crates/ra_ide/src/extend_selection.rs
@@ -39,12 +39,12 @@ fn try_extend_selection(
39 let list_kinds = [ 39 let list_kinds = [
40 RECORD_FIELD_PAT_LIST, 40 RECORD_FIELD_PAT_LIST,
41 MATCH_ARM_LIST, 41 MATCH_ARM_LIST,
42 RECORD_FIELD_DEF_LIST,
43 TUPLE_FIELD_DEF_LIST,
44 RECORD_FIELD_LIST, 42 RECORD_FIELD_LIST,
45 ENUM_VARIANT_LIST, 43 TUPLE_FIELD_LIST,
44 RECORD_EXPR_FIELD_LIST,
45 VARIANT_LIST,
46 USE_TREE_LIST, 46 USE_TREE_LIST,
47 TYPE_PARAM_LIST, 47 GENERIC_PARAM_LIST,
48 TYPE_ARG_LIST, 48 TYPE_ARG_LIST,
49 TYPE_BOUND_LIST, 49 TYPE_BOUND_LIST,
50 PARAM_LIST, 50 PARAM_LIST,
diff --git a/crates/ra_ide/src/file_structure.rs b/crates/ra_ide/src/file_structure.rs
new file mode 100644
index 000000000..91765140a
--- /dev/null
+++ b/crates/ra_ide/src/file_structure.rs
@@ -0,0 +1,431 @@
1use ra_syntax::{
2 ast::{self, AttrsOwner, GenericParamsOwner, NameOwner},
3 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, WalkEvent,
4};
5
6#[derive(Debug, Clone)]
7pub struct StructureNode {
8 pub parent: Option<usize>,
9 pub label: String,
10 pub navigation_range: TextRange,
11 pub node_range: TextRange,
12 pub kind: SyntaxKind,
13 pub detail: Option<String>,
14 pub deprecated: bool,
15}
16
17// Feature: File Structure
18//
19// Provides a tree of the symbols defined in the file. Can be used to
20//
21// * fuzzy search symbol in a file (super useful)
22// * draw breadcrumbs to describe the context around the cursor
23// * draw outline of the file
24//
25// |===
26// | Editor | Shortcut
27//
28// | VS Code | kbd:[Ctrl+Shift+O]
29// |===
30pub fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
31 let mut res = Vec::new();
32 let mut stack = Vec::new();
33
34 for event in file.syntax().preorder() {
35 match event {
36 WalkEvent::Enter(node) => {
37 if let Some(mut symbol) = structure_node(&node) {
38 symbol.parent = stack.last().copied();
39 stack.push(res.len());
40 res.push(symbol);
41 }
42 }
43 WalkEvent::Leave(node) => {
44 if structure_node(&node).is_some() {
45 stack.pop().unwrap();
46 }
47 }
48 }
49 }
50 res
51}
52
53fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
54 fn decl<N: NameOwner + AttrsOwner>(node: N) -> Option<StructureNode> {
55 decl_with_detail(&node, None)
56 }
57
58 fn decl_with_type_ref<N: NameOwner + AttrsOwner>(
59 node: &N,
60 type_ref: Option<ast::TypeRef>,
61 ) -> Option<StructureNode> {
62 let detail = type_ref.map(|type_ref| {
63 let mut detail = String::new();
64 collapse_ws(type_ref.syntax(), &mut detail);
65 detail
66 });
67 decl_with_detail(node, detail)
68 }
69
70 fn decl_with_detail<N: NameOwner + AttrsOwner>(
71 node: &N,
72 detail: Option<String>,
73 ) -> Option<StructureNode> {
74 let name = node.name()?;
75
76 Some(StructureNode {
77 parent: None,
78 label: name.text().to_string(),
79 navigation_range: name.syntax().text_range(),
80 node_range: node.syntax().text_range(),
81 kind: node.syntax().kind(),
82 detail,
83 deprecated: node.attrs().filter_map(|x| x.simple_name()).any(|x| x == "deprecated"),
84 })
85 }
86
87 fn collapse_ws(node: &SyntaxNode, output: &mut String) {
88 let mut can_insert_ws = false;
89 node.text().for_each_chunk(|chunk| {
90 for line in chunk.lines() {
91 let line = line.trim();
92 if line.is_empty() {
93 if can_insert_ws {
94 output.push(' ');
95 can_insert_ws = false;
96 }
97 } else {
98 output.push_str(line);
99 can_insert_ws = true;
100 }
101 }
102 })
103 }
104
105 match_ast! {
106 match node {
107 ast::Fn(it) => {
108 let mut detail = String::from("fn");
109 if let Some(type_param_list) = it.generic_param_list() {
110 collapse_ws(type_param_list.syntax(), &mut detail);
111 }
112 if let Some(param_list) = it.param_list() {
113 collapse_ws(param_list.syntax(), &mut detail);
114 }
115 if let Some(ret_type) = it.ret_type() {
116 detail.push_str(" ");
117 collapse_ws(ret_type.syntax(), &mut detail);
118 }
119
120 decl_with_detail(&it, Some(detail))
121 },
122 ast::Struct(it) => decl(it),
123 ast::Union(it) => decl(it),
124 ast::Enum(it) => decl(it),
125 ast::Variant(it) => decl(it),
126 ast::Trait(it) => decl(it),
127 ast::Module(it) => decl(it),
128 ast::TypeAlias(it) => decl_with_type_ref(&it, it.ty()),
129 ast::RecordField(it) => decl_with_type_ref(&it, it.ty()),
130 ast::Const(it) => decl_with_type_ref(&it, it.ty()),
131 ast::Static(it) => decl_with_type_ref(&it, it.ty()),
132 ast::Impl(it) => {
133 let target_type = it.target_type()?;
134 let target_trait = it.target_trait();
135 let label = match target_trait {
136 None => format!("impl {}", target_type.syntax().text()),
137 Some(t) => {
138 format!("impl {} for {}", t.syntax().text(), target_type.syntax().text(),)
139 }
140 };
141
142 let node = StructureNode {
143 parent: None,
144 label,
145 navigation_range: target_type.syntax().text_range(),
146 node_range: it.syntax().text_range(),
147 kind: it.syntax().kind(),
148 detail: None,
149 deprecated: false,
150 };
151 Some(node)
152 },
153 ast::MacroCall(it) => {
154 match it.path().and_then(|it| it.segment()).and_then(|it| it.name_ref()) {
155 Some(path_segment) if path_segment.text() == "macro_rules"
156 => decl(it),
157 _ => None,
158 }
159 },
160 _ => None,
161 }
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use expect::{expect, Expect};
168
169 use super::*;
170
171 fn check(ra_fixture: &str, expect: Expect) {
172 let file = SourceFile::parse(ra_fixture).ok().unwrap();
173 let structure = file_structure(&file);
174 expect.assert_debug_eq(&structure)
175 }
176
177 #[test]
178 fn test_file_structure() {
179 check(
180 r#"
181struct Foo {
182 x: i32
183}
184
185mod m {
186 fn bar1() {}
187 fn bar2<T>(t: T) -> T {}
188 fn bar3<A,
189 B>(a: A,
190 b: B) -> Vec<
191 u32
192 > {}
193}
194
195enum E { X, Y(i32) }
196type T = ();
197static S: i32 = 92;
198const C: i32 = 92;
199
200impl E {}
201
202impl fmt::Debug for E {}
203
204macro_rules! mc {
205 () => {}
206}
207
208#[macro_export]
209macro_rules! mcexp {
210 () => {}
211}
212
213/// Doc comment
214macro_rules! mcexp {
215 () => {}
216}
217
218#[deprecated]
219fn obsolete() {}
220
221#[deprecated(note = "for awhile")]
222fn very_obsolete() {}
223"#,
224 expect![[r#"
225 [
226 StructureNode {
227 parent: None,
228 label: "Foo",
229 navigation_range: 8..11,
230 node_range: 1..26,
231 kind: STRUCT,
232 detail: None,
233 deprecated: false,
234 },
235 StructureNode {
236 parent: Some(
237 0,
238 ),
239 label: "x",
240 navigation_range: 18..19,
241 node_range: 18..24,
242 kind: RECORD_FIELD,
243 detail: Some(
244 "i32",
245 ),
246 deprecated: false,
247 },
248 StructureNode {
249 parent: None,
250 label: "m",
251 navigation_range: 32..33,
252 node_range: 28..158,
253 kind: MODULE,
254 detail: None,
255 deprecated: false,
256 },
257 StructureNode {
258 parent: Some(
259 2,
260 ),
261 label: "bar1",
262 navigation_range: 43..47,
263 node_range: 40..52,
264 kind: FN,
265 detail: Some(
266 "fn()",
267 ),
268 deprecated: false,
269 },
270 StructureNode {
271 parent: Some(
272 2,
273 ),
274 label: "bar2",
275 navigation_range: 60..64,
276 node_range: 57..81,
277 kind: FN,
278 detail: Some(
279 "fn<T>(t: T) -> T",
280 ),
281 deprecated: false,
282 },
283 StructureNode {
284 parent: Some(
285 2,
286 ),
287 label: "bar3",
288 navigation_range: 89..93,
289 node_range: 86..156,
290 kind: FN,
291 detail: Some(
292 "fn<A, B>(a: A, b: B) -> Vec< u32 >",
293 ),
294 deprecated: false,
295 },
296 StructureNode {
297 parent: None,
298 label: "E",
299 navigation_range: 165..166,
300 node_range: 160..180,
301 kind: ENUM,
302 detail: None,
303 deprecated: false,
304 },
305 StructureNode {
306 parent: Some(
307 6,
308 ),
309 label: "X",
310 navigation_range: 169..170,
311 node_range: 169..170,
312 kind: VARIANT,
313 detail: None,
314 deprecated: false,
315 },
316 StructureNode {
317 parent: Some(
318 6,
319 ),
320 label: "Y",
321 navigation_range: 172..173,
322 node_range: 172..178,
323 kind: VARIANT,
324 detail: None,
325 deprecated: false,
326 },
327 StructureNode {
328 parent: None,
329 label: "T",
330 navigation_range: 186..187,
331 node_range: 181..193,
332 kind: TYPE_ALIAS,
333 detail: Some(
334 "()",
335 ),
336 deprecated: false,
337 },
338 StructureNode {
339 parent: None,
340 label: "S",
341 navigation_range: 201..202,
342 node_range: 194..213,
343 kind: STATIC,
344 detail: Some(
345 "i32",
346 ),
347 deprecated: false,
348 },
349 StructureNode {
350 parent: None,
351 label: "C",
352 navigation_range: 220..221,
353 node_range: 214..232,
354 kind: CONST,
355 detail: Some(
356 "i32",
357 ),
358 deprecated: false,
359 },
360 StructureNode {
361 parent: None,
362 label: "impl E",
363 navigation_range: 239..240,
364 node_range: 234..243,
365 kind: IMPL,
366 detail: None,
367 deprecated: false,
368 },
369 StructureNode {
370 parent: None,
371 label: "impl fmt::Debug for E",
372 navigation_range: 265..266,
373 node_range: 245..269,
374 kind: IMPL,
375 detail: None,
376 deprecated: false,
377 },
378 StructureNode {
379 parent: None,
380 label: "mc",
381 navigation_range: 284..286,
382 node_range: 271..303,
383 kind: MACRO_CALL,
384 detail: None,
385 deprecated: false,
386 },
387 StructureNode {
388 parent: None,
389 label: "mcexp",
390 navigation_range: 334..339,
391 node_range: 305..356,
392 kind: MACRO_CALL,
393 detail: None,
394 deprecated: false,
395 },
396 StructureNode {
397 parent: None,
398 label: "mcexp",
399 navigation_range: 387..392,
400 node_range: 358..409,
401 kind: MACRO_CALL,
402 detail: None,
403 deprecated: false,
404 },
405 StructureNode {
406 parent: None,
407 label: "obsolete",
408 navigation_range: 428..436,
409 node_range: 411..441,
410 kind: FN,
411 detail: Some(
412 "fn()",
413 ),
414 deprecated: true,
415 },
416 StructureNode {
417 parent: None,
418 label: "very_obsolete",
419 navigation_range: 481..494,
420 node_range: 443..499,
421 kind: FN,
422 detail: Some(
423 "fn()",
424 ),
425 deprecated: true,
426 },
427 ]
428 "#]],
429 );
430 }
431}
diff --git a/crates/ra_ide/src/folding_ranges.rs b/crates/ra_ide/src/folding_ranges.rs
index 8657377de..5a6e17936 100644
--- a/crates/ra_ide/src/folding_ranges.rs
+++ b/crates/ra_ide/src/folding_ranges.rs
@@ -15,6 +15,7 @@ pub enum FoldKind {
15 Imports, 15 Imports,
16 Mods, 16 Mods,
17 Block, 17 Block,
18 ArgList,
18} 19}
19 20
20#[derive(Debug)] 21#[derive(Debug)]
@@ -57,7 +58,7 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
57 } 58 }
58 NodeOrToken::Node(node) => { 59 NodeOrToken::Node(node) => {
59 // Fold groups of imports 60 // Fold groups of imports
60 if node.kind() == USE_ITEM && !visited_imports.contains(&node) { 61 if node.kind() == USE && !visited_imports.contains(&node) {
61 if let Some(range) = contiguous_range_for_group(&node, &mut visited_imports) { 62 if let Some(range) = contiguous_range_for_group(&node, &mut visited_imports) {
62 res.push(Fold { range, kind: FoldKind::Imports }) 63 res.push(Fold { range, kind: FoldKind::Imports })
63 } 64 }
@@ -82,15 +83,17 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
82fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> { 83fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
83 match kind { 84 match kind {
84 COMMENT => Some(FoldKind::Comment), 85 COMMENT => Some(FoldKind::Comment),
85 USE_ITEM => Some(FoldKind::Imports), 86 USE => Some(FoldKind::Imports),
86 RECORD_FIELD_DEF_LIST 87 ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList),
88 RECORD_FIELD_LIST
87 | RECORD_FIELD_PAT_LIST 89 | RECORD_FIELD_PAT_LIST
90 | RECORD_EXPR_FIELD_LIST
88 | ITEM_LIST 91 | ITEM_LIST
89 | EXTERN_ITEM_LIST 92 | EXTERN_ITEM_LIST
90 | USE_TREE_LIST 93 | USE_TREE_LIST
91 | BLOCK_EXPR 94 | BLOCK_EXPR
92 | MATCH_ARM_LIST 95 | MATCH_ARM_LIST
93 | ENUM_VARIANT_LIST 96 | VARIANT_LIST
94 | TOKEN_TREE => Some(FoldKind::Block), 97 | TOKEN_TREE => Some(FoldKind::Block),
95 _ => None, 98 _ => None,
96 } 99 }
@@ -196,89 +199,85 @@ fn contiguous_range_for_comment(
196 199
197#[cfg(test)] 200#[cfg(test)]
198mod tests { 201mod tests {
202 use test_utils::extract_tags;
203
199 use super::*; 204 use super::*;
200 use test_utils::extract_ranges;
201 205
202 fn do_check(text: &str, fold_kinds: &[FoldKind]) { 206 fn check(ra_fixture: &str) {
203 let (ranges, text) = extract_ranges(text, "fold"); 207 let (ranges, text) = extract_tags(ra_fixture, "fold");
208
204 let parse = SourceFile::parse(&text); 209 let parse = SourceFile::parse(&text);
205 let folds = folding_ranges(&parse.tree()); 210 let folds = folding_ranges(&parse.tree());
206
207 assert_eq!( 211 assert_eq!(
208 folds.len(), 212 folds.len(),
209 ranges.len(), 213 ranges.len(),
210 "The amount of folds is different than the expected amount" 214 "The amount of folds is different than the expected amount"
211 ); 215 );
212 assert_eq!( 216
213 folds.len(), 217 for (fold, (range, attr)) in folds.iter().zip(ranges.into_iter()) {
214 fold_kinds.len(),
215 "The amount of fold kinds is different than the expected amount"
216 );
217 for ((fold, range), fold_kind) in
218 folds.iter().zip(ranges.into_iter()).zip(fold_kinds.iter())
219 {
220 assert_eq!(fold.range.start(), range.start()); 218 assert_eq!(fold.range.start(), range.start());
221 assert_eq!(fold.range.end(), range.end()); 219 assert_eq!(fold.range.end(), range.end());
222 assert_eq!(&fold.kind, fold_kind); 220
221 let kind = match fold.kind {
222 FoldKind::Comment => "comment",
223 FoldKind::Imports => "imports",
224 FoldKind::Mods => "mods",
225 FoldKind::Block => "block",
226 FoldKind::ArgList => "arglist",
227 };
228 assert_eq!(kind, &attr.unwrap());
223 } 229 }
224 } 230 }
225 231
226 #[test] 232 #[test]
227 fn test_fold_comments() { 233 fn test_fold_comments() {
228 let text = r#" 234 check(
229<fold>// Hello 235 r#"
236<fold comment>// Hello
230// this is a multiline 237// this is a multiline
231// comment 238// comment
232//</fold> 239//</fold>
233 240
234// But this is not 241// But this is not
235 242
236fn main() <fold>{ 243fn main() <fold block>{
237 <fold>// We should 244 <fold comment>// We should
238 // also 245 // also
239 // fold 246 // fold
240 // this one.</fold> 247 // this one.</fold>
241 <fold>//! But this one is different 248 <fold comment>//! But this one is different
242 //! because it has another flavor</fold> 249 //! because it has another flavor</fold>
243 <fold>/* As does this 250 <fold comment>/* As does this
244 multiline comment */</fold> 251 multiline comment */</fold>
245}</fold>"#; 252}</fold>"#,
246 253 );
247 let fold_kinds = &[
248 FoldKind::Comment,
249 FoldKind::Block,
250 FoldKind::Comment,
251 FoldKind::Comment,
252 FoldKind::Comment,
253 ];
254 do_check(text, fold_kinds);
255 } 254 }
256 255
257 #[test] 256 #[test]
258 fn test_fold_imports() { 257 fn test_fold_imports() {
259 let text = r#" 258 check(
260<fold>use std::<fold>{ 259 r#"
260<fold imports>use std::<fold block>{
261 str, 261 str,
262 vec, 262 vec,
263 io as iop 263 io as iop
264}</fold>;</fold> 264}</fold>;</fold>
265 265
266fn main() <fold>{ 266fn main() <fold block>{
267}</fold>"#; 267}</fold>"#,
268 268 );
269 let folds = &[FoldKind::Imports, FoldKind::Block, FoldKind::Block];
270 do_check(text, folds);
271 } 269 }
272 270
273 #[test] 271 #[test]
274 fn test_fold_mods() { 272 fn test_fold_mods() {
275 let text = r#" 273 check(
274 r#"
276 275
277pub mod foo; 276pub mod foo;
278<fold>mod after_pub; 277<fold mods>mod after_pub;
279mod after_pub_next;</fold> 278mod after_pub_next;</fold>
280 279
281<fold>mod before_pub; 280<fold mods>mod before_pub;
282mod before_pub_next;</fold> 281mod before_pub_next;</fold>
283pub mod bar; 282pub mod bar;
284 283
@@ -286,90 +285,117 @@ mod not_folding_single;
286pub mod foobar; 285pub mod foobar;
287pub not_folding_single_next; 286pub not_folding_single_next;
288 287
289<fold>#[cfg(test)] 288<fold mods>#[cfg(test)]
290mod with_attribute; 289mod with_attribute;
291mod with_attribute_next;</fold> 290mod with_attribute_next;</fold>
292 291
293fn main() <fold>{ 292fn main() <fold block>{
294}</fold>"#; 293}</fold>"#,
295 294 );
296 let folds = &[FoldKind::Mods, FoldKind::Mods, FoldKind::Mods, FoldKind::Block];
297 do_check(text, folds);
298 } 295 }
299 296
300 #[test] 297 #[test]
301 fn test_fold_import_groups() { 298 fn test_fold_import_groups() {
302 let text = r#" 299 check(
303<fold>use std::str; 300 r#"
301<fold imports>use std::str;
304use std::vec; 302use std::vec;
305use std::io as iop;</fold> 303use std::io as iop;</fold>
306 304
307<fold>use std::mem; 305<fold imports>use std::mem;
308use std::f64;</fold> 306use std::f64;</fold>
309 307
310use std::collections::HashMap; 308use std::collections::HashMap;
311// Some random comment 309// Some random comment
312use std::collections::VecDeque; 310use std::collections::VecDeque;
313 311
314fn main() <fold>{ 312fn main() <fold block>{
315}</fold>"#; 313}</fold>"#,
316 314 );
317 let folds = &[FoldKind::Imports, FoldKind::Imports, FoldKind::Block];
318 do_check(text, folds);
319 } 315 }
320 316
321 #[test] 317 #[test]
322 fn test_fold_import_and_groups() { 318 fn test_fold_import_and_groups() {
323 let text = r#" 319 check(
324<fold>use std::str; 320 r#"
321<fold imports>use std::str;
325use std::vec; 322use std::vec;
326use std::io as iop;</fold> 323use std::io as iop;</fold>
327 324
328<fold>use std::mem; 325<fold imports>use std::mem;
329use std::f64;</fold> 326use std::f64;</fold>
330 327
331<fold>use std::collections::<fold>{ 328<fold imports>use std::collections::<fold block>{
332 HashMap, 329 HashMap,
333 VecDeque, 330 VecDeque,
334}</fold>;</fold> 331}</fold>;</fold>
335// Some random comment 332// Some random comment
336 333
337fn main() <fold>{ 334fn main() <fold block>{
338}</fold>"#; 335}</fold>"#,
339 336 );
340 let folds = &[
341 FoldKind::Imports,
342 FoldKind::Imports,
343 FoldKind::Imports,
344 FoldKind::Block,
345 FoldKind::Block,
346 ];
347 do_check(text, folds);
348 } 337 }
349 338
350 #[test] 339 #[test]
351 fn test_folds_macros() { 340 fn test_folds_macros() {
352 let text = r#" 341 check(
353macro_rules! foo <fold>{ 342 r#"
343macro_rules! foo <fold block>{
354 ($($tt:tt)*) => { $($tt)* } 344 ($($tt:tt)*) => { $($tt)* }
355}</fold> 345}</fold>
356"#; 346"#,
357 347 );
358 let folds = &[FoldKind::Block];
359 do_check(text, folds);
360 } 348 }
361 349
362 #[test] 350 #[test]
363 fn test_fold_match_arms() { 351 fn test_fold_match_arms() {
364 let text = r#" 352 check(
365fn main() <fold>{ 353 r#"
366 match 0 <fold>{ 354fn main() <fold block>{
355 match 0 <fold block>{
367 0 => 0, 356 0 => 0,
368 _ => 1, 357 _ => 1,
369 }</fold> 358 }</fold>
370}</fold>"#; 359}</fold>
360"#,
361 );
362 }
371 363
372 let folds = &[FoldKind::Block, FoldKind::Block]; 364 #[test]
373 do_check(text, folds); 365 fn fold_big_calls() {
366 check(
367 r#"
368fn main() <fold block>{
369 frobnicate<fold arglist>(
370 1,
371 2,
372 3,
373 )</fold>
374}</fold>
375"#,
376 )
377 }
378
379 #[test]
380 fn fold_record_literals() {
381 check(
382 r#"
383const _: S = S <fold block>{
384
385}</fold>;
386"#,
387 )
388 }
389
390 #[test]
391 fn fold_multiline_params() {
392 check(
393 r#"
394fn foo<fold arglist>(
395 x: i32,
396 y: String,
397)</fold> {}
398"#,
399 )
374 } 400 }
375} 401}
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index bea7fbfa7..4e3f428fa 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -7,7 +7,7 @@ use ra_syntax::{
7 ast::{self}, 7 ast::{self},
8 match_ast, AstNode, 8 match_ast, AstNode,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxToken, TokenAtOffset, 10 SyntaxToken, TokenAtOffset, T,
11}; 11};
12 12
13use crate::{ 13use crate::{
@@ -32,9 +32,10 @@ pub(crate) fn goto_definition(
32 let file = sema.parse(position.file_id).syntax().clone(); 32 let file = sema.parse(position.file_id).syntax().clone();
33 let original_token = pick_best(file.token_at_offset(position.offset))?; 33 let original_token = pick_best(file.token_at_offset(position.offset))?;
34 let token = sema.descend_into_macros(original_token.clone()); 34 let token = sema.descend_into_macros(original_token.clone());
35 let parent = token.parent();
35 36
36 let nav_targets = match_ast! { 37 let nav_targets = match_ast! {
37 match (token.parent()) { 38 match parent {
38 ast::NameRef(name_ref) => { 39 ast::NameRef(name_ref) => {
39 reference_definition(&sema, &name_ref).to_vec() 40 reference_definition(&sema, &name_ref).to_vec()
40 }, 41 },
@@ -57,7 +58,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
57 return tokens.max_by_key(priority); 58 return tokens.max_by_key(priority);
58 fn priority(n: &SyntaxToken) -> usize { 59 fn priority(n: &SyntaxToken) -> usize {
59 match n.kind() { 60 match n.kind() {
60 IDENT | INT_NUMBER => 2, 61 IDENT | INT_NUMBER | T![self] => 2,
61 kind if kind.is_trivia() => 0, 62 kind if kind.is_trivia() => 0,
62 _ => 1, 63 _ => 1,
63 } 64 }
@@ -103,205 +104,150 @@ pub(crate) fn reference_definition(
103 104
104#[cfg(test)] 105#[cfg(test)]
105mod tests { 106mod tests {
106 use test_utils::assert_eq_text; 107 use ra_db::FileRange;
107 108 use ra_syntax::{TextRange, TextSize};
108 use crate::mock_analysis::analysis_and_position; 109
109 110 use crate::mock_analysis::MockAnalysis;
110 fn check_goto(ra_fixture: &str, expected: &str, expected_range: &str) { 111
111 let (analysis, pos) = analysis_and_position(ra_fixture); 112 fn check(ra_fixture: &str) {
113 let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
114 let (mut expected, data) = mock.annotation();
115 let analysis = mock.analysis();
116 match data.as_str() {
117 "" => (),
118 "file" => {
119 expected.range =
120 TextRange::up_to(TextSize::of(&*analysis.file_text(expected.file_id).unwrap()))
121 }
122 data => panic!("bad data: {}", data),
123 }
112 124
113 let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info; 125 let mut navs =
126 analysis.goto_definition(position).unwrap().expect("no definition found").info;
114 if navs.len() == 0 { 127 if navs.len() == 0 {
115 panic!("unresolved reference") 128 panic!("unresolved reference")
116 } 129 }
117 assert_eq!(navs.len(), 1); 130 assert_eq!(navs.len(), 1);
118 131
119 let nav = navs.pop().unwrap(); 132 let nav = navs.pop().unwrap();
120 let file_text = analysis.file_text(nav.file_id()).unwrap(); 133 assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() });
121
122 let mut actual = file_text[nav.full_range()].to_string();
123 if let Some(focus) = nav.focus_range() {
124 actual += "|";
125 actual += &file_text[focus];
126 }
127
128 if !expected_range.contains("...") {
129 test_utils::assert_eq_text!(&actual, expected_range);
130 } else {
131 let mut parts = expected_range.split("...");
132 let prefix = parts.next().unwrap();
133 let suffix = parts.next().unwrap();
134 assert!(
135 actual.starts_with(prefix) && actual.ends_with(suffix),
136 "\nExpected: {}\n Actual: {}\n",
137 expected_range,
138 actual
139 );
140 }
141
142 nav.assert_match(expected);
143 } 134 }
144 135
145 #[test] 136 #[test]
146 fn goto_def_in_items() { 137 fn goto_def_in_items() {
147 check_goto( 138 check(
148 " 139 r#"
149 //- /lib.rs 140struct Foo;
150 struct Foo; 141 //^^^
151 enum E { X(Foo<|>) } 142enum E { X(Foo<|>) }
152 ", 143"#,
153 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
154 "struct Foo;|Foo",
155 ); 144 );
156 } 145 }
157 146
158 #[test] 147 #[test]
159 fn goto_def_at_start_of_item() { 148 fn goto_def_at_start_of_item() {
160 check_goto( 149 check(
161 " 150 r#"
162 //- /lib.rs 151struct Foo;
163 struct Foo; 152 //^^^
164 enum E { X(<|>Foo) } 153enum E { X(<|>Foo) }
165 ", 154"#,
166 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
167 "struct Foo;|Foo",
168 ); 155 );
169 } 156 }
170 157
171 #[test] 158 #[test]
172 fn goto_definition_resolves_correct_name() { 159 fn goto_definition_resolves_correct_name() {
173 check_goto( 160 check(
174 " 161 r#"
175 //- /lib.rs 162//- /lib.rs
176 use a::Foo; 163use a::Foo;
177 mod a; 164mod a;
178 mod b; 165mod b;
179 enum E { X(Foo<|>) } 166enum E { X(Foo<|>) }
180 167
181 //- /a.rs 168//- /a.rs
182 struct Foo; 169struct Foo;
183 170 //^^^
184 //- /b.rs 171//- /b.rs
185 struct Foo; 172struct Foo;
186 ", 173"#,
187 "Foo STRUCT_DEF FileId(2) 0..11 7..10",
188 "struct Foo;|Foo",
189 ); 174 );
190 } 175 }
191 176
192 #[test] 177 #[test]
193 fn goto_def_for_module_declaration() { 178 fn goto_def_for_module_declaration() {
194 check_goto( 179 check(
195 r#" 180 r#"
196//- /lib.rs 181//- /lib.rs
197mod <|>foo; 182mod <|>foo;
198 183
199//- /foo.rs 184//- /foo.rs
200// empty 185// empty
186//^ file
201"#, 187"#,
202 "foo SOURCE_FILE FileId(2) 0..9",
203 "// empty\n",
204 ); 188 );
205 189
206 check_goto( 190 check(
207 r#" 191 r#"
208//- /lib.rs 192//- /lib.rs
209mod <|>foo; 193mod <|>foo;
210 194
211//- /foo/mod.rs 195//- /foo/mod.rs
212// empty 196// empty
197//^ file
213"#, 198"#,
214 "foo SOURCE_FILE FileId(2) 0..9",
215 "// empty\n",
216 ); 199 );
217 } 200 }
218 201
219 #[test] 202 #[test]
220 fn goto_def_for_macros() { 203 fn goto_def_for_macros() {
221 check_goto( 204 check(
222 " 205 r#"
223 //- /lib.rs 206macro_rules! foo { () => { () } }
224 macro_rules! foo { () => { () } } 207 //^^^
225 208fn bar() {
226 fn bar() { 209 <|>foo!();
227 <|>foo!(); 210}
228 } 211"#,
229 ",
230 "foo MACRO_CALL FileId(1) 0..33 13..16",
231 "macro_rules! foo { () => { () } }|foo",
232 ); 212 );
233 } 213 }
234 214
235 #[test] 215 #[test]
236 fn goto_def_for_macros_from_other_crates() { 216 fn goto_def_for_macros_from_other_crates() {
237 check_goto( 217 check(
238 "
239 //- /lib.rs
240 use foo::foo;
241 fn bar() {
242 <|>foo!();
243 }
244
245 //- /foo/lib.rs
246 #[macro_export]
247 macro_rules! foo { () => { () } }
248 ",
249 "foo MACRO_CALL FileId(2) 0..49 29..32",
250 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
251 );
252 }
253
254 #[test]
255 fn goto_def_for_use_alias() {
256 check_goto(
257 r#" 218 r#"
258//- /lib.rs 219//- /lib.rs
259use foo as bar<|>; 220use foo::foo;
221fn bar() {
222 <|>foo!();
223}
260 224
261//- /foo/lib.rs 225//- /foo/lib.rs
262#[macro_export] 226#[macro_export]
263macro_rules! foo { () => { () } } 227macro_rules! foo { () => { () } }
228 //^^^
264"#, 229"#,
265 "SOURCE_FILE FileId(2) 0..50",
266 "#[macro_export]\nmacro_rules! foo { () => { () } }\n",
267 );
268 }
269
270 #[test]
271 fn goto_def_for_use_alias_foo_macro() {
272 check_goto(
273 "
274 //- /lib.rs
275 use foo::foo as bar<|>;
276
277 //- /foo/lib.rs
278 #[macro_export]
279 macro_rules! foo { () => { () } }
280 ",
281 "foo MACRO_CALL FileId(2) 0..49 29..32",
282 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
283 ); 230 );
284 } 231 }
285 232
286 #[test] 233 #[test]
287 fn goto_def_for_macros_in_use_tree() { 234 fn goto_def_for_macros_in_use_tree() {
288 check_goto( 235 check(
289 " 236 r#"
290 //- /lib.rs 237//- /lib.rs
291 use foo::foo<|>; 238use foo::foo<|>;
292 239
293 //- /foo/lib.rs 240//- /foo/lib.rs
294 #[macro_export] 241#[macro_export]
295 macro_rules! foo { () => { () } } 242macro_rules! foo { () => { () } }
296 ", 243 //^^^
297 "foo MACRO_CALL FileId(2) 0..49 29..32", 244"#,
298 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
299 ); 245 );
300 } 246 }
301 247
302 #[test] 248 #[test]
303 fn goto_def_for_macro_defined_fn_with_arg() { 249 fn goto_def_for_macro_defined_fn_with_arg() {
304 check_goto( 250 check(
305 r#" 251 r#"
306//- /lib.rs 252//- /lib.rs
307macro_rules! define_fn { 253macro_rules! define_fn {
@@ -309,522 +255,478 @@ macro_rules! define_fn {
309} 255}
310 256
311define_fn!(foo); 257define_fn!(foo);
258 //^^^
312 259
313fn bar() { 260fn bar() {
314 <|>foo(); 261 <|>foo();
315} 262}
316"#, 263"#,
317 "foo FN_DEF FileId(1) 65..81 76..79",
318 "define_fn!(foo);|foo",
319 ); 264 );
320 } 265 }
321 266
322 #[test] 267 #[test]
323 fn goto_def_for_macro_defined_fn_no_arg() { 268 fn goto_def_for_macro_defined_fn_no_arg() {
324 check_goto( 269 check(
325 r#" 270 r#"
326//- /lib.rs 271//- /lib.rs
327macro_rules! define_fn { 272macro_rules! define_fn {
328 () => (fn foo() {}) 273 () => (fn foo() {})
329} 274}
330 275
331define_fn!(); 276 define_fn!();
277//^^^^^^^^^^^^^
332 278
333fn bar() { 279fn bar() {
334 <|>foo(); 280 <|>foo();
335} 281}
336"#, 282"#,
337 "foo FN_DEF FileId(1) 52..65 52..65",
338 "define_fn!();|define_fn!();",
339 ); 283 );
340 } 284 }
341 285
342 #[test] 286 #[test]
343 fn goto_definition_works_for_macro_inside_pattern() { 287 fn goto_definition_works_for_macro_inside_pattern() {
344 check_goto( 288 check(
345 " 289 r#"
346 //- /lib.rs 290//- /lib.rs
347 macro_rules! foo {() => {0}} 291macro_rules! foo {() => {0}}
348 292 //^^^
349 fn bar() { 293
350 match (0,1) { 294fn bar() {
351 (<|>foo!(), _) => {} 295 match (0,1) {
352 } 296 (<|>foo!(), _) => {}
353 } 297 }
354 ", 298}
355 "foo MACRO_CALL FileId(1) 0..28 13..16", 299"#,
356 "macro_rules! foo {() => {0}}|foo",
357 ); 300 );
358 } 301 }
359 302
360 #[test] 303 #[test]
361 fn goto_definition_works_for_macro_inside_match_arm_lhs() { 304 fn goto_definition_works_for_macro_inside_match_arm_lhs() {
362 check_goto( 305 check(
363 " 306 r#"
364 //- /lib.rs 307//- /lib.rs
365 macro_rules! foo {() => {0}} 308macro_rules! foo {() => {0}}
366 309 //^^^
367 fn bar() { 310fn bar() {
368 match 0 { 311 match 0 {
369 <|>foo!() => {} 312 <|>foo!() => {}
370 } 313 }
371 } 314}
372 ", 315"#,
373 "foo MACRO_CALL FileId(1) 0..28 13..16", 316 );
374 "macro_rules! foo {() => {0}}|foo", 317 }
318
319 #[test]
320 fn goto_def_for_use_alias() {
321 check(
322 r#"
323//- /lib.rs
324use foo as bar<|>;
325
326//- /foo/lib.rs
327// empty
328//^ file
329"#,
330 );
331 }
332
333 #[test]
334 fn goto_def_for_use_alias_foo_macro() {
335 check(
336 r#"
337//- /lib.rs
338use foo::foo as bar<|>;
339
340//- /foo/lib.rs
341#[macro_export]
342macro_rules! foo { () => { () } }
343 //^^^
344"#,
375 ); 345 );
376 } 346 }
377 347
378 #[test] 348 #[test]
379 fn goto_def_for_methods() { 349 fn goto_def_for_methods() {
380 check_goto( 350 check(
381 " 351 r#"
382 //- /lib.rs 352//- /lib.rs
383 struct Foo; 353struct Foo;
384 impl Foo { 354impl Foo {
385 fn frobnicate(&self) { } 355 fn frobnicate(&self) { }
386 } 356 //^^^^^^^^^^
357}
387 358
388 fn bar(foo: &Foo) { 359fn bar(foo: &Foo) {
389 foo.frobnicate<|>(); 360 foo.frobnicate<|>();
390 } 361}
391 ", 362"#,
392 "frobnicate FN_DEF FileId(1) 27..51 30..40",
393 "fn frobnicate(&self) { }|frobnicate",
394 ); 363 );
395 } 364 }
396 365
397 #[test] 366 #[test]
398 fn goto_def_for_fields() { 367 fn goto_def_for_fields() {
399 check_goto( 368 check(
400 r" 369 r#"
401 //- /lib.rs 370struct Foo {
402 struct Foo { 371 spam: u32,
403 spam: u32, 372} //^^^^
404 }
405 373
406 fn bar(foo: &Foo) { 374fn bar(foo: &Foo) {
407 foo.spam<|>; 375 foo.spam<|>;
408 } 376}
409 ", 377"#,
410 "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21",
411 "spam: u32|spam",
412 ); 378 );
413 } 379 }
414 380
415 #[test] 381 #[test]
416 fn goto_def_for_record_fields() { 382 fn goto_def_for_record_fields() {
417 check_goto( 383 check(
418 r" 384 r#"
419 //- /lib.rs 385//- /lib.rs
420 struct Foo { 386struct Foo {
421 spam: u32, 387 spam: u32,
422 } 388} //^^^^
423 389
424 fn bar() -> Foo { 390fn bar() -> Foo {
425 Foo { 391 Foo {
426 spam<|>: 0, 392 spam<|>: 0,
427 } 393 }
428 } 394}
429 ", 395"#,
430 "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21",
431 "spam: u32|spam",
432 ); 396 );
433 } 397 }
434 398
435 #[test] 399 #[test]
436 fn goto_def_for_record_pat_fields() { 400 fn goto_def_for_record_pat_fields() {
437 check_goto( 401 check(
438 r" 402 r#"
439 //- /lib.rs 403//- /lib.rs
440 struct Foo { 404struct Foo {
441 spam: u32, 405 spam: u32,
442 } 406} //^^^^
443 407
444 fn bar(foo: Foo) -> Foo { 408fn bar(foo: Foo) -> Foo {
445 let Foo { spam<|>: _, } = foo 409 let Foo { spam<|>: _, } = foo
446 } 410}
447 ", 411"#,
448 "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21",
449 "spam: u32|spam",
450 ); 412 );
451 } 413 }
452 414
453 #[test] 415 #[test]
454 fn goto_def_for_record_fields_macros() { 416 fn goto_def_for_record_fields_macros() {
455 check_goto( 417 check(
456 r" 418 r"
457 //- /lib.rs 419macro_rules! m { () => { 92 };}
458 macro_rules! m { () => { 92 };} 420struct Foo { spam: u32 }
459 struct Foo { spam: u32 } 421 //^^^^
460 422
461 fn bar() -> Foo { 423fn bar() -> Foo {
462 Foo { spam<|>: m!() } 424 Foo { spam<|>: m!() }
463 } 425}
464 ", 426",
465 "spam RECORD_FIELD_DEF FileId(1) 45..54 45..49",
466 "spam: u32|spam",
467 ); 427 );
468 } 428 }
469 429
470 #[test] 430 #[test]
471 fn goto_for_tuple_fields() { 431 fn goto_for_tuple_fields() {
472 check_goto( 432 check(
473 " 433 r#"
474 //- /lib.rs 434struct Foo(u32);
475 struct Foo(u32); 435 //^^^
476 436
477 fn bar() { 437fn bar() {
478 let foo = Foo(0); 438 let foo = Foo(0);
479 foo.<|>0; 439 foo.<|>0;
480 } 440}
481 ", 441"#,
482 "TUPLE_FIELD_DEF FileId(1) 11..14",
483 "u32",
484 ); 442 );
485 } 443 }
486 444
487 #[test] 445 #[test]
488 fn goto_def_for_ufcs_inherent_methods() { 446 fn goto_def_for_ufcs_inherent_methods() {
489 check_goto( 447 check(
490 " 448 r#"
491 //- /lib.rs 449struct Foo;
492 struct Foo; 450impl Foo {
493 impl Foo { 451 fn frobnicate() { }
494 fn frobnicate() { } 452} //^^^^^^^^^^
495 }
496 453
497 fn bar(foo: &Foo) { 454fn bar(foo: &Foo) {
498 Foo::frobnicate<|>(); 455 Foo::frobnicate<|>();
499 } 456}
500 ", 457"#,
501 "frobnicate FN_DEF FileId(1) 27..46 30..40",
502 "fn frobnicate() { }|frobnicate",
503 ); 458 );
504 } 459 }
505 460
506 #[test] 461 #[test]
507 fn goto_def_for_ufcs_trait_methods_through_traits() { 462 fn goto_def_for_ufcs_trait_methods_through_traits() {
508 check_goto( 463 check(
509 " 464 r#"
510 //- /lib.rs 465trait Foo {
511 trait Foo { 466 fn frobnicate();
512 fn frobnicate(); 467} //^^^^^^^^^^
513 }
514 468
515 fn bar() { 469fn bar() {
516 Foo::frobnicate<|>(); 470 Foo::frobnicate<|>();
517 } 471}
518 ", 472"#,
519 "frobnicate FN_DEF FileId(1) 16..32 19..29",
520 "fn frobnicate();|frobnicate",
521 ); 473 );
522 } 474 }
523 475
524 #[test] 476 #[test]
525 fn goto_def_for_ufcs_trait_methods_through_self() { 477 fn goto_def_for_ufcs_trait_methods_through_self() {
526 check_goto( 478 check(
527 " 479 r#"
528 //- /lib.rs 480struct Foo;
529 struct Foo; 481trait Trait {
530 trait Trait { 482 fn frobnicate();
531 fn frobnicate(); 483} //^^^^^^^^^^
532 } 484impl Trait for Foo {}
533 impl Trait for Foo {}
534 485
535 fn bar() { 486fn bar() {
536 Foo::frobnicate<|>(); 487 Foo::frobnicate<|>();
537 } 488}
538 ", 489"#,
539 "frobnicate FN_DEF FileId(1) 30..46 33..43",
540 "fn frobnicate();|frobnicate",
541 ); 490 );
542 } 491 }
543 492
544 #[test] 493 #[test]
545 fn goto_definition_on_self() { 494 fn goto_definition_on_self() {
546 check_goto( 495 check(
547 " 496 r#"
548 //- /lib.rs 497struct Foo;
549 struct Foo; 498impl Foo {
550 impl Foo { 499 //^^^
551 pub fn new() -> Self { 500 pub fn new() -> Self {
552 Self<|> {} 501 Self<|> {}
553 } 502 }
554 } 503}
555 ", 504"#,
556 "impl IMPL_DEF FileId(1) 12..73", 505 );
557 "impl Foo {...}", 506 check(
558 ); 507 r#"
559 508struct Foo;
560 check_goto( 509impl Foo {
561 " 510 //^^^
562 //- /lib.rs 511 pub fn new() -> Self<|> {
563 struct Foo; 512 Self {}
564 impl Foo { 513 }
565 pub fn new() -> Self<|> { 514}
566 Self {} 515"#,
567 } 516 );
568 } 517
569 ", 518 check(
570 "impl IMPL_DEF FileId(1) 12..73", 519 r#"
571 "impl Foo {...}", 520enum Foo { A }
572 ); 521impl Foo {
573 522 //^^^
574 check_goto( 523 pub fn new() -> Self<|> {
575 " 524 Foo::A
576 //- /lib.rs 525 }
577 enum Foo { A } 526}
578 impl Foo { 527"#,
579 pub fn new() -> Self<|> { 528 );
580 Foo::A 529
581 } 530 check(
582 } 531 r#"
583 ", 532enum Foo { A }
584 "impl IMPL_DEF FileId(1) 15..75", 533impl Foo {
585 "impl Foo {...}", 534 //^^^
586 ); 535 pub fn thing(a: &Self<|>) {
587 536 }
588 check_goto( 537}
589 " 538"#,
590 //- /lib.rs
591 enum Foo { A }
592 impl Foo {
593 pub fn thing(a: &Self<|>) {
594 }
595 }
596 ",
597 "impl IMPL_DEF FileId(1) 15..62",
598 "impl Foo {...}",
599 ); 539 );
600 } 540 }
601 541
602 #[test] 542 #[test]
603 fn goto_definition_on_self_in_trait_impl() { 543 fn goto_definition_on_self_in_trait_impl() {
604 check_goto( 544 check(
605 " 545 r#"
606 //- /lib.rs 546struct Foo;
607 struct Foo; 547trait Make {
608 trait Make { 548 fn new() -> Self;
609 fn new() -> Self; 549}
610 } 550impl Make for Foo {
611 impl Make for Foo { 551 //^^^
612 fn new() -> Self { 552 fn new() -> Self {
613 Self<|> {} 553 Self<|> {}
614 } 554 }
615 } 555}
616 ", 556"#,
617 "impl IMPL_DEF FileId(1) 49..115",
618 "impl Make for Foo {...}",
619 ); 557 );
620 558
621 check_goto( 559 check(
622 " 560 r#"
623 //- /lib.rs 561struct Foo;
624 struct Foo; 562trait Make {
625 trait Make { 563 fn new() -> Self;
626 fn new() -> Self; 564}
627 } 565impl Make for Foo {
628 impl Make for Foo { 566 //^^^
629 fn new() -> Self<|> { 567 fn new() -> Self<|> {
630 Self {} 568 Self {}
631 } 569 }
632 } 570}
633 ", 571"#,
634 "impl IMPL_DEF FileId(1) 49..115",
635 "impl Make for Foo {...}",
636 ); 572 );
637 } 573 }
638 574
639 #[test] 575 #[test]
640 fn goto_def_when_used_on_definition_name_itself() { 576 fn goto_def_when_used_on_definition_name_itself() {
641 check_goto( 577 check(
642 " 578 r#"
643 //- /lib.rs 579struct Foo<|> { value: u32 }
644 struct Foo<|> { value: u32 } 580 //^^^
645 ", 581 "#,
646 "Foo STRUCT_DEF FileId(1) 0..25 7..10",
647 "struct Foo { value: u32 }|Foo",
648 ); 582 );
649 583
650 check_goto( 584 check(
651 r#" 585 r#"
652 //- /lib.rs 586struct Foo {
653 struct Foo { 587 field<|>: string,
654 field<|>: string, 588} //^^^^^
655 } 589"#,
656 "#,
657 "field RECORD_FIELD_DEF FileId(1) 17..30 17..22",
658 "field: string|field",
659 ); 590 );
660 591
661 check_goto( 592 check(
662 " 593 r#"
663 //- /lib.rs 594fn foo_test<|>() { }
664 fn foo_test<|>() { } 595 //^^^^^^^^
665 ", 596"#,
666 "foo_test FN_DEF FileId(1) 0..17 3..11",
667 "fn foo_test() { }|foo_test",
668 ); 597 );
669 598
670 check_goto( 599 check(
671 " 600 r#"
672 //- /lib.rs 601enum Foo<|> { Variant }
673 enum Foo<|> { 602 //^^^
674 Variant, 603"#,
675 }
676 ",
677 "Foo ENUM_DEF FileId(1) 0..25 5..8",
678 "enum Foo {...}|Foo",
679 );
680
681 check_goto(
682 "
683 //- /lib.rs
684 enum Foo {
685 Variant1,
686 Variant2<|>,
687 Variant3,
688 }
689 ",
690 "Variant2 ENUM_VARIANT FileId(1) 29..37 29..37",
691 "Variant2|Variant2",
692 ); 604 );
693 605
694 check_goto( 606 check(
695 r#" 607 r#"
696 //- /lib.rs 608enum Foo {
697 static INNER<|>: &str = ""; 609 Variant1,
698 "#, 610 Variant2<|>,
699 "INNER STATIC_DEF FileId(1) 0..24 7..12", 611 //^^^^^^^^
700 "static INNER: &str = \"\";|INNER", 612 Variant3,
613}
614"#,
701 ); 615 );
702 616
703 check_goto( 617 check(
704 r#" 618 r#"
705 //- /lib.rs 619static INNER<|>: &str = "";
706 const INNER<|>: &str = ""; 620 //^^^^^
707 "#, 621"#,
708 "INNER CONST_DEF FileId(1) 0..23 6..11",
709 "const INNER: &str = \"\";|INNER",
710 ); 622 );
711 623
712 check_goto( 624 check(
713 r#" 625 r#"
714 //- /lib.rs 626const INNER<|>: &str = "";
715 type Thing<|> = Option<()>; 627 //^^^^^
716 "#, 628"#,
717 "Thing TYPE_ALIAS_DEF FileId(1) 0..24 5..10",
718 "type Thing = Option<()>;|Thing",
719 ); 629 );
720 630
721 check_goto( 631 check(
722 r#" 632 r#"
723 //- /lib.rs 633type Thing<|> = Option<()>;
724 trait Foo<|> { } 634 //^^^^^
725 "#, 635"#,
726 "Foo TRAIT_DEF FileId(1) 0..13 6..9",
727 "trait Foo { }|Foo",
728 ); 636 );
729 637
730 check_goto( 638 check(
731 r#" 639 r#"
732 //- /lib.rs 640trait Foo<|> { }
733 mod bar<|> { } 641 //^^^
734 "#, 642"#,
735 "bar MODULE FileId(1) 0..11 4..7", 643 );
736 "mod bar { }|bar", 644
645 check(
646 r#"
647mod bar<|> { }
648 //^^^
649"#,
737 ); 650 );
738 } 651 }
739 652
740 #[test] 653 #[test]
741 fn goto_from_macro() { 654 fn goto_from_macro() {
742 check_goto( 655 check(
743 " 656 r#"
744 //- /lib.rs 657macro_rules! id {
745 macro_rules! id { 658 ($($tt:tt)*) => { $($tt)* }
746 ($($tt:tt)*) => { $($tt)* } 659}
747 } 660fn foo() {}
748 fn foo() {} 661 //^^^
749 id! { 662id! {
750 fn bar() { 663 fn bar() {
751 fo<|>o(); 664 fo<|>o();
752 } 665 }
753 } 666}
754 mod confuse_index { fn foo(); } 667mod confuse_index { fn foo(); }
755 ", 668"#,
756 "foo FN_DEF FileId(1) 52..63 55..58",
757 "fn foo() {}|foo",
758 ); 669 );
759 } 670 }
760 671
761 #[test] 672 #[test]
762 fn goto_through_format() { 673 fn goto_through_format() {
763 check_goto( 674 check(
764 " 675 r#"
765 //- /lib.rs 676#[macro_export]
766 #[macro_export] 677macro_rules! format {
767 macro_rules! format { 678 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*)))
768 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*))) 679}
769 } 680#[rustc_builtin_macro]
770 #[rustc_builtin_macro] 681#[macro_export]
771 #[macro_export] 682macro_rules! format_args {
772 macro_rules! format_args { 683 ($fmt:expr) => ({ /* compiler built-in */ });
773 ($fmt:expr) => ({ /* compiler built-in */ }); 684 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
774 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) 685}
775 } 686pub mod __export {
776 pub mod __export { 687 pub use crate::format_args;
777 pub use crate::format_args; 688 fn foo() {} // for index confusion
778 fn foo() {} // for index confusion 689}
779 } 690fn foo() -> i8 {}
780 fn foo() -> i8 {} 691 //^^^
781 fn test() { 692fn test() {
782 format!(\"{}\", fo<|>o()) 693 format!("{}", fo<|>o())
783 } 694}
784 ", 695"#,
785 "foo FN_DEF FileId(1) 398..415 401..404",
786 "fn foo() -> i8 {}|foo",
787 ); 696 );
788 } 697 }
789 698
790 #[test] 699 #[test]
791 fn goto_for_type_param() { 700 fn goto_for_type_param() {
792 check_goto( 701 check(
793 r#" 702 r#"
794 //- /lib.rs 703struct Foo<T: Clone> { t: <|>T }
795 struct Foo<T: Clone> { 704 //^
796 t: <|>T, 705"#,
797 }
798 "#,
799 "T TYPE_PARAM FileId(1) 11..19 11..12",
800 "T: Clone|T",
801 ); 706 );
802 } 707 }
803 708
804 #[test] 709 #[test]
805 fn goto_within_macro() { 710 fn goto_within_macro() {
806 check_goto( 711 check(
807 r#" 712 r#"
808//- /lib.rs
809macro_rules! id { 713macro_rules! id {
810 ($($tt:tt)*) => ($($tt)*) 714 ($($tt:tt)*) => ($($tt)*)
811} 715}
812 716
813fn foo() { 717fn foo() {
814 let x = 1; 718 let x = 1;
719 //^
815 id!({ 720 id!({
816 let y = <|>x; 721 let y = <|>x;
817 let z = y; 722 let z = y;
818 }); 723 });
819} 724}
820"#, 725"#,
821 "x BIND_PAT FileId(1) 70..71",
822 "x",
823 ); 726 );
824 727
825 check_goto( 728 check(
826 r#" 729 r#"
827//- /lib.rs
828macro_rules! id { 730macro_rules! id {
829 ($($tt:tt)*) => ($($tt)*) 731 ($($tt:tt)*) => ($($tt)*)
830} 732}
@@ -833,159 +735,233 @@ fn foo() {
833 let x = 1; 735 let x = 1;
834 id!({ 736 id!({
835 let y = x; 737 let y = x;
738 //^
836 let z = <|>y; 739 let z = <|>y;
837 }); 740 });
838} 741}
839"#, 742"#,
840 "y BIND_PAT FileId(1) 99..100",
841 "y",
842 ); 743 );
843 } 744 }
844 745
845 #[test] 746 #[test]
846 fn goto_def_in_local_fn() { 747 fn goto_def_in_local_fn() {
847 check_goto( 748 check(
848 " 749 r#"
849 //- /lib.rs 750fn main() {
850 fn main() { 751 fn foo() {
851 fn foo() { 752 let x = 92;
852 let x = 92; 753 //^
853 <|>x; 754 <|>x;
854 } 755 }
855 } 756}
856 ", 757"#,
857 "x BIND_PAT FileId(1) 39..40",
858 "x",
859 ); 758 );
860 } 759 }
861 760
862 #[test] 761 #[test]
863 fn goto_def_in_local_macro() { 762 fn goto_def_in_local_macro() {
864 check_goto( 763 check(
865 r" 764 r#"
866 //- /lib.rs 765fn bar() {
867 fn bar() { 766 macro_rules! foo { () => { () } }
868 macro_rules! foo { () => { () } } 767 //^^^
869 <|>foo!(); 768 <|>foo!();
870 } 769}
871 ", 770"#,
872 "foo MACRO_CALL FileId(1) 15..48 28..31",
873 "macro_rules! foo { () => { () } }|foo",
874 ); 771 );
875 } 772 }
876 773
877 #[test] 774 #[test]
878 fn goto_def_for_field_init_shorthand() { 775 fn goto_def_for_field_init_shorthand() {
879 check_goto( 776 check(
880 " 777 r#"
881 //- /lib.rs 778struct Foo { x: i32 }
882 struct Foo { x: i32 } 779fn main() {
883 fn main() { 780 let x = 92;
884 let x = 92; 781 //^
885 Foo { x<|> }; 782 Foo { x<|> };
886 } 783}
887 ", 784"#,
888 "x BIND_PAT FileId(1) 42..43",
889 "x",
890 ) 785 )
891 } 786 }
892 787
893 #[test] 788 #[test]
894 fn goto_def_for_enum_variant_field() { 789 fn goto_def_for_enum_variant_field() {
895 check_goto( 790 check(
896 " 791 r#"
897 //- /lib.rs 792enum Foo {
898 enum Foo { 793 Bar { x: i32 }
899 Bar { x: i32 } 794} //^
900 } 795fn baz(foo: Foo) {
901 fn baz(foo: Foo) { 796 match foo {
902 match foo { 797 Foo::Bar { x<|> } => x
903 Foo::Bar { x<|> } => x 798 };
904 }; 799}
905 } 800"#,
906 ",
907 "x RECORD_FIELD_DEF FileId(1) 21..27 21..22",
908 "x: i32|x",
909 ); 801 );
910 } 802 }
911 803
912 #[test] 804 #[test]
913 fn goto_def_for_enum_variant_self_pattern_const() { 805 fn goto_def_for_enum_variant_self_pattern_const() {
914 check_goto( 806 check(
915 " 807 r#"
916 //- /lib.rs 808enum Foo { Bar }
917 enum Foo { 809 //^^^
918 Bar, 810impl Foo {
919 } 811 fn baz(self) {
920 impl Foo { 812 match self { Self::Bar<|> => {} }
921 fn baz(self) { 813 }
922 match self { 814}
923 Self::Bar<|> => {} 815"#,
924 }
925 }
926 }
927 ",
928 "Bar ENUM_VARIANT FileId(1) 15..18 15..18",
929 "Bar|Bar",
930 ); 816 );
931 } 817 }
932 818
933 #[test] 819 #[test]
934 fn goto_def_for_enum_variant_self_pattern_record() { 820 fn goto_def_for_enum_variant_self_pattern_record() {
935 check_goto( 821 check(
936 " 822 r#"
937 //- /lib.rs 823enum Foo { Bar { val: i32 } }
938 enum Foo { 824 //^^^
939 Bar { val: i32 }, 825impl Foo {
940 } 826 fn baz(self) -> i32 {
941 impl Foo { 827 match self { Self::Bar<|> { val } => {} }
942 fn baz(self) -> i32 { 828 }
943 match self { 829}
944 Self::Bar<|> { val } => {} 830"#,
945 }
946 }
947 }
948 ",
949 "Bar ENUM_VARIANT FileId(1) 15..31 15..18",
950 "Bar { val: i32 }|Bar",
951 ); 831 );
952 } 832 }
953 833
954 #[test] 834 #[test]
955 fn goto_def_for_enum_variant_self_expr_const() { 835 fn goto_def_for_enum_variant_self_expr_const() {
956 check_goto( 836 check(
957 " 837 r#"
958 //- /lib.rs 838enum Foo { Bar }
959 enum Foo { 839 //^^^
960 Bar, 840impl Foo {
961 } 841 fn baz(self) { Self::Bar<|>; }
962 impl Foo { 842}
963 fn baz(self) { 843"#,
964 Self::Bar<|>;
965 }
966 }
967 ",
968 "Bar ENUM_VARIANT FileId(1) 15..18 15..18",
969 "Bar|Bar",
970 ); 844 );
971 } 845 }
972 846
973 #[test] 847 #[test]
974 fn goto_def_for_enum_variant_self_expr_record() { 848 fn goto_def_for_enum_variant_self_expr_record() {
975 check_goto( 849 check(
976 " 850 r#"
977 //- /lib.rs 851enum Foo { Bar { val: i32 } }
978 enum Foo { 852 //^^^
979 Bar { val: i32 }, 853impl Foo {
980 } 854 fn baz(self) { Self::Bar<|> {val: 4}; }
981 impl Foo { 855}
982 fn baz(self) { 856"#,
983 Self::Bar<|> {val: 4}; 857 );
984 } 858 }
985 } 859
986 ", 860 #[test]
987 "Bar ENUM_VARIANT FileId(1) 15..31 15..18", 861 fn goto_def_for_type_alias_generic_parameter() {
988 "Bar { val: i32 }|Bar", 862 check(
863 r#"
864type Alias<T> = T<|>;
865 //^
866"#,
867 )
868 }
869
870 #[test]
871 fn goto_def_for_macro_container() {
872 check(
873 r#"
874//- /lib.rs
875foo::module<|>::mac!();
876
877//- /foo/lib.rs
878pub mod module {
879 //^^^^^^
880 #[macro_export]
881 macro_rules! _mac { () => { () } }
882 pub use crate::_mac as mac;
883}
884"#,
885 );
886 }
887
888 #[test]
889 fn goto_def_for_assoc_ty_in_path() {
890 check(
891 r#"
892trait Iterator {
893 type Item;
894 //^^^^
895}
896
897fn f() -> impl Iterator<Item<|> = u8> {}
898"#,
899 );
900 }
901
902 #[test]
903 fn goto_def_for_assoc_ty_in_path_multiple() {
904 check(
905 r#"
906trait Iterator {
907 type A;
908 //^
909 type B;
910}
911
912fn f() -> impl Iterator<A<|> = u8, B = ()> {}
913"#,
914 );
915 check(
916 r#"
917trait Iterator {
918 type A;
919 type B;
920 //^
921}
922
923fn f() -> impl Iterator<A = u8, B<|> = ()> {}
924"#,
925 );
926 }
927
928 #[test]
929 fn goto_def_for_assoc_ty_ufcs() {
930 check(
931 r#"
932trait Iterator {
933 type Item;
934 //^^^^
935}
936
937fn g() -> <() as Iterator<Item<|> = ()>>::Item {}
938"#,
939 );
940 }
941
942 #[test]
943 fn goto_def_for_assoc_ty_ufcs_multiple() {
944 check(
945 r#"
946trait Iterator {
947 type A;
948 //^
949 type B;
950}
951
952fn g() -> <() as Iterator<A<|> = (), B = u8>>::B {}
953"#,
954 );
955 check(
956 r#"
957trait Iterator {
958 type A;
959 type B;
960 //^
961}
962
963fn g() -> <() as Iterator<A = (), B<|> = u8>>::A {}
964"#,
989 ); 965 );
990 } 966 }
991} 967}
diff --git a/crates/ra_ide/src/goto_implementation.rs b/crates/ra_ide/src/goto_implementation.rs
index 0cec0657e..9912b7142 100644
--- a/crates/ra_ide/src/goto_implementation.rs
+++ b/crates/ra_ide/src/goto_implementation.rs
@@ -23,12 +23,12 @@ pub(crate) fn goto_implementation(
23 23
24 let krate = sema.to_module_def(position.file_id)?.krate(); 24 let krate = sema.to_module_def(position.file_id)?.krate();
25 25
26 if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(&syntax, position.offset) { 26 if let Some(nominal_def) = find_node_at_offset::<ast::AdtDef>(&syntax, position.offset) {
27 return Some(RangeInfo::new( 27 return Some(RangeInfo::new(
28 nominal_def.syntax().text_range(), 28 nominal_def.syntax().text_range(),
29 impls_for_def(&sema, &nominal_def, krate)?, 29 impls_for_def(&sema, &nominal_def, krate)?,
30 )); 30 ));
31 } else if let Some(trait_def) = find_node_at_offset::<ast::TraitDef>(&syntax, position.offset) { 31 } else if let Some(trait_def) = find_node_at_offset::<ast::Trait>(&syntax, position.offset) {
32 return Some(RangeInfo::new( 32 return Some(RangeInfo::new(
33 trait_def.syntax().text_range(), 33 trait_def.syntax().text_range(),
34 impls_for_trait(&sema, &trait_def, krate)?, 34 impls_for_trait(&sema, &trait_def, krate)?,
@@ -40,13 +40,13 @@ pub(crate) fn goto_implementation(
40 40
41fn impls_for_def( 41fn impls_for_def(
42 sema: &Semantics<RootDatabase>, 42 sema: &Semantics<RootDatabase>,
43 node: &ast::NominalDef, 43 node: &ast::AdtDef,
44 krate: Crate, 44 krate: Crate,
45) -> Option<Vec<NavigationTarget>> { 45) -> Option<Vec<NavigationTarget>> {
46 let ty = match node { 46 let ty = match node {
47 ast::NominalDef::StructDef(def) => sema.to_def(def)?.ty(sema.db), 47 ast::AdtDef::Struct(def) => sema.to_def(def)?.ty(sema.db),
48 ast::NominalDef::EnumDef(def) => sema.to_def(def)?.ty(sema.db), 48 ast::AdtDef::Enum(def) => sema.to_def(def)?.ty(sema.db),
49 ast::NominalDef::UnionDef(def) => sema.to_def(def)?.ty(sema.db), 49 ast::AdtDef::Union(def) => sema.to_def(def)?.ty(sema.db),
50 }; 50 };
51 51
52 let impls = ImplDef::all_in_crate(sema.db, krate); 52 let impls = ImplDef::all_in_crate(sema.db, krate);
@@ -62,7 +62,7 @@ fn impls_for_def(
62 62
63fn impls_for_trait( 63fn impls_for_trait(
64 sema: &Semantics<RootDatabase>, 64 sema: &Semantics<RootDatabase>,
65 node: &ast::TraitDef, 65 node: &ast::Trait,
66 krate: Crate, 66 krate: Crate,
67) -> Option<Vec<NavigationTarget>> { 67) -> Option<Vec<NavigationTarget>> {
68 let tr = sema.to_def(node)?; 68 let tr = sema.to_def(node)?;
@@ -74,135 +74,156 @@ fn impls_for_trait(
74 74
75#[cfg(test)] 75#[cfg(test)]
76mod tests { 76mod tests {
77 use crate::mock_analysis::analysis_and_position; 77 use ra_db::FileRange;
78 78
79 fn check_goto(fixture: &str, expected: &[&str]) { 79 use crate::mock_analysis::MockAnalysis;
80 let (analysis, pos) = analysis_and_position(fixture);
81 80
82 let mut navs = analysis.goto_implementation(pos).unwrap().unwrap().info; 81 fn check(ra_fixture: &str) {
83 assert_eq!(navs.len(), expected.len()); 82 let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
84 navs.sort_by_key(|nav| (nav.file_id(), nav.full_range().start())); 83 let annotations = mock.annotations();
85 navs.into_iter().enumerate().for_each(|(i, nav)| nav.assert_match(expected[i])); 84 let analysis = mock.analysis();
85
86 let navs = analysis.goto_implementation(position).unwrap().unwrap().info;
87
88 let key = |frange: &FileRange| (frange.file_id, frange.range.start());
89
90 let mut expected = annotations
91 .into_iter()
92 .map(|(range, data)| {
93 assert!(data.is_empty());
94 range
95 })
96 .collect::<Vec<_>>();
97 expected.sort_by_key(key);
98
99 let mut actual = navs
100 .into_iter()
101 .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
102 .collect::<Vec<_>>();
103 actual.sort_by_key(key);
104
105 assert_eq!(expected, actual);
86 } 106 }
87 107
88 #[test] 108 #[test]
89 fn goto_implementation_works() { 109 fn goto_implementation_works() {
90 check_goto( 110 check(
91 " 111 r#"
92 //- /lib.rs 112struct Foo<|>;
93 struct Foo<|>; 113impl Foo {}
94 impl Foo {} 114 //^^^
95 ", 115"#,
96 &["impl IMPL_DEF FileId(1) 12..23"],
97 ); 116 );
98 } 117 }
99 118
100 #[test] 119 #[test]
101 fn goto_implementation_works_multiple_blocks() { 120 fn goto_implementation_works_multiple_blocks() {
102 check_goto( 121 check(
103 " 122 r#"
104 //- /lib.rs 123struct Foo<|>;
105 struct Foo<|>; 124impl Foo {}
106 impl Foo {} 125 //^^^
107 impl Foo {} 126impl Foo {}
108 ", 127 //^^^
109 &["impl IMPL_DEF FileId(1) 12..23", "impl IMPL_DEF FileId(1) 24..35"], 128"#,
110 ); 129 );
111 } 130 }
112 131
113 #[test] 132 #[test]
114 fn goto_implementation_works_multiple_mods() { 133 fn goto_implementation_works_multiple_mods() {
115 check_goto( 134 check(
116 " 135 r#"
117 //- /lib.rs 136struct Foo<|>;
118 struct Foo<|>; 137mod a {
119 mod a { 138 impl super::Foo {}
120 impl super::Foo {} 139 //^^^^^^^^^^
121 } 140}
122 mod b { 141mod b {
123 impl super::Foo {} 142 impl super::Foo {}
124 } 143 //^^^^^^^^^^
125 ", 144}
126 &["impl IMPL_DEF FileId(1) 24..42", "impl IMPL_DEF FileId(1) 57..75"], 145"#,
127 ); 146 );
128 } 147 }
129 148
130 #[test] 149 #[test]
131 fn goto_implementation_works_multiple_files() { 150 fn goto_implementation_works_multiple_files() {
132 check_goto( 151 check(
133 " 152 r#"
134 //- /lib.rs 153//- /lib.rs
135 struct Foo<|>; 154struct Foo<|>;
136 mod a; 155mod a;
137 mod b; 156mod b;
138 //- /a.rs 157//- /a.rs
139 impl crate::Foo {} 158impl crate::Foo {}
140 //- /b.rs 159 //^^^^^^^^^^
141 impl crate::Foo {} 160//- /b.rs
142 ", 161impl crate::Foo {}
143 &["impl IMPL_DEF FileId(2) 0..18", "impl IMPL_DEF FileId(3) 0..18"], 162 //^^^^^^^^^^
163"#,
144 ); 164 );
145 } 165 }
146 166
147 #[test] 167 #[test]
148 fn goto_implementation_for_trait() { 168 fn goto_implementation_for_trait() {
149 check_goto( 169 check(
150 " 170 r#"
151 //- /lib.rs 171trait T<|> {}
152 trait T<|> {} 172struct Foo;
153 struct Foo; 173impl T for Foo {}
154 impl T for Foo {} 174 //^^^
155 ", 175"#,
156 &["impl IMPL_DEF FileId(1) 23..40"],
157 ); 176 );
158 } 177 }
159 178
160 #[test] 179 #[test]
161 fn goto_implementation_for_trait_multiple_files() { 180 fn goto_implementation_for_trait_multiple_files() {
162 check_goto( 181 check(
163 " 182 r#"
164 //- /lib.rs 183//- /lib.rs
165 trait T<|> {}; 184trait T<|> {};
166 struct Foo; 185struct Foo;
167 mod a; 186mod a;
168 mod b; 187mod b;
169 //- /a.rs 188//- /a.rs
170 impl crate::T for crate::Foo {} 189impl crate::T for crate::Foo {}
171 //- /b.rs 190 //^^^^^^^^^^
172 impl crate::T for crate::Foo {} 191//- /b.rs
173 ", 192impl crate::T for crate::Foo {}
174 &["impl IMPL_DEF FileId(2) 0..31", "impl IMPL_DEF FileId(3) 0..31"], 193 //^^^^^^^^^^
194 "#,
175 ); 195 );
176 } 196 }
177 197
178 #[test] 198 #[test]
179 fn goto_implementation_all_impls() { 199 fn goto_implementation_all_impls() {
180 check_goto( 200 check(
181 " 201 r#"
182 //- /lib.rs 202//- /lib.rs
183 trait T {} 203trait T {}
184 struct Foo<|>; 204struct Foo<|>;
185 impl Foo {} 205impl Foo {}
186 impl T for Foo {} 206 //^^^
187 impl T for &Foo {} 207impl T for Foo {}
188 ", 208 //^^^
189 &[ 209impl T for &Foo {}
190 "impl IMPL_DEF FileId(1) 23..34", 210 //^^^^
191 "impl IMPL_DEF FileId(1) 35..52", 211"#,
192 "impl IMPL_DEF FileId(1) 53..71",
193 ],
194 ); 212 );
195 } 213 }
196 214
197 #[test] 215 #[test]
198 fn goto_implementation_to_builtin_derive() { 216 fn goto_implementation_to_builtin_derive() {
199 check_goto( 217 check(
200 " 218 r#"
201 //- /lib.rs 219 #[derive(Copy)]
202 #[derive(Copy)] 220//^^^^^^^^^^^^^^^
203 struct Foo<|>; 221struct Foo<|>;
204 ", 222
205 &["impl IMPL_DEF FileId(1) 0..15"], 223mod marker {
224 trait Copy {}
225}
226"#,
206 ); 227 );
207 } 228 }
208} 229}
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs
index 91a3097fb..8f52feea6 100644
--- a/crates/ra_ide/src/goto_type_definition.rs
+++ b/crates/ra_ide/src/goto_type_definition.rs
@@ -1,5 +1,5 @@
1use ra_ide_db::RootDatabase; 1use ra_ide_db::RootDatabase;
2use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; 2use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
3 3
4use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; 4use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
5 5
@@ -25,8 +25,9 @@ pub(crate) fn goto_type_definition(
25 let (ty, node) = sema.ancestors_with_macros(token.parent()).find_map(|node| { 25 let (ty, node) = sema.ancestors_with_macros(token.parent()).find_map(|node| {
26 let ty = match_ast! { 26 let ty = match_ast! {
27 match node { 27 match node {
28 ast::Expr(expr) => sema.type_of_expr(&expr)?, 28 ast::Expr(it) => sema.type_of_expr(&it)?,
29 ast::Pat(pat) => sema.type_of_pat(&pat)?, 29 ast::Pat(it) => sema.type_of_pat(&it)?,
30 ast::SelfParam(it) => sema.type_of_self(&it)?,
30 _ => return None, 31 _ => return None,
31 } 32 }
32 }; 33 };
@@ -34,7 +35,7 @@ pub(crate) fn goto_type_definition(
34 Some((ty, node)) 35 Some((ty, node))
35 })?; 36 })?;
36 37
37 let adt_def = ty.autoderef(db).find_map(|ty| ty.as_adt())?; 38 let adt_def = ty.autoderef(db).filter_map(|ty| ty.as_adt()).last()?;
38 39
39 let nav = adt_def.to_nav(db); 40 let nav = adt_def.to_nav(db);
40 Some(RangeInfo::new(node.text_range(), vec![nav])) 41 Some(RangeInfo::new(node.text_range(), vec![nav]))
@@ -44,7 +45,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
44 return tokens.max_by_key(priority); 45 return tokens.max_by_key(priority);
45 fn priority(n: &SyntaxToken) -> usize { 46 fn priority(n: &SyntaxToken) -> usize {
46 match n.kind() { 47 match n.kind() {
47 IDENT | INT_NUMBER => 2, 48 IDENT | INT_NUMBER | T![self] => 2,
48 kind if kind.is_trivia() => 0, 49 kind if kind.is_trivia() => 0,
49 _ => 1, 50 _ => 1,
50 } 51 }
@@ -53,91 +54,98 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
53 54
54#[cfg(test)] 55#[cfg(test)]
55mod tests { 56mod tests {
56 use crate::mock_analysis::analysis_and_position; 57 use ra_db::FileRange;
57 58
58 fn check_goto(fixture: &str, expected: &str) { 59 use crate::mock_analysis::MockAnalysis;
59 let (analysis, pos) = analysis_and_position(fixture);
60 60
61 let mut navs = analysis.goto_type_definition(pos).unwrap().unwrap().info; 61 fn check(ra_fixture: &str) {
62 let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
63 let (expected, data) = mock.annotation();
64 assert!(data.is_empty());
65 let analysis = mock.analysis();
66
67 let mut navs = analysis.goto_type_definition(position).unwrap().unwrap().info;
62 assert_eq!(navs.len(), 1); 68 assert_eq!(navs.len(), 1);
63 let nav = navs.pop().unwrap(); 69 let nav = navs.pop().unwrap();
64 nav.assert_match(expected); 70 assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() });
65 } 71 }
66 72
67 #[test] 73 #[test]
68 fn goto_type_definition_works_simple() { 74 fn goto_type_definition_works_simple() {
69 check_goto( 75 check(
70 " 76 r#"
71 //- /lib.rs 77struct Foo;
72 struct Foo; 78 //^^^
73 fn foo() { 79fn foo() {
74 let f: Foo; 80 let f: Foo; f<|>
75 f<|> 81}
76 } 82"#,
77 ",
78 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
79 ); 83 );
80 } 84 }
81 85
82 #[test] 86 #[test]
83 fn goto_type_definition_works_simple_ref() { 87 fn goto_type_definition_works_simple_ref() {
84 check_goto( 88 check(
85 " 89 r#"
86 //- /lib.rs 90struct Foo;
87 struct Foo; 91 //^^^
88 fn foo() { 92fn foo() {
89 let f: &Foo; 93 let f: &Foo; f<|>
90 f<|> 94}
91 } 95"#,
92 ",
93 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
94 ); 96 );
95 } 97 }
96 98
97 #[test] 99 #[test]
98 fn goto_type_definition_works_through_macro() { 100 fn goto_type_definition_works_through_macro() {
99 check_goto( 101 check(
100 " 102 r#"
101 //- /lib.rs 103macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
102 macro_rules! id { 104struct Foo {}
103 ($($tt:tt)*) => { $($tt)* } 105 //^^^
104 } 106id! {
105 struct Foo {} 107 fn bar() { let f<|> = Foo {}; }
106 id! { 108}
107 fn bar() { 109"#,
108 let f<|> = Foo {};
109 }
110 }
111 ",
112 "Foo STRUCT_DEF FileId(1) 52..65 59..62",
113 ); 110 );
114 } 111 }
115 112
116 #[test] 113 #[test]
117 fn goto_type_definition_for_param() { 114 fn goto_type_definition_for_param() {
118 check_goto( 115 check(
119 " 116 r#"
120 //- /lib.rs 117struct Foo;
121 struct Foo; 118 //^^^
122 fn foo(<|>f: Foo) {} 119fn foo(<|>f: Foo) {}
123 ", 120"#,
124 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
125 ); 121 );
126 } 122 }
127 123
128 #[test] 124 #[test]
129 fn goto_type_definition_for_tuple_field() { 125 fn goto_type_definition_for_tuple_field() {
130 check_goto( 126 check(
131 " 127 r#"
132 //- /lib.rs 128struct Foo;
133 struct Foo; 129 //^^^
134 struct Bar(Foo); 130struct Bar(Foo);
135 fn foo() { 131fn foo() {
136 let bar = Bar(Foo); 132 let bar = Bar(Foo);
137 bar.<|>0; 133 bar.<|>0;
138 } 134}
139 ", 135"#,
140 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
141 ); 136 );
142 } 137 }
138
139 #[test]
140 fn goto_def_for_self_param() {
141 check(
142 r#"
143struct Foo;
144 //^^^
145impl Foo {
146 fn f(&self<|>) {}
147}
148"#,
149 )
150 }
143} 151}
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index f36b9de7e..ad68bc43c 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -16,18 +16,18 @@ use ra_ide_db::{
16 defs::{classify_name, classify_name_ref, Definition}, 16 defs::{classify_name, classify_name_ref, Definition},
17 RootDatabase, 17 RootDatabase,
18}; 18};
19use ra_syntax::{ast, ast::Path, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; 19use ra_syntax::{ast, ast::Path, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
20use ra_tt::{Ident, Leaf, Literal, TokenTree}; 20use ra_tt::{Ident, Leaf, Literal, TokenTree};
21use stdx::format_to;
22use test_utils::mark;
21use url::Url; 23use url::Url;
22 24
23use crate::{ 25use crate::{
24 display::{ 26 display::{macro_label, ShortLabel, ToNav, TryToNav},
25 macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav, TryToNav, 27 markup::Markup,
26 },
27 runnables::runnable, 28 runnables::runnable,
28 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, 29 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
29}; 30};
30use test_utils::mark;
31 31
32#[derive(Clone, Debug, PartialEq, Eq)] 32#[derive(Clone, Debug, PartialEq, Eq)]
33pub struct HoverConfig { 33pub struct HoverConfig {
@@ -76,50 +76,8 @@ pub struct HoverGotoTypeData {
76/// Contains the results when hovering over an item 76/// Contains the results when hovering over an item
77#[derive(Debug, Default)] 77#[derive(Debug, Default)]
78pub struct HoverResult { 78pub struct HoverResult {
79 results: Vec<String>, 79 pub markup: Markup,
80 actions: Vec<HoverAction>, 80 pub actions: Vec<HoverAction>,
81}
82
83impl HoverResult {
84 pub fn new() -> HoverResult {
85 Self::default()
86 }
87
88 pub fn extend(&mut self, item: Option<String>) {
89 self.results.extend(item);
90 }
91
92 pub fn is_empty(&self) -> bool {
93 self.results.is_empty()
94 }
95
96 pub fn len(&self) -> usize {
97 self.results.len()
98 }
99
100 pub fn first(&self) -> Option<&str> {
101 self.results.first().map(String::as_str)
102 }
103
104 pub fn results(&self) -> &[String] {
105 &self.results
106 }
107
108 pub fn actions(&self) -> &[HoverAction] {
109 &self.actions
110 }
111
112 pub fn push_action(&mut self, action: HoverAction) {
113 self.actions.push(action);
114 }
115
116 /// Returns the results converted into markup
117 /// for displaying in a UI
118 ///
119 /// Does not process actions!
120 pub fn to_markup(&self) -> String {
121 self.results.join("\n\n___\n")
122 }
123} 81}
124 82
125// Feature: Hover 83// Feature: Hover
@@ -132,37 +90,33 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
132 let token = pick_best(file.token_at_offset(position.offset))?; 90 let token = pick_best(file.token_at_offset(position.offset))?;
133 let token = sema.descend_into_macros(token); 91 let token = sema.descend_into_macros(token);
134 92
135 let mut res = HoverResult::new(); 93 let mut res = HoverResult::default();
136 94
137 if let Some((node, name_kind)) = match_ast! { 95 let node = token.parent();
138 match (token.parent()) { 96 let definition = match_ast! {
139 ast::NameRef(name_ref) => { 97 match node {
140 classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition())) 98 ast::NameRef(name_ref) => classify_name_ref(&sema, &name_ref).map(|d| d.definition()),
141 }, 99 ast::Name(name) => classify_name(&sema, &name).map(|d| d.definition()),
142 ast::Name(name) => {
143 classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition()))
144 },
145 _ => None, 100 _ => None,
146 } 101 }
147 } { 102 };
148 let range = sema.original_range(&node).range; 103 if let Some(definition) = definition {
149 let text = hover_text_from_name_kind(db, name_kind.clone()); 104 if let Some(markup) = hover_for_definition(db, definition) {
150 let text = text.map(|text| rewrite_links(db, &text, &name_kind).unwrap_or(text)); 105 let markup = rewrite_links(db, &markup.as_str(), &definition);
151 res.extend(text); 106 res.markup = Markup::from(markup);
152 107 if let Some(action) = show_implementations_action(db, definition) {
153 if !res.is_empty() { 108 res.actions.push(action);
154 if let Some(action) = show_implementations_action(db, name_kind) {
155 res.push_action(action);
156 } 109 }
157 110
158 if let Some(action) = runnable_action(&sema, name_kind, position.file_id) { 111 if let Some(action) = runnable_action(&sema, definition, position.file_id) {
159 res.push_action(action); 112 res.actions.push(action);
160 } 113 }
161 114
162 if let Some(action) = goto_type_action(db, name_kind) { 115 if let Some(action) = goto_type_action(db, definition) {
163 res.push_action(action); 116 res.actions.push(action);
164 } 117 }
165 118
119 let range = sema.original_range(&node).range;
166 return Some(RangeInfo::new(range, res)); 120 return Some(RangeInfo::new(range, res));
167 } 121 }
168 } 122 }
@@ -173,22 +127,16 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
173 127
174 let ty = match_ast! { 128 let ty = match_ast! {
175 match node { 129 match node {
176 ast::MacroCall(_it) => { 130 ast::Expr(it) => sema.type_of_expr(&it)?,
177 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve. 131 ast::Pat(it) => sema.type_of_pat(&it)?,
178 // (e.g expanding a builtin macro). So we give up here. 132 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
179 return None; 133 // (e.g expanding a builtin macro). So we give up here.
180 }, 134 ast::MacroCall(_it) => return None,
181 ast::Expr(it) => { 135 _ => return None,
182 sema.type_of_expr(&it)
183 },
184 ast::Pat(it) => {
185 sema.type_of_pat(&it)
186 },
187 _ => None,
188 } 136 }
189 }?; 137 };
190 138
191 res.extend(Some(rust_code_markup(&ty.display(db)))); 139 res.markup = Markup::fenced_block(&ty.display(db));
192 let range = sema.original_range(&node).range; 140 let range = sema.original_range(&node).range;
193 Some(RangeInfo::new(range, res)) 141 Some(RangeInfo::new(range, res))
194} 142}
@@ -196,8 +144,8 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
196fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { 144fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
197 fn to_action(nav_target: NavigationTarget) -> HoverAction { 145 fn to_action(nav_target: NavigationTarget) -> HoverAction {
198 HoverAction::Implementaion(FilePosition { 146 HoverAction::Implementaion(FilePosition {
199 file_id: nav_target.file_id(), 147 file_id: nav_target.file_id,
200 offset: nav_target.range().start(), 148 offset: nav_target.focus_or_full_range().start(),
201 }) 149 })
202 } 150 }
203 151
@@ -269,7 +217,11 @@ fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
269 .into_iter() 217 .into_iter()
270 .filter_map(|it| { 218 .filter_map(|it| {
271 Some(HoverGotoTypeData { 219 Some(HoverGotoTypeData {
272 mod_path: mod_path(db, &it)?, 220 mod_path: render_path(
221 db,
222 it.module(db)?,
223 it.name(db).map(|name| name.to_string()),
224 ),
273 nav: it.try_to_nav(db)?, 225 nav: it.try_to_nav(db)?,
274 }) 226 })
275 }) 227 })
@@ -281,15 +233,28 @@ fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
281 } 233 }
282} 234}
283 235
284fn hover_text( 236fn hover_markup(
285 docs: Option<String>, 237 docs: Option<String>,
286 desc: Option<String>, 238 desc: Option<String>,
287 mod_path: Option<String>, 239 mod_path: Option<String>,
288) -> Option<String> { 240) -> Option<Markup> {
289 if let Some(desc) = desc { 241 match desc {
290 Some(rust_code_markup_with_doc(&desc, docs.as_deref(), mod_path.as_deref())) 242 Some(desc) => {
291 } else { 243 let mut buf = String::new();
292 docs 244
245 if let Some(mod_path) = mod_path {
246 if !mod_path.is_empty() {
247 format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
248 }
249 }
250 format_to!(buf, "```rust\n{}\n```", desc);
251
252 if let Some(doc) = docs {
253 format_to!(buf, "\n___\n\n{}", doc);
254 }
255 Some(buf.into())
256 }
257 None => docs.map(Markup::from),
293 } 258 }
294} 259}
295 260
@@ -311,43 +276,35 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
311 .map(|name| name.to_string()) 276 .map(|name| name.to_string())
312} 277}
313 278
314fn determine_mod_path(db: &RootDatabase, module: Module, name: Option<String>) -> String { 279fn render_path(db: &RootDatabase, module: Module, item_name: Option<String>) -> String {
315 once(db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string)) 280 let crate_name =
316 .chain( 281 db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string);
317 module 282 let module_path = module
318 .path_to_root(db) 283 .path_to_root(db)
319 .into_iter() 284 .into_iter()
320 .rev() 285 .rev()
321 .map(|it| it.name(db).map(|name| name.to_string())), 286 .flat_map(|it| it.name(db).map(|name| name.to_string()));
322 ) 287 crate_name.into_iter().chain(module_path).chain(item_name).join("::")
323 .chain(once(name))
324 .flatten()
325 .join("::")
326}
327
328// returns None only for ModuleDef::BuiltinType
329fn mod_path(db: &RootDatabase, item: &ModuleDef) -> Option<String> {
330 Some(determine_mod_path(db, item.module(db)?, item.name(db).map(|name| name.to_string())))
331} 288}
332 289
333fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { 290fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
334 def.module(db).map(|module| determine_mod_path(db, module, definition_owner_name(db, def))) 291 def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def)))
335} 292}
336 293
337fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> { 294fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
338 let mod_path = definition_mod_path(db, &def); 295 let mod_path = definition_mod_path(db, &def);
339 return match def { 296 return match def {
340 Definition::Macro(it) => { 297 Definition::Macro(it) => {
341 let src = it.source(db); 298 let src = it.source(db);
342 let docs = Documentation::from_ast(&src.value).map(Into::into); 299 let docs = Documentation::from_ast(&src.value).map(Into::into);
343 hover_text(docs, Some(macro_label(&src.value)), mod_path) 300 hover_markup(docs, Some(macro_label(&src.value)), mod_path)
344 } 301 }
345 Definition::Field(it) => { 302 Definition::Field(it) => {
346 let src = it.source(db); 303 let src = it.source(db);
347 match src.value { 304 match src.value {
348 FieldSource::Named(it) => { 305 FieldSource::Named(it) => {
349 let docs = Documentation::from_ast(&it).map(Into::into); 306 let docs = Documentation::from_ast(&it).map(Into::into);
350 hover_text(docs, it.short_label(), mod_path) 307 hover_markup(docs, it.short_label(), mod_path)
351 } 308 }
352 _ => None, 309 _ => None,
353 } 310 }
@@ -356,7 +313,7 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
356 ModuleDef::Module(it) => match it.definition_source(db).value { 313 ModuleDef::Module(it) => match it.definition_source(db).value {
357 ModuleSource::Module(it) => { 314 ModuleSource::Module(it) => {
358 let docs = Documentation::from_ast(&it).map(Into::into); 315 let docs = Documentation::from_ast(&it).map(Into::into);
359 hover_text(docs, it.short_label(), mod_path) 316 hover_markup(docs, it.short_label(), mod_path)
360 } 317 }
361 _ => None, 318 _ => None,
362 }, 319 },
@@ -369,23 +326,23 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
369 ModuleDef::Static(it) => from_def_source(db, it, mod_path), 326 ModuleDef::Static(it) => from_def_source(db, it, mod_path),
370 ModuleDef::Trait(it) => from_def_source(db, it, mod_path), 327 ModuleDef::Trait(it) => from_def_source(db, it, mod_path),
371 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), 328 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path),
372 ModuleDef::BuiltinType(it) => Some(it.to_string()), 329 ModuleDef::BuiltinType(it) => return Some(it.to_string().into()),
373 }, 330 },
374 Definition::Local(it) => Some(rust_code_markup(&it.ty(db).display(db))), 331 Definition::Local(it) => return Some(Markup::fenced_block(&it.ty(db).display(db))),
375 Definition::TypeParam(_) | Definition::SelfType(_) => { 332 Definition::TypeParam(_) | Definition::SelfType(_) => {
376 // FIXME: Hover for generic param 333 // FIXME: Hover for generic param
377 None 334 None
378 } 335 }
379 }; 336 };
380 337
381 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String> 338 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup>
382 where 339 where
383 D: HasSource<Ast = A>, 340 D: HasSource<Ast = A>,
384 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel + ast::AttrsOwner, 341 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel + ast::AttrsOwner,
385 { 342 {
386 let src = def.source(db); 343 let src = def.source(db);
387 let docs = Documentation::from_ast(&src.value).map(Into::into); 344 let docs = Documentation::from_ast(&src.value).map(Into::into);
388 hover_text(docs, src.value.short_label(), mod_path) 345 hover_markup(docs, src.value.short_label(), mod_path)
389 } 346 }
390} 347}
391 348
@@ -422,7 +379,7 @@ fn map_links<'e>(
422} 379}
423 380
424/// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs) 381/// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs)
425fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition) -> Option<String> { 382fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition) -> String {
426 let doc = Parser::new_with_broken_link_callback( 383 let doc = Parser::new_with_broken_link_callback(
427 markdown, 384 markdown,
428 Options::empty(), 385 Options::empty(),
@@ -452,7 +409,7 @@ fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition) ->
452 }); 409 });
453 let mut out = String::new(); 410 let mut out = String::new();
454 cmark(doc, &mut out, None).ok(); 411 cmark(doc, &mut out, None).ok();
455 Some(out) 412 out
456} 413}
457 414
458#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] 415#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
@@ -665,7 +622,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
665 fn priority(n: &SyntaxToken) -> usize { 622 fn priority(n: &SyntaxToken) -> usize {
666 match n.kind() { 623 match n.kind() {
667 IDENT | INT_NUMBER => 3, 624 IDENT | INT_NUMBER => 3,
668 L_PAREN | R_PAREN => 2, 625 T!['('] | T![')'] => 2,
669 kind if kind.is_trivia() => 0, 626 kind if kind.is_trivia() => 0,
670 _ => 1, 627 _ => 1,
671 } 628 }
@@ -674,64 +631,38 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
674 631
675#[cfg(test)] 632#[cfg(test)]
676mod tests { 633mod tests {
677 use super::*; 634 use expect::{expect, Expect};
678 use insta::assert_debug_snapshot;
679
680 use ra_db::FileLoader; 635 use ra_db::FileLoader;
681 use ra_syntax::TextRange;
682 636
683 use crate::mock_analysis::analysis_and_position; 637 use crate::mock_analysis::analysis_and_position;
684 638
685 fn trim_markup(s: &str) -> String { 639 use super::*;
686 s.trim()
687 .replace("````", "```")
688 .replace("---", "___")
689 .replace("\\<-", "<-")
690 .replace("```\n\n___", "```\n___")
691 .trim_start_matches("```rust\n")
692 .trim_start_matches("test\n```\n\n```rust\n")
693 .trim_end_matches("\n```")
694 .to_string()
695 }
696
697 fn trim_markup_opt(s: Option<&str>) -> Option<String> {
698 s.map(trim_markup)
699 }
700 640
701 fn assert_impl_action(action: &HoverAction, position: u32) { 641 fn check_hover_no_result(ra_fixture: &str) {
702 let offset = match action { 642 let (analysis, position) = analysis_and_position(ra_fixture);
703 HoverAction::Implementaion(pos) => pos.offset, 643 assert!(analysis.hover(position).unwrap().is_none());
704 it => panic!("Unexpected hover action: {:#?}", it),
705 };
706 assert_eq!(offset, position.into());
707 } 644 }
708 645
709 fn check_hover_result(fixture: &str, expected: &[&str]) -> (String, Vec<HoverAction>) { 646 fn check(ra_fixture: &str, expect: Expect) {
710 let (analysis, position) = analysis_and_position(fixture); 647 let (analysis, position) = analysis_and_position(ra_fixture);
711 let hover = analysis.hover(position).unwrap().unwrap(); 648 let hover = analysis.hover(position).unwrap().unwrap();
712 let mut results = Vec::from(hover.info.results());
713 results.sort();
714
715 for (markup, expected) in
716 results.iter().zip(expected.iter().chain(std::iter::repeat(&"<missing>")))
717 {
718 assert_eq!(trim_markup(&markup), *expected);
719 }
720
721 assert_eq!(hover.info.len(), expected.len());
722 649
723 let content = analysis.db.file_text(position.file_id); 650 let content = analysis.db.file_text(position.file_id);
724 (content[hover.range].to_string(), hover.info.actions().to_vec()) 651 let hovered_element = &content[hover.range];
652
653 let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
654 expect.assert_eq(&actual)
725 } 655 }
726 656
727 fn check_hover_no_result(fixture: &str) { 657 fn check_actions(ra_fixture: &str, expect: Expect) {
728 let (analysis, position) = analysis_and_position(fixture); 658 let (analysis, position) = analysis_and_position(ra_fixture);
729 assert!(analysis.hover(position).unwrap().is_none()); 659 let hover = analysis.hover(position).unwrap().unwrap();
660 expect.assert_debug_eq(&hover.info.actions)
730 } 661 }
731 662
732 #[test] 663 #[test]
733 fn hover_shows_type_of_an_expression() { 664 fn hover_shows_type_of_an_expression() {
734 let (analysis, position) = analysis_and_position( 665 check(
735 r#" 666 r#"
736pub fn foo() -> u32 { 1 } 667pub fn foo() -> u32 { 1 }
737 668
@@ -739,606 +670,643 @@ fn main() {
739 let foo_test = foo()<|>; 670 let foo_test = foo()<|>;
740} 671}
741"#, 672"#,
673 expect![[r#"
674 *foo()*
675 ```rust
676 u32
677 ```
678 "#]],
742 ); 679 );
743 let hover = analysis.hover(position).unwrap().unwrap();
744 assert_eq!(hover.range, TextRange::new(58.into(), 63.into()));
745 assert_eq!(trim_markup_opt(hover.info.first()).as_deref(), Some("u32"));
746 } 680 }
747 681
748 #[test] 682 #[test]
749 fn hover_shows_long_type_of_an_expression() { 683 fn hover_shows_long_type_of_an_expression() {
750 check_hover_result( 684 check(
751 r#" 685 r#"
752 //- /main.rs 686struct Scan<A, B, C> { a: A, b: B, c: C }
753 struct Scan<A, B, C> { 687struct Iter<I> { inner: I }
754 a: A, 688enum Option<T> { Some(T), None }
755 b: B,
756 c: C,
757 }
758 689
759 struct FakeIter<I> { 690struct OtherStruct<T> { i: T }
760 inner: I,
761 }
762
763 struct OtherStruct<T> {
764 i: T,
765 }
766 691
767 enum FakeOption<T> { 692fn scan<A, B, C>(a: A, b: B, c: C) -> Iter<Scan<OtherStruct<A>, B, C>> {
768 Some(T), 693 Iter { inner: Scan { a, b, c } }
769 None, 694}
770 }
771
772 fn scan<A, B, C>(a: A, b: B, c: C) -> FakeIter<Scan<OtherStruct<A>, B, C>> {
773 FakeIter { inner: Scan { a, b, c } }
774 }
775 695
776 fn main() { 696fn main() {
777 let num: i32 = 55; 697 let num: i32 = 55;
778 let closure = |memo: &mut u32, value: &u32, _another: &mut u32| -> FakeOption<u32> { 698 let closure = |memo: &mut u32, value: &u32, _another: &mut u32| -> Option<u32> {
779 FakeOption::Some(*memo + value) 699 Option::Some(*memo + value)
780 }; 700 };
781 let number = 5u32; 701 let number = 5u32;
782 let mut iter<|> = scan(OtherStruct { i: num }, closure, number); 702 let mut iter<|> = scan(OtherStruct { i: num }, closure, number);
783 } 703}
784 "#, 704"#,
785 &["FakeIter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> FakeOption<u32>, u32>>"], 705 expect![[r#"
706 *iter*
707 ```rust
708 Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>>
709 ```
710 "#]],
786 ); 711 );
787 } 712 }
788 713
789 #[test] 714 #[test]
790 fn hover_shows_fn_signature() { 715 fn hover_shows_fn_signature() {
791 // Single file with result 716 // Single file with result
792 check_hover_result( 717 check(
793 r#" 718 r#"
794 //- /main.rs 719pub fn foo() -> u32 { 1 }
795 pub fn foo() -> u32 { 1 }
796 720
797 fn main() { 721fn main() { let foo_test = fo<|>o(); }
798 let foo_test = fo<|>o(); 722"#,
799 } 723 expect![[r#"
800 "#, 724 *foo*
801 &["pub fn foo() -> u32"], 725 ```rust
726 pub fn foo() -> u32
727 ```
728 "#]],
802 ); 729 );
803 730
804 // Multiple candidates but results are ambiguous. 731 // Multiple candidates but results are ambiguous.
805 check_hover_result( 732 check(
806 r#" 733 r#"
807 //- /a.rs 734//- /a.rs
808 pub fn foo() -> u32 { 1 } 735pub fn foo() -> u32 { 1 }
809 736
810 //- /b.rs 737//- /b.rs
811 pub fn foo() -> &str { "" } 738pub fn foo() -> &str { "" }
812 739
813 //- /c.rs 740//- /c.rs
814 pub fn foo(a: u32, b: u32) {} 741pub fn foo(a: u32, b: u32) {}
815 742
816 //- /main.rs 743//- /main.rs
817 mod a; 744mod a;
818 mod b; 745mod b;
819 mod c; 746mod c;
820 747
821 fn main() { 748fn main() { let foo_test = fo<|>o(); }
822 let foo_test = fo<|>o();
823 }
824 "#, 749 "#,
825 &["{unknown}"], 750 expect![[r#"
751 *foo*
752 ```rust
753 {unknown}
754 ```
755 "#]],
826 ); 756 );
827 } 757 }
828 758
829 #[test] 759 #[test]
830 fn hover_shows_fn_signature_with_type_params() { 760 fn hover_shows_fn_signature_with_type_params() {
831 check_hover_result( 761 check(
832 r#" 762 r#"
833 //- /main.rs 763pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str { }
834 pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str { }
835 764
836 fn main() { 765fn main() { let foo_test = fo<|>o(); }
837 let foo_test = fo<|>o();
838 }
839 "#, 766 "#,
840 &["pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str"], 767 expect![[r#"
768 *foo*
769 ```rust
770 pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str
771 ```
772 "#]],
841 ); 773 );
842 } 774 }
843 775
844 #[test] 776 #[test]
845 fn hover_shows_fn_signature_on_fn_name() { 777 fn hover_shows_fn_signature_on_fn_name() {
846 check_hover_result( 778 check(
847 r#" 779 r#"
848 //- /main.rs 780pub fn foo<|>(a: u32, b: u32) -> u32 {}
849 pub fn foo<|>(a: u32, b: u32) -> u32 {}
850 781
851 fn main() { 782fn main() { }
852 } 783"#,
853 "#, 784 expect![[r#"
854 &["pub fn foo(a: u32, b: u32) -> u32"], 785 *foo*
786 ```rust
787 pub fn foo(a: u32, b: u32) -> u32
788 ```
789 "#]],
855 ); 790 );
856 } 791 }
857 792
858 #[test] 793 #[test]
859 fn hover_shows_struct_field_info() { 794 fn hover_shows_struct_field_info() {
860 // Hovering over the field when instantiating 795 // Hovering over the field when instantiating
861 check_hover_result( 796 check(
862 r#" 797 r#"
863 //- /main.rs 798struct Foo { field_a: u32 }
864 struct Foo {
865 field_a: u32,
866 }
867 799
868 fn main() { 800fn main() {
869 let foo = Foo { 801 let foo = Foo { field_a<|>: 0, };
870 field_a<|>: 0, 802}
871 }; 803"#,
872 } 804 expect![[r#"
873 "#, 805 *field_a*
874 &["test::Foo\n```\n\n```rust\nfield_a: u32"], 806 ```rust
807 Foo
808 ```
809
810 ```rust
811 field_a: u32
812 ```
813 "#]],
875 ); 814 );
876 815
877 // Hovering over the field in the definition 816 // Hovering over the field in the definition
878 check_hover_result( 817 check(
879 r#" 818 r#"
880 //- /main.rs 819struct Foo { field_a<|>: u32 }
881 struct Foo {
882 field_a<|>: u32,
883 }
884 820
885 fn main() { 821fn main() {
886 let foo = Foo { 822 let foo = Foo { field_a: 0 };
887 field_a: 0, 823}
888 }; 824"#,
889 } 825 expect![[r#"
890 "#, 826 *field_a*
891 &["test::Foo\n```\n\n```rust\nfield_a: u32"], 827 ```rust
828 Foo
829 ```
830
831 ```rust
832 field_a: u32
833 ```
834 "#]],
892 ); 835 );
893 } 836 }
894 837
895 #[test] 838 #[test]
896 fn hover_const_static() { 839 fn hover_const_static() {
897 check_hover_result( 840 check(
898 r#" 841 r#"const foo<|>: u32 = 0;"#,
899 //- /main.rs 842 expect![[r#"
900 const foo<|>: u32 = 0; 843 *foo*
901 "#, 844 ```rust
902 &["const foo: u32"], 845 const foo: u32
846 ```
847 "#]],
903 ); 848 );
904 849 check(
905 check_hover_result( 850 r#"static foo<|>: u32 = 0;"#,
906 r#" 851 expect![[r#"
907 //- /main.rs 852 *foo*
908 static foo<|>: u32 = 0; 853 ```rust
909 "#, 854 static foo: u32
910 &["static foo: u32"], 855 ```
856 "#]],
911 ); 857 );
912 } 858 }
913 859
914 #[test] 860 #[test]
915 fn hover_default_generic_types() { 861 fn hover_default_generic_types() {
916 check_hover_result( 862 check(
917 r#" 863 r#"
918//- /main.rs 864struct Test<K, T = u8> { k: K, t: T }
919struct Test<K, T = u8> {
920 k: K,
921 t: T,
922}
923 865
924fn main() { 866fn main() {
925 let zz<|> = Test { t: 23u8, k: 33 }; 867 let zz<|> = Test { t: 23u8, k: 33 };
926}"#, 868}"#,
927 &["Test<i32, u8>"], 869 expect![[r#"
870 *zz*
871 ```rust
872 Test<i32, u8>
873 ```
874 "#]],
928 ); 875 );
929 } 876 }
930 877
931 #[test] 878 #[test]
932 fn hover_some() { 879 fn hover_some() {
933 let (analysis, position) = analysis_and_position( 880 check(
934 " 881 r#"
935 enum Option<T> { Some(T) } 882enum Option<T> { Some(T) }
936 use Option::Some; 883use Option::Some;
937 884
938 fn main() { 885fn main() { So<|>me(12); }
939 So<|>me(12); 886"#,
940 } 887 expect![[r#"
941 ", 888 *Some*
942 ); 889 ```rust
943 let hover = analysis.hover(position).unwrap().unwrap(); 890 Option
944 assert_eq!( 891 ```
945 trim_markup_opt(hover.info.first()).as_deref(), 892
946 Some("test::Option\n```\n\n```rust\nSome") 893 ```rust
894 Some
895 ```
896 "#]],
947 ); 897 );
948 898
949 let (analysis, position) = analysis_and_position( 899 check(
950 " 900 r#"
951 enum Option<T> { Some(T) } 901enum Option<T> { Some(T) }
952 use Option::Some; 902use Option::Some;
953 903
954 fn main() { 904fn main() { let b<|>ar = Some(12); }
955 let b<|>ar = Some(12); 905"#,
956 } 906 expect![[r#"
957 ", 907 *bar*
908 ```rust
909 Option<i32>
910 ```
911 "#]],
958 ); 912 );
959 let hover = analysis.hover(position).unwrap().unwrap();
960 assert_eq!(trim_markup_opt(hover.info.first()).as_deref(), Some("Option<i32>"));
961 } 913 }
962 914
963 #[test] 915 #[test]
964 fn hover_enum_variant() { 916 fn hover_enum_variant() {
965 check_hover_result( 917 check(
966 r#" 918 r#"
967 //- /main.rs 919enum Option<T> {
968 enum Option<T> { 920 /// The None variant
969 /// The None variant 921 Non<|>e
970 Non<|>e 922}
971 } 923"#,
972 "#, 924 expect![[r#"
973 &[" 925 *None*
974test::Option 926 ```rust
975``` 927 Option
928 ```
976 929
977```rust 930 ```rust
978None 931 None
979``` 932 ```
980___ 933 ___
981 934
982The None variant 935 The None variant
983 " 936 "#]],
984 .trim()],
985 ); 937 );
986 938
987 check_hover_result( 939 check(
988 r#" 940 r#"
989 //- /main.rs 941enum Option<T> {
990 enum Option<T> { 942 /// The Some variant
991 /// The Some variant 943 Some(T)
992 Some(T) 944}
993 } 945fn main() {
994 fn main() { 946 let s = Option::Som<|>e(12);
995 let s = Option::Som<|>e(12); 947}
996 } 948"#,
997 "#, 949 expect![[r#"
998 &[" 950 *Some*
999test::Option 951 ```rust
1000``` 952 Option
953 ```
1001 954
1002```rust 955 ```rust
1003Some 956 Some
1004``` 957 ```
1005___ 958 ___
1006 959
1007The Some variant 960 The Some variant
1008 " 961 "#]],
1009 .trim()],
1010 ); 962 );
1011 } 963 }
1012 964
1013 #[test] 965 #[test]
1014 fn hover_for_local_variable() { 966 fn hover_for_local_variable() {
1015 let (analysis, position) = analysis_and_position("fn func(foo: i32) { fo<|>o; }"); 967 check(
1016 let hover = analysis.hover(position).unwrap().unwrap(); 968 r#"fn func(foo: i32) { fo<|>o; }"#,
1017 assert_eq!(trim_markup_opt(hover.info.first()).as_deref(), Some("i32")); 969 expect![[r#"
970 *foo*
971 ```rust
972 i32
973 ```
974 "#]],
975 )
1018 } 976 }
1019 977
1020 #[test] 978 #[test]
1021 fn hover_for_local_variable_pat() { 979 fn hover_for_local_variable_pat() {
1022 let (analysis, position) = analysis_and_position("fn func(fo<|>o: i32) {}"); 980 check(
1023 let hover = analysis.hover(position).unwrap().unwrap(); 981 r#"fn func(fo<|>o: i32) {}"#,
1024 assert_eq!(trim_markup_opt(hover.info.first()).as_deref(), Some("i32")); 982 expect![[r#"
983 *foo*
984 ```rust
985 i32
986 ```
987 "#]],
988 )
1025 } 989 }
1026 990
1027 #[test] 991 #[test]
1028 fn hover_local_var_edge() { 992 fn hover_local_var_edge() {
1029 let (analysis, position) = analysis_and_position( 993 check(
1030 " 994 r#"fn func(foo: i32) { if true { <|>foo; }; }"#,
1031fn func(foo: i32) { if true { <|>foo; }; } 995 expect![[r#"
1032", 996 *foo*
1033 ); 997 ```rust
1034 let hover = analysis.hover(position).unwrap().unwrap(); 998 i32
1035 assert_eq!(trim_markup_opt(hover.info.first()).as_deref(), Some("i32")); 999 ```
1000 "#]],
1001 )
1036 } 1002 }
1037 1003
1038 #[test] 1004 #[test]
1039 fn hover_for_param_edge() { 1005 fn hover_for_param_edge() {
1040 let (analysis, position) = analysis_and_position("fn func(<|>foo: i32) {}"); 1006 check(
1041 let hover = analysis.hover(position).unwrap().unwrap(); 1007 r#"fn func(<|>foo: i32) {}"#,
1042 assert_eq!(trim_markup_opt(hover.info.first()).as_deref(), Some("i32")); 1008 expect![[r#"
1009 *foo*
1010 ```rust
1011 i32
1012 ```
1013 "#]],
1014 )
1043 } 1015 }
1044 1016
1045 #[test] 1017 #[test]
1046 fn test_hover_infer_associated_method_result() { 1018 fn test_hover_infer_associated_method_result() {
1047 let (analysis, position) = analysis_and_position( 1019 check(
1048 " 1020 r#"
1049 struct Thing { x: u32 } 1021struct Thing { x: u32 }
1050 1022
1051 impl Thing { 1023impl Thing {
1052 fn new() -> Thing { 1024 fn new() -> Thing { Thing { x: 0 } }
1053 Thing { x: 0 } 1025}
1054 }
1055 }
1056 1026
1057 fn main() { 1027fn main() { let foo_<|>test = Thing::new(); }
1058 let foo_<|>test = Thing::new(); 1028 "#,
1059 } 1029 expect![[r#"
1060 ", 1030 *foo_test*
1061 ); 1031 ```rust
1062 let hover = analysis.hover(position).unwrap().unwrap(); 1032 Thing
1063 assert_eq!(trim_markup_opt(hover.info.first()).as_deref(), Some("Thing")); 1033 ```
1034 "#]],
1035 )
1064 } 1036 }
1065 1037
1066 #[test] 1038 #[test]
1067 fn test_hover_infer_associated_method_exact() { 1039 fn test_hover_infer_associated_method_exact() {
1068 let (analysis, position) = analysis_and_position( 1040 check(
1069 " 1041 r#"
1070 mod wrapper { 1042mod wrapper {
1071 struct Thing { x: u32 } 1043 struct Thing { x: u32 }
1072 1044
1073 impl Thing { 1045 impl Thing {
1074 fn new() -> Thing { 1046 fn new() -> Thing { Thing { x: 0 } }
1075 Thing { x: 0 } 1047 }
1076 } 1048}
1077 }
1078 }
1079 1049
1080 fn main() { 1050fn main() { let foo_test = wrapper::Thing::new<|>(); }
1081 let foo_test = wrapper::Thing::new<|>(); 1051"#,
1082 } 1052 expect![[r#"
1083 ", 1053 *new*
1084 ); 1054 ```rust
1085 let hover = analysis.hover(position).unwrap().unwrap(); 1055 wrapper::Thing
1086 assert_eq!( 1056 ```
1087 trim_markup_opt(hover.info.first()).as_deref(), 1057
1088 Some("test::wrapper::Thing\n```\n\n```rust\nfn new() -> Thing") 1058 ```rust
1089 ); 1059 fn new() -> Thing
1060 ```
1061 "#]],
1062 )
1090 } 1063 }
1091 1064
1092 #[test] 1065 #[test]
1093 fn test_hover_infer_associated_const_in_pattern() { 1066 fn test_hover_infer_associated_const_in_pattern() {
1094 let (analysis, position) = analysis_and_position( 1067 check(
1095 " 1068 r#"
1096 struct X; 1069struct X;
1097 impl X { 1070impl X {
1098 const C: u32 = 1; 1071 const C: u32 = 1;
1099 } 1072}
1100 1073
1101 fn main() { 1074fn main() {
1102 match 1 { 1075 match 1 {
1103 X::C<|> => {}, 1076 X::C<|> => {},
1104 2 => {}, 1077 2 => {},
1105 _ => {} 1078 _ => {}
1106 }; 1079 };
1107 } 1080}
1108 ", 1081"#,
1109 ); 1082 expect![[r#"
1110 let hover = analysis.hover(position).unwrap().unwrap(); 1083 *C*
1111 assert_eq!(trim_markup_opt(hover.info.first()).as_deref(), Some("const C: u32")); 1084 ```rust
1085 const C: u32
1086 ```
1087 "#]],
1088 )
1112 } 1089 }
1113 1090
1114 #[test] 1091 #[test]
1115 fn test_hover_self() { 1092 fn test_hover_self() {
1116 let (analysis, position) = analysis_and_position( 1093 check(
1117 " 1094 r#"
1118 struct Thing { x: u32 } 1095struct Thing { x: u32 }
1119 impl Thing { 1096impl Thing {
1120 fn new() -> Self { 1097 fn new() -> Self { Self<|> { x: 0 } }
1121 Self<|> { x: 0 } 1098}
1122 } 1099"#,
1123 } 1100 expect![[r#"
1124 ", 1101 *Self { x: 0 }*
1125 ); 1102 ```rust
1126 let hover = analysis.hover(position).unwrap().unwrap(); 1103 Thing
1127 assert_eq!(trim_markup_opt(hover.info.first()).as_deref(), Some("Thing")); 1104 ```
1128 1105 "#]],
1129 /* FIXME: revive these tests 1106 )
1130 let (analysis, position) = analysis_and_position( 1107 } /* FIXME: revive these tests
1131 " 1108 let (analysis, position) = analysis_and_position(
1132 struct Thing { x: u32 } 1109 "
1133 impl Thing { 1110 struct Thing { x: u32 }
1134 fn new() -> Self<|> { 1111 impl Thing {
1135 Self { x: 0 } 1112 fn new() -> Self<|> {
1136 } 1113 Self { x: 0 }
1137 } 1114 }
1138 ", 1115 }
1139 ); 1116 ",
1140 1117 );
1141 let hover = analysis.hover(position).unwrap().unwrap(); 1118
1142 assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing")); 1119 let hover = analysis.hover(position).unwrap().unwrap();
1143 1120 assert_eq!(trim_markup(&hover.info.markup.as_str()), ("Thing"));
1144 let (analysis, position) = analysis_and_position( 1121
1145 " 1122 let (analysis, position) = analysis_and_position(
1146 enum Thing { A } 1123 "
1147 impl Thing { 1124 enum Thing { A }
1148 pub fn new() -> Self<|> { 1125 impl Thing {
1149 Thing::A 1126 pub fn new() -> Self<|> {
1150 } 1127 Thing::A
1151 } 1128 }
1152 ", 1129 }
1153 ); 1130 ",
1154 let hover = analysis.hover(position).unwrap().unwrap(); 1131 );
1155 assert_eq!(trim_markup_opt(hover.info.first()), Some("enum Thing")); 1132 let hover = analysis.hover(position).unwrap().unwrap();
1156 1133 assert_eq!(trim_markup(&hover.info.markup.as_str()), ("enum Thing"));
1157 let (analysis, position) = analysis_and_position( 1134
1158 " 1135 let (analysis, position) = analysis_and_position(
1159 enum Thing { A } 1136 "
1160 impl Thing { 1137 enum Thing { A }
1161 pub fn thing(a: Self<|>) { 1138 impl Thing {
1162 } 1139 pub fn thing(a: Self<|>) {
1163 } 1140 }
1164 ", 1141 }
1165 ); 1142 ",
1166 let hover = analysis.hover(position).unwrap().unwrap(); 1143 );
1167 assert_eq!(trim_markup_opt(hover.info.first()), Some("enum Thing")); 1144 let hover = analysis.hover(position).unwrap().unwrap();
1168 */ 1145 assert_eq!(trim_markup(&hover.info.markup.as_str()), ("enum Thing"));
1169 } 1146 */
1170 1147
1171 #[test] 1148 #[test]
1172 fn test_hover_shadowing_pat() { 1149 fn test_hover_shadowing_pat() {
1173 let (analysis, position) = analysis_and_position( 1150 check(
1174 " 1151 r#"
1175 fn x() {} 1152fn x() {}
1176 1153
1177 fn y() { 1154fn y() {
1178 let x = 0i32; 1155 let x = 0i32;
1179 x<|>; 1156 x<|>;
1180 } 1157}
1181 ", 1158"#,
1182 ); 1159 expect![[r#"
1183 let hover = analysis.hover(position).unwrap().unwrap(); 1160 *x*
1184 assert_eq!(trim_markup_opt(hover.info.first()).as_deref(), Some("i32")); 1161 ```rust
1162 i32
1163 ```
1164 "#]],
1165 )
1185 } 1166 }
1186 1167
1187 #[test] 1168 #[test]
1188 fn test_hover_macro_invocation() { 1169 fn test_hover_macro_invocation() {
1189 let (analysis, position) = analysis_and_position( 1170 check(
1190 " 1171 r#"
1191 macro_rules! foo { 1172macro_rules! foo { () => {} }
1192 () => {}
1193 }
1194 1173
1195 fn f() { 1174fn f() { fo<|>o!(); }
1196 fo<|>o!(); 1175"#,
1197 } 1176 expect![[r#"
1198 ", 1177 *foo*
1199 ); 1178 ```rust
1200 let hover = analysis.hover(position).unwrap().unwrap(); 1179 macro_rules! foo
1201 assert_eq!(trim_markup_opt(hover.info.first()).as_deref(), Some("macro_rules! foo")); 1180 ```
1181 "#]],
1182 )
1202 } 1183 }
1203 1184
1204 #[test] 1185 #[test]
1205 fn test_hover_tuple_field() { 1186 fn test_hover_tuple_field() {
1206 let (analysis, position) = analysis_and_position( 1187 check(
1207 " 1188 r#"struct TS(String, i32<|>);"#,
1208 struct TS(String, i32<|>); 1189 expect![[r#"
1209 ", 1190 *i32*
1210 ); 1191 i32
1211 let hover = analysis.hover(position).unwrap().unwrap(); 1192 "#]],
1212 assert_eq!(trim_markup_opt(hover.info.first()).as_deref(), Some("i32")); 1193 )
1213 } 1194 }
1214 1195
1215 #[test] 1196 #[test]
1216 fn test_hover_through_macro() { 1197 fn test_hover_through_macro() {
1217 let (hover_on, _) = check_hover_result( 1198 check(
1218 " 1199 r#"
1219 //- /lib.rs 1200macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
1220 macro_rules! id { 1201fn foo() {}
1221 ($($tt:tt)*) => { $($tt)* } 1202id! {
1222 } 1203 fn bar() { fo<|>o(); }
1223 fn foo() {} 1204}
1224 id! { 1205"#,
1225 fn bar() { 1206 expect![[r#"
1226 fo<|>o(); 1207 *foo*
1227 } 1208 ```rust
1228 } 1209 fn foo()
1229 ", 1210 ```
1230 &["fn foo()"], 1211 "#]],
1231 ); 1212 );
1232
1233 assert_eq!(hover_on, "foo")
1234 } 1213 }
1235 1214
1236 #[test] 1215 #[test]
1237 fn test_hover_through_expr_in_macro() { 1216 fn test_hover_through_expr_in_macro() {
1238 let (hover_on, _) = check_hover_result( 1217 check(
1239 " 1218 r#"
1240 //- /lib.rs 1219macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
1241 macro_rules! id { 1220fn foo(bar:u32) { let a = id!(ba<|>r); }
1242 ($($tt:tt)*) => { $($tt)* } 1221"#,
1243 } 1222 expect![[r#"
1244 fn foo(bar:u32) { 1223 *bar*
1245 let a = id!(ba<|>r); 1224 ```rust
1246 } 1225 u32
1247 ", 1226 ```
1248 &["u32"], 1227 "#]],
1249 ); 1228 );
1250
1251 assert_eq!(hover_on, "bar")
1252 } 1229 }
1253 1230
1254 #[test] 1231 #[test]
1255 fn test_hover_through_expr_in_macro_recursive() { 1232 fn test_hover_through_expr_in_macro_recursive() {
1256 let (hover_on, _) = check_hover_result( 1233 check(
1257 " 1234 r#"
1258 //- /lib.rs 1235macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
1259 macro_rules! id_deep { 1236macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
1260 ($($tt:tt)*) => { $($tt)* } 1237fn foo(bar:u32) { let a = id!(ba<|>r); }
1261 } 1238"#,
1262 macro_rules! id { 1239 expect![[r#"
1263 ($($tt:tt)*) => { id_deep!($($tt)*) } 1240 *bar*
1264 } 1241 ```rust
1265 fn foo(bar:u32) { 1242 u32
1266 let a = id!(ba<|>r); 1243 ```
1267 } 1244 "#]],
1268 ",
1269 &["u32"],
1270 ); 1245 );
1271
1272 assert_eq!(hover_on, "bar")
1273 } 1246 }
1274 1247
1275 #[test] 1248 #[test]
1276 fn test_hover_through_func_in_macro_recursive() { 1249 fn test_hover_through_func_in_macro_recursive() {
1277 let (hover_on, _) = check_hover_result( 1250 check(
1278 " 1251 r#"
1279 //- /lib.rs 1252macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
1280 macro_rules! id_deep { 1253macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
1281 ($($tt:tt)*) => { $($tt)* } 1254fn bar() -> u32 { 0 }
1282 } 1255fn foo() { let a = id!([0u32, bar(<|>)] ); }
1283 macro_rules! id { 1256"#,
1284 ($($tt:tt)*) => { id_deep!($($tt)*) } 1257 expect![[r#"
1285 } 1258 *bar()*
1286 fn bar() -> u32 { 1259 ```rust
1287 0 1260 u32
1288 } 1261 ```
1289 fn foo() { 1262 "#]],
1290 let a = id!([0u32, bar(<|>)] );
1291 }
1292 ",
1293 &["u32"],
1294 ); 1263 );
1295
1296 assert_eq!(hover_on, "bar()")
1297 } 1264 }
1298 1265
1299 #[test] 1266 #[test]
1300 fn test_hover_through_literal_string_in_macro() { 1267 fn test_hover_through_literal_string_in_macro() {
1301 let (hover_on, _) = check_hover_result( 1268 check(
1302 r#" 1269 r#"
1303 //- /lib.rs 1270macro_rules! arr { ($($tt:tt)*) => { [$($tt)*)] } }
1304 macro_rules! arr { 1271fn foo() {
1305 ($($tt:tt)*) => { [$($tt)*)] } 1272 let mastered_for_itunes = "";
1306 } 1273 let _ = arr!("Tr<|>acks", &mastered_for_itunes);
1307 fn foo() { 1274}
1308 let mastered_for_itunes = ""; 1275"#,
1309 let _ = arr!("Tr<|>acks", &mastered_for_itunes); 1276 expect![[r#"
1310 } 1277 *"Tracks"*
1311 "#, 1278 ```rust
1312 &["&str"], 1279 &str
1280 ```
1281 "#]],
1313 ); 1282 );
1314
1315 assert_eq!(hover_on, "\"Tracks\"");
1316 } 1283 }
1317 1284
1318 #[test] 1285 #[test]
1319 fn test_hover_through_assert_macro() { 1286 fn test_hover_through_assert_macro() {
1320 let (hover_on, _) = check_hover_result( 1287 check(
1321 r#" 1288 r#"
1322 //- /lib.rs 1289#[rustc_builtin_macro]
1323 #[rustc_builtin_macro] 1290macro_rules! assert {}
1324 macro_rules! assert {}
1325 1291
1326 fn bar() -> bool { true } 1292fn bar() -> bool { true }
1327 fn foo() { 1293fn foo() {
1328 assert!(ba<|>r()); 1294 assert!(ba<|>r());
1329 } 1295}
1330 "#, 1296"#,
1331 &["fn bar() -> bool"], 1297 expect![[r#"
1298 *bar*
1299 ```rust
1300 fn bar() -> bool
1301 ```
1302 "#]],
1332 ); 1303 );
1333
1334 assert_eq!(hover_on, "bar");
1335 } 1304 }
1336 1305
1337 #[test] 1306 #[test]
1338 fn test_hover_through_literal_string_in_builtin_macro() { 1307 fn test_hover_through_literal_string_in_builtin_macro() {
1339 check_hover_no_result( 1308 check_hover_no_result(
1340 r#" 1309 r#"
1341 //- /lib.rs
1342 #[rustc_builtin_macro] 1310 #[rustc_builtin_macro]
1343 macro_rules! format {} 1311 macro_rules! format {}
1344 1312
@@ -1351,122 +1319,159 @@ fn func(foo: i32) { if true { <|>foo; }; }
1351 1319
1352 #[test] 1320 #[test]
1353 fn test_hover_non_ascii_space_doc() { 1321 fn test_hover_non_ascii_space_doc() {
1354 check_hover_result( 1322 check(
1355 " 1323 "
1356 //- /lib.rs 1324/// <- `\u{3000}` here
1357 /// <- `\u{3000}` here 1325fn foo() { }
1358 fn foo() {
1359 }
1360 1326
1361 fn bar() { 1327fn bar() { fo<|>o(); }
1362 fo<|>o(); 1328",
1363 } 1329 expect![[r#"
1364 ", 1330 *foo*
1365 &["fn foo()\n```\n___\n\n<- `\u{3000}` here"], 1331 ```rust
1332 fn foo()
1333 ```
1334 ___
1335
1336 <- ` ` here
1337 "#]],
1366 ); 1338 );
1367 } 1339 }
1368 1340
1369 #[test] 1341 #[test]
1370 fn test_hover_function_show_qualifiers() { 1342 fn test_hover_function_show_qualifiers() {
1371 check_hover_result( 1343 check(
1372 " 1344 r#"async fn foo<|>() {}"#,
1373 //- /lib.rs 1345 expect![[r#"
1374 async fn foo<|>() {} 1346 *foo*
1375 ", 1347 ```rust
1376 &["async fn foo()"], 1348 async fn foo()
1377 ); 1349 ```
1378 check_hover_result( 1350 "#]],
1379 " 1351 );
1380 //- /lib.rs 1352 check(
1381 pub const unsafe fn foo<|>() {} 1353 r#"pub const unsafe fn foo<|>() {}"#,
1382 ", 1354 expect![[r#"
1383 &["pub const unsafe fn foo()"], 1355 *foo*
1384 ); 1356 ```rust
1385 check_hover_result( 1357 pub const unsafe fn foo()
1386 r#" 1358 ```
1387 //- /lib.rs 1359 "#]],
1388 pub(crate) async unsafe extern "C" fn foo<|>() {} 1360 );
1389 "#, 1361 check(
1390 &[r#"pub(crate) async unsafe extern "C" fn foo()"#], 1362 r#"pub(crate) async unsafe extern "C" fn foo<|>() {}"#,
1363 expect![[r#"
1364 *foo*
1365 ```rust
1366 pub(crate) async unsafe extern "C" fn foo()
1367 ```
1368 "#]],
1391 ); 1369 );
1392 } 1370 }
1393 1371
1394 #[test] 1372 #[test]
1395 fn test_hover_trait_show_qualifiers() { 1373 fn test_hover_trait_show_qualifiers() {
1396 let (_, actions) = check_hover_result( 1374 check_actions(
1397 " 1375 r"unsafe trait foo<|>() {}",
1398 //- /lib.rs 1376 expect![[r#"
1399 unsafe trait foo<|>() {} 1377 [
1400 ", 1378 Implementaion(
1401 &["unsafe trait foo"], 1379 FilePosition {
1380 file_id: FileId(
1381 1,
1382 ),
1383 offset: 13,
1384 },
1385 ),
1386 ]
1387 "#]],
1402 ); 1388 );
1403 assert_impl_action(&actions[0], 13);
1404 } 1389 }
1405 1390
1406 #[test] 1391 #[test]
1407 fn test_hover_mod_with_same_name_as_function() { 1392 fn test_hover_mod_with_same_name_as_function() {
1408 check_hover_result( 1393 check(
1409 " 1394 r#"
1410 //- /lib.rs 1395use self::m<|>y::Bar;
1411 use self::m<|>y::Bar; 1396mod my { pub struct Bar; }
1412
1413 mod my {
1414 pub struct Bar;
1415 }
1416 1397
1417 fn my() {} 1398fn my() {}
1418 ", 1399"#,
1419 &["mod my"], 1400 expect![[r#"
1401 *my*
1402 ```rust
1403 mod my
1404 ```
1405 "#]],
1420 ); 1406 );
1421 } 1407 }
1422 1408
1423 #[test] 1409 #[test]
1424 fn test_hover_struct_doc_comment() { 1410 fn test_hover_struct_doc_comment() {
1425 check_hover_result( 1411 check(
1426 r#" 1412 r#"
1427 //- /lib.rs 1413/// bar docs
1428 /// bar docs 1414struct Bar;
1429 struct Bar;
1430 1415
1431 fn foo() { 1416fn foo() { let bar = Ba<|>r; }
1432 let bar = Ba<|>r; 1417"#,
1433 } 1418 expect![[r#"
1434 "#, 1419 *Bar*
1435 &["struct Bar\n```\n___\n\nbar docs"], 1420 ```rust
1421 struct Bar
1422 ```
1423 ___
1424
1425 bar docs
1426 "#]],
1436 ); 1427 );
1437 } 1428 }
1438 1429
1439 #[test] 1430 #[test]
1440 fn test_hover_struct_doc_attr() { 1431 fn test_hover_struct_doc_attr() {
1441 check_hover_result( 1432 check(
1442 r#" 1433 r#"
1443 //- /lib.rs 1434#[doc = "bar docs"]
1444 #[doc = "bar docs"] 1435struct Bar;
1445 struct Bar;
1446 1436
1447 fn foo() { 1437fn foo() { let bar = Ba<|>r; }
1448 let bar = Ba<|>r; 1438"#,
1449 } 1439 expect![[r#"
1450 "#, 1440 *Bar*
1451 &["struct Bar\n```\n___\n\nbar docs"], 1441 ```rust
1442 struct Bar
1443 ```
1444 ___
1445
1446 bar docs
1447 "#]],
1452 ); 1448 );
1453 } 1449 }
1454 1450
1455 #[test] 1451 #[test]
1456 fn test_hover_struct_doc_attr_multiple_and_mixed() { 1452 fn test_hover_struct_doc_attr_multiple_and_mixed() {
1457 check_hover_result( 1453 check(
1458 r#" 1454 r#"
1459 //- /lib.rs 1455/// bar docs 0
1460 /// bar docs 0 1456#[doc = "bar docs 1"]
1461 #[doc = "bar docs 1"] 1457#[doc = "bar docs 2"]
1462 #[doc = "bar docs 2"] 1458struct Bar;
1463 struct Bar;
1464 1459
1465 fn foo() { 1460fn foo() { let bar = Ba<|>r; }
1466 let bar = Ba<|>r; 1461"#,
1467 } 1462 expect![[r#"
1468 "#, 1463 *Bar*
1469 &["struct Bar\n```\n___\n\nbar docs 0\n\nbar docs 1\n\nbar docs 2"], 1464 ```rust
1465 struct Bar
1466 ```
1467 ___
1468
1469 bar docs 0
1470
1471 bar docs 1
1472
1473 bar docs 2
1474 "#]],
1470 ); 1475 );
1471 } 1476 }
1472 1477
@@ -1623,27 +1628,35 @@ fn func(foo: i32) { if true { <|>foo; }; }
1623 fn test_hover_macro_generated_struct_fn_doc_comment() { 1628 fn test_hover_macro_generated_struct_fn_doc_comment() {
1624 mark::check!(hover_macro_generated_struct_fn_doc_comment); 1629 mark::check!(hover_macro_generated_struct_fn_doc_comment);
1625 1630
1626 check_hover_result( 1631 check(
1627 r#" 1632 r#"
1628 //- /lib.rs 1633macro_rules! bar {
1629 macro_rules! bar { 1634 () => {
1630 () => { 1635 struct Bar;
1631 struct Bar; 1636 impl Bar {
1632 impl Bar { 1637 /// Do the foo
1633 /// Do the foo 1638 fn foo(&self) {}
1634 fn foo(&self) {} 1639 }
1635 } 1640 }
1636 } 1641}
1637 }
1638 1642
1639 bar!(); 1643bar!();
1640 1644
1641 fn foo() { 1645fn foo() { let bar = Bar; bar.fo<|>o(); }
1642 let bar = Bar; 1646"#,
1643 bar.fo<|>o(); 1647 expect![[r#"
1644 } 1648 *foo*
1645 "#, 1649 ```rust
1646 &["test::Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"], 1650 Bar
1651 ```
1652
1653 ```rust
1654 fn foo(&self)
1655 ```
1656 ___
1657
1658 Do the foo
1659 "#]],
1647 ); 1660 );
1648 } 1661 }
1649 1662
@@ -1651,1204 +1664,1155 @@ fn func(foo: i32) { if true { <|>foo; }; }
1651 fn test_hover_macro_generated_struct_fn_doc_attr() { 1664 fn test_hover_macro_generated_struct_fn_doc_attr() {
1652 mark::check!(hover_macro_generated_struct_fn_doc_attr); 1665 mark::check!(hover_macro_generated_struct_fn_doc_attr);
1653 1666
1654 check_hover_result( 1667 check(
1655 r#" 1668 r#"
1656 //- /lib.rs 1669macro_rules! bar {
1657 macro_rules! bar { 1670 () => {
1658 () => { 1671 struct Bar;
1659 struct Bar; 1672 impl Bar {
1660 impl Bar { 1673 #[doc = "Do the foo"]
1661 #[doc = "Do the foo"] 1674 fn foo(&self) {}
1662 fn foo(&self) {} 1675 }
1663 } 1676 }
1664 } 1677}
1665 }
1666 1678
1667 bar!(); 1679bar!();
1668 1680
1669 fn foo() { 1681fn foo() { let bar = Bar; bar.fo<|>o(); }
1670 let bar = Bar; 1682"#,
1671 bar.fo<|>o(); 1683 expect![[r#"
1672 } 1684 *foo*
1673 "#, 1685 ```rust
1674 &["test::Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"], 1686 Bar
1687 ```
1688
1689 ```rust
1690 fn foo(&self)
1691 ```
1692 ___
1693
1694 Do the foo
1695 "#]],
1675 ); 1696 );
1676 } 1697 }
1677 1698
1678 #[test] 1699 #[test]
1679 fn test_hover_trait_has_impl_action() { 1700 fn test_hover_trait_has_impl_action() {
1680 let (_, actions) = check_hover_result( 1701 check_actions(
1681 " 1702 r#"trait foo<|>() {}"#,
1682 //- /lib.rs 1703 expect![[r#"
1683 trait foo<|>() {} 1704 [
1684 ", 1705 Implementaion(
1685 &["trait foo"], 1706 FilePosition {
1707 file_id: FileId(
1708 1,
1709 ),
1710 offset: 6,
1711 },
1712 ),
1713 ]
1714 "#]],
1686 ); 1715 );
1687 assert_impl_action(&actions[0], 6);
1688 } 1716 }
1689 1717
1690 #[test] 1718 #[test]
1691 fn test_hover_struct_has_impl_action() { 1719 fn test_hover_struct_has_impl_action() {
1692 let (_, actions) = check_hover_result( 1720 check_actions(
1693 " 1721 r"struct foo<|>() {}",
1694 //- /lib.rs 1722 expect![[r#"
1695 struct foo<|>() {} 1723 [
1696 ", 1724 Implementaion(
1697 &["struct foo"], 1725 FilePosition {
1726 file_id: FileId(
1727 1,
1728 ),
1729 offset: 7,
1730 },
1731 ),
1732 ]
1733 "#]],
1698 ); 1734 );
1699 assert_impl_action(&actions[0], 7);
1700 } 1735 }
1701 1736
1702 #[test] 1737 #[test]
1703 fn test_hover_union_has_impl_action() { 1738 fn test_hover_union_has_impl_action() {
1704 let (_, actions) = check_hover_result( 1739 check_actions(
1705 " 1740 r#"union foo<|>() {}"#,
1706 //- /lib.rs 1741 expect![[r#"
1707 union foo<|>() {} 1742 [
1708 ", 1743 Implementaion(
1709 &["union foo"], 1744 FilePosition {
1745 file_id: FileId(
1746 1,
1747 ),
1748 offset: 6,
1749 },
1750 ),
1751 ]
1752 "#]],
1710 ); 1753 );
1711 assert_impl_action(&actions[0], 6);
1712 } 1754 }
1713 1755
1714 #[test] 1756 #[test]
1715 fn test_hover_enum_has_impl_action() { 1757 fn test_hover_enum_has_impl_action() {
1716 let (_, actions) = check_hover_result( 1758 check_actions(
1717 " 1759 r"enum foo<|>() { A, B }",
1718 //- /lib.rs 1760 expect![[r#"
1719 enum foo<|>() { 1761 [
1720 A, 1762 Implementaion(
1721 B 1763 FilePosition {
1722 } 1764 file_id: FileId(
1723 ", 1765 1,
1724 &["enum foo"], 1766 ),
1767 offset: 5,
1768 },
1769 ),
1770 ]
1771 "#]],
1725 ); 1772 );
1726 assert_impl_action(&actions[0], 5);
1727 } 1773 }
1728 1774
1729 #[test] 1775 #[test]
1730 fn test_hover_test_has_action() { 1776 fn test_hover_test_has_action() {
1731 let (_, actions) = check_hover_result( 1777 check_actions(
1732 " 1778 r#"
1733 //- /lib.rs 1779#[test]
1734 #[test] 1780fn foo_<|>test() {}
1735 fn foo_<|>test() {} 1781"#,
1736 ", 1782 expect![[r#"
1737 &["fn foo_test()"], 1783 [
1738 ); 1784 Runnable(
1739 assert_debug_snapshot!(actions, 1785 Runnable {
1740 @r###" 1786 nav: NavigationTarget {
1741 [ 1787 file_id: FileId(
1742 Runnable( 1788 1,
1743 Runnable { 1789 ),
1744 nav: NavigationTarget { 1790 full_range: 0..24,
1745 file_id: FileId( 1791 focus_range: Some(
1746 1, 1792 11..19,
1747 ), 1793 ),
1748 full_range: 0..24, 1794 name: "foo_test",
1749 name: "foo_test", 1795 kind: FN,
1750 kind: FN_DEF, 1796 container_name: None,
1751 focus_range: Some( 1797 description: None,
1752 11..19, 1798 docs: None,
1753 ), 1799 },
1754 container_name: None, 1800 kind: Test {
1755 description: None, 1801 test_id: Path(
1756 docs: None, 1802 "foo_test",
1757 }, 1803 ),
1758 kind: Test { 1804 attr: TestAttr {
1759 test_id: Path( 1805 ignore: false,
1760 "foo_test", 1806 },
1761 ),
1762 attr: TestAttr {
1763 ignore: false,
1764 }, 1807 },
1808 cfg_exprs: [],
1765 }, 1809 },
1766 cfg_exprs: [], 1810 ),
1767 }, 1811 ]
1768 ), 1812 "#]],
1769 ] 1813 );
1770 "###);
1771 } 1814 }
1772 1815
1773 #[test] 1816 #[test]
1774 fn test_hover_test_mod_has_action() { 1817 fn test_hover_test_mod_has_action() {
1775 let (_, actions) = check_hover_result( 1818 check_actions(
1776 " 1819 r#"
1777 //- /lib.rs 1820mod tests<|> {
1778 mod tests<|> { 1821 #[test]
1779 #[test] 1822 fn foo_test() {}
1780 fn foo_test() {} 1823}
1781 } 1824"#,
1782 ", 1825 expect![[r#"
1783 &["mod tests"], 1826 [
1784 ); 1827 Runnable(
1785 assert_debug_snapshot!(actions, 1828 Runnable {
1786 @r###" 1829 nav: NavigationTarget {
1787 [ 1830 file_id: FileId(
1788 Runnable( 1831 1,
1789 Runnable { 1832 ),
1790 nav: NavigationTarget { 1833 full_range: 0..46,
1791 file_id: FileId( 1834 focus_range: Some(
1792 1, 1835 4..9,
1793 ), 1836 ),
1794 full_range: 0..46, 1837 name: "tests",
1795 name: "tests", 1838 kind: MODULE,
1796 kind: MODULE, 1839 container_name: None,
1797 focus_range: Some( 1840 description: None,
1798 4..9, 1841 docs: None,
1799 ), 1842 },
1800 container_name: None, 1843 kind: TestMod {
1801 description: None, 1844 path: "tests",
1802 docs: None, 1845 },
1803 }, 1846 cfg_exprs: [],
1804 kind: TestMod {
1805 path: "tests",
1806 }, 1847 },
1807 cfg_exprs: [], 1848 ),
1808 }, 1849 ]
1809 ), 1850 "#]],
1810 ] 1851 );
1811 "###);
1812 } 1852 }
1813 1853
1814 #[test] 1854 #[test]
1815 fn test_hover_struct_has_goto_type_action() { 1855 fn test_hover_struct_has_goto_type_action() {
1816 let (_, actions) = check_hover_result( 1856 check_actions(
1817 " 1857 r#"
1818 //- /main.rs 1858struct S{ f1: u32 }
1819 struct S{ f1: u32 }
1820 1859
1821 fn main() { 1860fn main() { let s<|>t = S{ f1:0 }; }
1822 let s<|>t = S{ f1:0 }; 1861 "#,
1823 } 1862 expect![[r#"
1824 ",
1825 &["S"],
1826 );
1827 assert_debug_snapshot!(actions,
1828 @r###"
1829 [
1830 GoToType(
1831 [ 1863 [
1832 HoverGotoTypeData { 1864 GoToType(
1833 mod_path: "test::S", 1865 [
1834 nav: NavigationTarget { 1866 HoverGotoTypeData {
1835 file_id: FileId( 1867 mod_path: "S",
1836 1, 1868 nav: NavigationTarget {
1837 ), 1869 file_id: FileId(
1838 full_range: 0..19, 1870 1,
1839 name: "S", 1871 ),
1840 kind: STRUCT_DEF, 1872 full_range: 0..19,
1841 focus_range: Some( 1873 focus_range: Some(
1842 7..8, 1874 7..8,
1843 ), 1875 ),
1844 container_name: None, 1876 name: "S",
1845 description: Some( 1877 kind: STRUCT,
1846 "struct S", 1878 container_name: None,
1847 ), 1879 description: Some(
1848 docs: None, 1880 "struct S",
1849 }, 1881 ),
1850 }, 1882 docs: None,
1851 ], 1883 },
1852 ), 1884 },
1853 ] 1885 ],
1854 "###); 1886 ),
1887 ]
1888 "#]],
1889 );
1855 } 1890 }
1856 1891
1857 #[test] 1892 #[test]
1858 fn test_hover_generic_struct_has_goto_type_actions() { 1893 fn test_hover_generic_struct_has_goto_type_actions() {
1859 let (_, actions) = check_hover_result( 1894 check_actions(
1860 " 1895 r#"
1861 //- /main.rs 1896struct Arg(u32);
1862 struct Arg(u32); 1897struct S<T>{ f1: T }
1863 struct S<T>{ f1: T }
1864 1898
1865 fn main() { 1899fn main() { let s<|>t = S{ f1:Arg(0) }; }
1866 let s<|>t = S{ f1:Arg(0) }; 1900"#,
1867 } 1901 expect![[r#"
1868 ",
1869 &["S<Arg>"],
1870 );
1871 assert_debug_snapshot!(actions,
1872 @r###"
1873 [
1874 GoToType(
1875 [ 1902 [
1876 HoverGotoTypeData { 1903 GoToType(
1877 mod_path: "test::S", 1904 [
1878 nav: NavigationTarget { 1905 HoverGotoTypeData {
1879 file_id: FileId( 1906 mod_path: "S",
1880 1, 1907 nav: NavigationTarget {
1881 ), 1908 file_id: FileId(
1882 full_range: 17..37, 1909 1,
1883 name: "S", 1910 ),
1884 kind: STRUCT_DEF, 1911 full_range: 17..37,
1885 focus_range: Some( 1912 focus_range: Some(
1886 24..25, 1913 24..25,
1887 ), 1914 ),
1888 container_name: None, 1915 name: "S",
1889 description: Some( 1916 kind: STRUCT,
1890 "struct S", 1917 container_name: None,
1891 ), 1918 description: Some(
1892 docs: None, 1919 "struct S",
1893 }, 1920 ),
1894 }, 1921 docs: None,
1895 HoverGotoTypeData { 1922 },
1896 mod_path: "test::Arg", 1923 },
1897 nav: NavigationTarget { 1924 HoverGotoTypeData {
1898 file_id: FileId( 1925 mod_path: "Arg",
1899 1, 1926 nav: NavigationTarget {
1900 ), 1927 file_id: FileId(
1901 full_range: 0..16, 1928 1,
1902 name: "Arg", 1929 ),
1903 kind: STRUCT_DEF, 1930 full_range: 0..16,
1904 focus_range: Some( 1931 focus_range: Some(
1905 7..10, 1932 7..10,
1906 ), 1933 ),
1907 container_name: None, 1934 name: "Arg",
1908 description: Some( 1935 kind: STRUCT,
1909 "struct Arg", 1936 container_name: None,
1910 ), 1937 description: Some(
1911 docs: None, 1938 "struct Arg",
1912 }, 1939 ),
1913 }, 1940 docs: None,
1914 ], 1941 },
1915 ), 1942 },
1916 ] 1943 ],
1917 "###); 1944 ),
1945 ]
1946 "#]],
1947 );
1918 } 1948 }
1919 1949
1920 #[test] 1950 #[test]
1921 fn test_hover_generic_struct_has_flattened_goto_type_actions() { 1951 fn test_hover_generic_struct_has_flattened_goto_type_actions() {
1922 let (_, actions) = check_hover_result( 1952 check_actions(
1923 " 1953 r#"
1924 //- /main.rs 1954struct Arg(u32);
1925 struct Arg(u32); 1955struct S<T>{ f1: T }
1926 struct S<T>{ f1: T }
1927 1956
1928 fn main() { 1957fn main() { let s<|>t = S{ f1: S{ f1: Arg(0) } }; }
1929 let s<|>t = S{ f1: S{ f1: Arg(0) } }; 1958 "#,
1930 } 1959 expect![[r#"
1931 ",
1932 &["S<S<Arg>>"],
1933 );
1934 assert_debug_snapshot!(actions,
1935 @r###"
1936 [
1937 GoToType(
1938 [ 1960 [
1939 HoverGotoTypeData { 1961 GoToType(
1940 mod_path: "test::S", 1962 [
1941 nav: NavigationTarget { 1963 HoverGotoTypeData {
1942 file_id: FileId( 1964 mod_path: "S",
1943 1, 1965 nav: NavigationTarget {
1944 ), 1966 file_id: FileId(
1945 full_range: 17..37, 1967 1,
1946 name: "S", 1968 ),
1947 kind: STRUCT_DEF, 1969 full_range: 17..37,
1948 focus_range: Some( 1970 focus_range: Some(
1949 24..25, 1971 24..25,
1950 ), 1972 ),
1951 container_name: None, 1973 name: "S",
1952 description: Some( 1974 kind: STRUCT,
1953 "struct S", 1975 container_name: None,
1954 ), 1976 description: Some(
1955 docs: None, 1977 "struct S",
1956 }, 1978 ),
1957 }, 1979 docs: None,
1958 HoverGotoTypeData { 1980 },
1959 mod_path: "test::Arg", 1981 },
1960 nav: NavigationTarget { 1982 HoverGotoTypeData {
1961 file_id: FileId( 1983 mod_path: "Arg",
1962 1, 1984 nav: NavigationTarget {
1963 ), 1985 file_id: FileId(
1964 full_range: 0..16, 1986 1,
1965 name: "Arg", 1987 ),
1966 kind: STRUCT_DEF, 1988 full_range: 0..16,
1967 focus_range: Some( 1989 focus_range: Some(
1968 7..10, 1990 7..10,
1969 ), 1991 ),
1970 container_name: None, 1992 name: "Arg",
1971 description: Some( 1993 kind: STRUCT,
1972 "struct Arg", 1994 container_name: None,
1973 ), 1995 description: Some(
1974 docs: None, 1996 "struct Arg",
1975 }, 1997 ),
1976 }, 1998 docs: None,
1977 ], 1999 },
1978 ), 2000 },
1979 ] 2001 ],
1980 "###); 2002 ),
2003 ]
2004 "#]],
2005 );
1981 } 2006 }
1982 2007
1983 #[test] 2008 #[test]
1984 fn test_hover_tuple_has_goto_type_actions() { 2009 fn test_hover_tuple_has_goto_type_actions() {
1985 let (_, actions) = check_hover_result( 2010 check_actions(
1986 " 2011 r#"
1987 //- /main.rs 2012struct A(u32);
1988 struct A(u32); 2013struct B(u32);
1989 struct B(u32); 2014mod M {
1990 mod M { 2015 pub struct C(u32);
1991 pub struct C(u32); 2016}
1992 }
1993 2017
1994 fn main() { 2018fn main() { let s<|>t = (A(1), B(2), M::C(3) ); }
1995 let s<|>t = (A(1), B(2), M::C(3) ); 2019"#,
1996 } 2020 expect![[r#"
1997 ",
1998 &["(A, B, C)"],
1999 );
2000 assert_debug_snapshot!(actions,
2001 @r###"
2002 [
2003 GoToType(
2004 [ 2021 [
2005 HoverGotoTypeData { 2022 GoToType(
2006 mod_path: "test::A", 2023 [
2007 nav: NavigationTarget { 2024 HoverGotoTypeData {
2008 file_id: FileId( 2025 mod_path: "A",
2009 1, 2026 nav: NavigationTarget {
2010 ), 2027 file_id: FileId(
2011 full_range: 0..14, 2028 1,
2012 name: "A", 2029 ),
2013 kind: STRUCT_DEF, 2030 full_range: 0..14,
2014 focus_range: Some( 2031 focus_range: Some(
2015 7..8, 2032 7..8,
2016 ), 2033 ),
2017 container_name: None, 2034 name: "A",
2018 description: Some( 2035 kind: STRUCT,
2019 "struct A", 2036 container_name: None,
2020 ), 2037 description: Some(
2021 docs: None, 2038 "struct A",
2022 }, 2039 ),
2023 }, 2040 docs: None,
2024 HoverGotoTypeData { 2041 },
2025 mod_path: "test::B", 2042 },
2026 nav: NavigationTarget { 2043 HoverGotoTypeData {
2027 file_id: FileId( 2044 mod_path: "B",
2028 1, 2045 nav: NavigationTarget {
2029 ), 2046 file_id: FileId(
2030 full_range: 15..29, 2047 1,
2031 name: "B", 2048 ),
2032 kind: STRUCT_DEF, 2049 full_range: 15..29,
2033 focus_range: Some( 2050 focus_range: Some(
2034 22..23, 2051 22..23,
2035 ), 2052 ),
2036 container_name: None, 2053 name: "B",
2037 description: Some( 2054 kind: STRUCT,
2038 "struct B", 2055 container_name: None,
2039 ), 2056 description: Some(
2040 docs: None, 2057 "struct B",
2041 }, 2058 ),
2042 }, 2059 docs: None,
2043 HoverGotoTypeData { 2060 },
2044 mod_path: "test::M::C", 2061 },
2045 nav: NavigationTarget { 2062 HoverGotoTypeData {
2046 file_id: FileId( 2063 mod_path: "M::C",
2047 1, 2064 nav: NavigationTarget {
2048 ), 2065 file_id: FileId(
2049 full_range: 42..60, 2066 1,
2050 name: "C", 2067 ),
2051 kind: STRUCT_DEF, 2068 full_range: 42..60,
2052 focus_range: Some( 2069 focus_range: Some(
2053 53..54, 2070 53..54,
2054 ), 2071 ),
2055 container_name: None, 2072 name: "C",
2056 description: Some( 2073 kind: STRUCT,
2057 "pub struct C", 2074 container_name: None,
2058 ), 2075 description: Some(
2059 docs: None, 2076 "pub struct C",
2060 }, 2077 ),
2061 }, 2078 docs: None,
2062 ], 2079 },
2063 ), 2080 },
2064 ] 2081 ],
2065 "###); 2082 ),
2083 ]
2084 "#]],
2085 );
2066 } 2086 }
2067 2087
2068 #[test] 2088 #[test]
2069 fn test_hover_return_impl_trait_has_goto_type_action() { 2089 fn test_hover_return_impl_trait_has_goto_type_action() {
2070 let (_, actions) = check_hover_result( 2090 check_actions(
2071 " 2091 r#"
2072 //- /main.rs 2092trait Foo {}
2073 trait Foo {} 2093fn foo() -> impl Foo {}
2074
2075 fn foo() -> impl Foo {}
2076 2094
2077 fn main() { 2095fn main() { let s<|>t = foo(); }
2078 let s<|>t = foo(); 2096"#,
2079 } 2097 expect![[r#"
2080 ",
2081 &["impl Foo"],
2082 );
2083 assert_debug_snapshot!(actions,
2084 @r###"
2085 [
2086 GoToType(
2087 [ 2098 [
2088 HoverGotoTypeData { 2099 GoToType(
2089 mod_path: "test::Foo", 2100 [
2090 nav: NavigationTarget { 2101 HoverGotoTypeData {
2091 file_id: FileId( 2102 mod_path: "Foo",
2092 1, 2103 nav: NavigationTarget {
2093 ), 2104 file_id: FileId(
2094 full_range: 0..12, 2105 1,
2095 name: "Foo", 2106 ),
2096 kind: TRAIT_DEF, 2107 full_range: 0..12,
2097 focus_range: Some( 2108 focus_range: Some(
2098 6..9, 2109 6..9,
2099 ), 2110 ),
2100 container_name: None, 2111 name: "Foo",
2101 description: Some( 2112 kind: TRAIT,
2102 "trait Foo", 2113 container_name: None,
2103 ), 2114 description: Some(
2104 docs: None, 2115 "trait Foo",
2105 }, 2116 ),
2106 }, 2117 docs: None,
2107 ], 2118 },
2108 ), 2119 },
2109 ] 2120 ],
2110 "###); 2121 ),
2122 ]
2123 "#]],
2124 );
2111 } 2125 }
2112 2126
2113 #[test] 2127 #[test]
2114 fn test_hover_generic_return_impl_trait_has_goto_type_action() { 2128 fn test_hover_generic_return_impl_trait_has_goto_type_action() {
2115 let (_, actions) = check_hover_result( 2129 check_actions(
2116 " 2130 r#"
2117 //- /main.rs 2131trait Foo<T> {}
2118 trait Foo<T> {} 2132struct S;
2119 struct S; 2133fn foo() -> impl Foo<S> {}
2120
2121 fn foo() -> impl Foo<S> {}
2122 2134
2123 fn main() { 2135fn main() { let s<|>t = foo(); }
2124 let s<|>t = foo(); 2136"#,
2125 } 2137 expect![[r#"
2126 ",
2127 &["impl Foo<S>"],
2128 );
2129 assert_debug_snapshot!(actions,
2130 @r###"
2131 [
2132 GoToType(
2133 [ 2138 [
2134 HoverGotoTypeData { 2139 GoToType(
2135 mod_path: "test::Foo", 2140 [
2136 nav: NavigationTarget { 2141 HoverGotoTypeData {
2137 file_id: FileId( 2142 mod_path: "Foo",
2138 1, 2143 nav: NavigationTarget {
2139 ), 2144 file_id: FileId(
2140 full_range: 0..15, 2145 1,
2141 name: "Foo", 2146 ),
2142 kind: TRAIT_DEF, 2147 full_range: 0..15,
2143 focus_range: Some( 2148 focus_range: Some(
2144 6..9, 2149 6..9,
2145 ), 2150 ),
2146 container_name: None, 2151 name: "Foo",
2147 description: Some( 2152 kind: TRAIT,
2148 "trait Foo", 2153 container_name: None,
2149 ), 2154 description: Some(
2150 docs: None, 2155 "trait Foo",
2151 }, 2156 ),
2152 }, 2157 docs: None,
2153 HoverGotoTypeData { 2158 },
2154 mod_path: "test::S", 2159 },
2155 nav: NavigationTarget { 2160 HoverGotoTypeData {
2156 file_id: FileId( 2161 mod_path: "S",
2157 1, 2162 nav: NavigationTarget {
2158 ), 2163 file_id: FileId(
2159 full_range: 16..25, 2164 1,
2160 name: "S", 2165 ),
2161 kind: STRUCT_DEF, 2166 full_range: 16..25,
2162 focus_range: Some( 2167 focus_range: Some(
2163 23..24, 2168 23..24,
2164 ), 2169 ),
2165 container_name: None, 2170 name: "S",
2166 description: Some( 2171 kind: STRUCT,
2167 "struct S", 2172 container_name: None,
2168 ), 2173 description: Some(
2169 docs: None, 2174 "struct S",
2170 }, 2175 ),
2171 }, 2176 docs: None,
2172 ], 2177 },
2173 ), 2178 },
2174 ] 2179 ],
2175 "###); 2180 ),
2181 ]
2182 "#]],
2183 );
2176 } 2184 }
2177 2185
2178 #[test] 2186 #[test]
2179 fn test_hover_return_impl_traits_has_goto_type_action() { 2187 fn test_hover_return_impl_traits_has_goto_type_action() {
2180 let (_, actions) = check_hover_result( 2188 check_actions(
2181 " 2189 r#"
2182 //- /main.rs 2190trait Foo {}
2183 trait Foo {} 2191trait Bar {}
2184 trait Bar {} 2192fn foo() -> impl Foo + Bar {}
2185
2186 fn foo() -> impl Foo + Bar {}
2187 2193
2188 fn main() { 2194fn main() { let s<|>t = foo(); }
2189 let s<|>t = foo(); 2195 "#,
2190 } 2196 expect![[r#"
2191 ",
2192 &["impl Foo + Bar"],
2193 );
2194 assert_debug_snapshot!(actions,
2195 @r###"
2196 [
2197 GoToType(
2198 [ 2197 [
2199 HoverGotoTypeData { 2198 GoToType(
2200 mod_path: "test::Foo", 2199 [
2201 nav: NavigationTarget { 2200 HoverGotoTypeData {
2202 file_id: FileId( 2201 mod_path: "Foo",
2203 1, 2202 nav: NavigationTarget {
2204 ), 2203 file_id: FileId(
2205 full_range: 0..12, 2204 1,
2206 name: "Foo", 2205 ),
2207 kind: TRAIT_DEF, 2206 full_range: 0..12,
2208 focus_range: Some( 2207 focus_range: Some(
2209 6..9, 2208 6..9,
2210 ), 2209 ),
2211 container_name: None, 2210 name: "Foo",
2212 description: Some( 2211 kind: TRAIT,
2213 "trait Foo", 2212 container_name: None,
2214 ), 2213 description: Some(
2215 docs: None, 2214 "trait Foo",
2216 }, 2215 ),
2217 }, 2216 docs: None,
2218 HoverGotoTypeData { 2217 },
2219 mod_path: "test::Bar", 2218 },
2220 nav: NavigationTarget { 2219 HoverGotoTypeData {
2221 file_id: FileId( 2220 mod_path: "Bar",
2222 1, 2221 nav: NavigationTarget {
2223 ), 2222 file_id: FileId(
2224 full_range: 13..25, 2223 1,
2225 name: "Bar", 2224 ),
2226 kind: TRAIT_DEF, 2225 full_range: 13..25,
2227 focus_range: Some( 2226 focus_range: Some(
2228 19..22, 2227 19..22,
2229 ), 2228 ),
2230 container_name: None, 2229 name: "Bar",
2231 description: Some( 2230 kind: TRAIT,
2232 "trait Bar", 2231 container_name: None,
2233 ), 2232 description: Some(
2234 docs: None, 2233 "trait Bar",
2235 }, 2234 ),
2236 }, 2235 docs: None,
2237 ], 2236 },
2238 ), 2237 },
2239 ] 2238 ],
2240 "###); 2239 ),
2240 ]
2241 "#]],
2242 );
2241 } 2243 }
2242 2244
2243 #[test] 2245 #[test]
2244 fn test_hover_generic_return_impl_traits_has_goto_type_action() { 2246 fn test_hover_generic_return_impl_traits_has_goto_type_action() {
2245 let (_, actions) = check_hover_result( 2247 check_actions(
2246 " 2248 r#"
2247 //- /main.rs 2249trait Foo<T> {}
2248 trait Foo<T> {} 2250trait Bar<T> {}
2249 trait Bar<T> {} 2251struct S1 {}
2250 struct S1 {} 2252struct S2 {}
2251 struct S2 {}
2252 2253
2253 fn foo() -> impl Foo<S1> + Bar<S2> {} 2254fn foo() -> impl Foo<S1> + Bar<S2> {}
2254 2255
2255 fn main() { 2256fn main() { let s<|>t = foo(); }
2256 let s<|>t = foo(); 2257"#,
2257 } 2258 expect![[r#"
2258 ",
2259 &["impl Foo<S1> + Bar<S2>"],
2260 );
2261 assert_debug_snapshot!(actions,
2262 @r###"
2263 [
2264 GoToType(
2265 [ 2259 [
2266 HoverGotoTypeData { 2260 GoToType(
2267 mod_path: "test::Foo", 2261 [
2268 nav: NavigationTarget { 2262 HoverGotoTypeData {
2269 file_id: FileId( 2263 mod_path: "Foo",
2270 1, 2264 nav: NavigationTarget {
2271 ), 2265 file_id: FileId(
2272 full_range: 0..15, 2266 1,
2273 name: "Foo", 2267 ),
2274 kind: TRAIT_DEF, 2268 full_range: 0..15,
2275 focus_range: Some( 2269 focus_range: Some(
2276 6..9, 2270 6..9,
2277 ), 2271 ),
2278 container_name: None, 2272 name: "Foo",
2279 description: Some( 2273 kind: TRAIT,
2280 "trait Foo", 2274 container_name: None,
2281 ), 2275 description: Some(
2282 docs: None, 2276 "trait Foo",
2283 }, 2277 ),
2284 }, 2278 docs: None,
2285 HoverGotoTypeData { 2279 },
2286 mod_path: "test::Bar", 2280 },
2287 nav: NavigationTarget { 2281 HoverGotoTypeData {
2288 file_id: FileId( 2282 mod_path: "Bar",
2289 1, 2283 nav: NavigationTarget {
2290 ), 2284 file_id: FileId(
2291 full_range: 16..31, 2285 1,
2292 name: "Bar", 2286 ),
2293 kind: TRAIT_DEF, 2287 full_range: 16..31,
2294 focus_range: Some( 2288 focus_range: Some(
2295 22..25, 2289 22..25,
2296 ), 2290 ),
2297 container_name: None, 2291 name: "Bar",
2298 description: Some( 2292 kind: TRAIT,
2299 "trait Bar", 2293 container_name: None,
2300 ), 2294 description: Some(
2301 docs: None, 2295 "trait Bar",
2302 }, 2296 ),
2303 }, 2297 docs: None,
2304 HoverGotoTypeData { 2298 },
2305 mod_path: "test::S1", 2299 },
2306 nav: NavigationTarget { 2300 HoverGotoTypeData {
2307 file_id: FileId( 2301 mod_path: "S1",
2308 1, 2302 nav: NavigationTarget {
2309 ), 2303 file_id: FileId(
2310 full_range: 32..44, 2304 1,
2311 name: "S1", 2305 ),
2312 kind: STRUCT_DEF, 2306 full_range: 32..44,
2313 focus_range: Some( 2307 focus_range: Some(
2314 39..41, 2308 39..41,
2315 ), 2309 ),
2316 container_name: None, 2310 name: "S1",
2317 description: Some( 2311 kind: STRUCT,
2318 "struct S1", 2312 container_name: None,
2319 ), 2313 description: Some(
2320 docs: None, 2314 "struct S1",
2321 }, 2315 ),
2322 }, 2316 docs: None,
2323 HoverGotoTypeData { 2317 },
2324 mod_path: "test::S2", 2318 },
2325 nav: NavigationTarget { 2319 HoverGotoTypeData {
2326 file_id: FileId( 2320 mod_path: "S2",
2327 1, 2321 nav: NavigationTarget {
2328 ), 2322 file_id: FileId(
2329 full_range: 45..57, 2323 1,
2330 name: "S2", 2324 ),
2331 kind: STRUCT_DEF, 2325 full_range: 45..57,
2332 focus_range: Some( 2326 focus_range: Some(
2333 52..54, 2327 52..54,
2334 ), 2328 ),
2335 container_name: None, 2329 name: "S2",
2336 description: Some( 2330 kind: STRUCT,
2337 "struct S2", 2331 container_name: None,
2338 ), 2332 description: Some(
2339 docs: None, 2333 "struct S2",
2340 }, 2334 ),
2341 }, 2335 docs: None,
2342 ], 2336 },
2343 ), 2337 },
2344 ] 2338 ],
2345 "###); 2339 ),
2340 ]
2341 "#]],
2342 );
2346 } 2343 }
2347 2344
2348 #[test] 2345 #[test]
2349 fn test_hover_arg_impl_trait_has_goto_type_action() { 2346 fn test_hover_arg_impl_trait_has_goto_type_action() {
2350 let (_, actions) = check_hover_result( 2347 check_actions(
2351 " 2348 r#"
2352 //- /lib.rs 2349trait Foo {}
2353 trait Foo {} 2350fn foo(ar<|>g: &impl Foo) {}
2354 fn foo(ar<|>g: &impl Foo) {} 2351"#,
2355 ", 2352 expect![[r#"
2356 &["&impl Foo"],
2357 );
2358 assert_debug_snapshot!(actions,
2359 @r###"
2360 [
2361 GoToType(
2362 [ 2353 [
2363 HoverGotoTypeData { 2354 GoToType(
2364 mod_path: "test::Foo", 2355 [
2365 nav: NavigationTarget { 2356 HoverGotoTypeData {
2366 file_id: FileId( 2357 mod_path: "Foo",
2367 1, 2358 nav: NavigationTarget {
2368 ), 2359 file_id: FileId(
2369 full_range: 0..12, 2360 1,
2370 name: "Foo", 2361 ),
2371 kind: TRAIT_DEF, 2362 full_range: 0..12,
2372 focus_range: Some( 2363 focus_range: Some(
2373 6..9, 2364 6..9,
2374 ), 2365 ),
2375 container_name: None, 2366 name: "Foo",
2376 description: Some( 2367 kind: TRAIT,
2377 "trait Foo", 2368 container_name: None,
2378 ), 2369 description: Some(
2379 docs: None, 2370 "trait Foo",
2380 }, 2371 ),
2381 }, 2372 docs: None,
2382 ], 2373 },
2383 ), 2374 },
2384 ] 2375 ],
2385 "###); 2376 ),
2377 ]
2378 "#]],
2379 );
2386 } 2380 }
2387 2381
2388 #[test] 2382 #[test]
2389 fn test_hover_arg_impl_traits_has_goto_type_action() { 2383 fn test_hover_arg_impl_traits_has_goto_type_action() {
2390 let (_, actions) = check_hover_result( 2384 check_actions(
2391 " 2385 r#"
2392 //- /lib.rs 2386trait Foo {}
2393 trait Foo {} 2387trait Bar<T> {}
2394 trait Bar<T> {} 2388struct S{}
2395 struct S{}
2396 2389
2397 fn foo(ar<|>g: &impl Foo + Bar<S>) {} 2390fn foo(ar<|>g: &impl Foo + Bar<S>) {}
2398 ", 2391"#,
2399 &["&impl Foo + Bar<S>"], 2392 expect![[r#"
2400 );
2401 assert_debug_snapshot!(actions,
2402 @r###"
2403 [
2404 GoToType(
2405 [ 2393 [
2406 HoverGotoTypeData { 2394 GoToType(
2407 mod_path: "test::Foo", 2395 [
2408 nav: NavigationTarget { 2396 HoverGotoTypeData {
2409 file_id: FileId( 2397 mod_path: "Foo",
2410 1, 2398 nav: NavigationTarget {
2411 ), 2399 file_id: FileId(
2412 full_range: 0..12, 2400 1,
2413 name: "Foo", 2401 ),
2414 kind: TRAIT_DEF, 2402 full_range: 0..12,
2415 focus_range: Some( 2403 focus_range: Some(
2416 6..9, 2404 6..9,
2417 ), 2405 ),
2418 container_name: None, 2406 name: "Foo",
2419 description: Some( 2407 kind: TRAIT,
2420 "trait Foo", 2408 container_name: None,
2421 ), 2409 description: Some(
2422 docs: None, 2410 "trait Foo",
2423 }, 2411 ),
2424 }, 2412 docs: None,
2425 HoverGotoTypeData { 2413 },
2426 mod_path: "test::Bar", 2414 },
2427 nav: NavigationTarget { 2415 HoverGotoTypeData {
2428 file_id: FileId( 2416 mod_path: "Bar",
2429 1, 2417 nav: NavigationTarget {
2430 ), 2418 file_id: FileId(
2431 full_range: 13..28, 2419 1,
2432 name: "Bar", 2420 ),
2433 kind: TRAIT_DEF, 2421 full_range: 13..28,
2434 focus_range: Some( 2422 focus_range: Some(
2435 19..22, 2423 19..22,
2436 ), 2424 ),
2437 container_name: None, 2425 name: "Bar",
2438 description: Some( 2426 kind: TRAIT,
2439 "trait Bar", 2427 container_name: None,
2440 ), 2428 description: Some(
2441 docs: None, 2429 "trait Bar",
2442 }, 2430 ),
2443 }, 2431 docs: None,
2444 HoverGotoTypeData { 2432 },
2445 mod_path: "test::S", 2433 },
2446 nav: NavigationTarget { 2434 HoverGotoTypeData {
2447 file_id: FileId( 2435 mod_path: "S",
2448 1, 2436 nav: NavigationTarget {
2449 ), 2437 file_id: FileId(
2450 full_range: 29..39, 2438 1,
2451 name: "S", 2439 ),
2452 kind: STRUCT_DEF, 2440 full_range: 29..39,
2453 focus_range: Some( 2441 focus_range: Some(
2454 36..37, 2442 36..37,
2455 ), 2443 ),
2456 container_name: None, 2444 name: "S",
2457 description: Some( 2445 kind: STRUCT,
2458 "struct S", 2446 container_name: None,
2459 ), 2447 description: Some(
2460 docs: None, 2448 "struct S",
2461 }, 2449 ),
2462 }, 2450 docs: None,
2463 ], 2451 },
2464 ), 2452 },
2465 ] 2453 ],
2466 "###); 2454 ),
2455 ]
2456 "#]],
2457 );
2467 } 2458 }
2468 2459
2469 #[test] 2460 #[test]
2470 fn test_hover_arg_generic_impl_trait_has_goto_type_action() { 2461 fn test_hover_arg_generic_impl_trait_has_goto_type_action() {
2471 let (_, actions) = check_hover_result( 2462 check_actions(
2472 " 2463 r#"
2473 //- /lib.rs 2464trait Foo<T> {}
2474 trait Foo<T> {} 2465struct S {}
2475 struct S {} 2466fn foo(ar<|>g: &impl Foo<S>) {}
2476 fn foo(ar<|>g: &impl Foo<S>) {} 2467"#,
2477 ", 2468 expect![[r#"
2478 &["&impl Foo<S>"],
2479 );
2480 assert_debug_snapshot!(actions,
2481 @r###"
2482 [
2483 GoToType(
2484 [ 2469 [
2485 HoverGotoTypeData { 2470 GoToType(
2486 mod_path: "test::Foo", 2471 [
2487 nav: NavigationTarget { 2472 HoverGotoTypeData {
2488 file_id: FileId( 2473 mod_path: "Foo",
2489 1, 2474 nav: NavigationTarget {
2490 ), 2475 file_id: FileId(
2491 full_range: 0..15, 2476 1,
2492 name: "Foo", 2477 ),
2493 kind: TRAIT_DEF, 2478 full_range: 0..15,
2494 focus_range: Some( 2479 focus_range: Some(
2495 6..9, 2480 6..9,
2496 ), 2481 ),
2497 container_name: None, 2482 name: "Foo",
2498 description: Some( 2483 kind: TRAIT,
2499 "trait Foo", 2484 container_name: None,
2500 ), 2485 description: Some(
2501 docs: None, 2486 "trait Foo",
2502 }, 2487 ),
2503 }, 2488 docs: None,
2504 HoverGotoTypeData { 2489 },
2505 mod_path: "test::S", 2490 },
2506 nav: NavigationTarget { 2491 HoverGotoTypeData {
2507 file_id: FileId( 2492 mod_path: "S",
2508 1, 2493 nav: NavigationTarget {
2509 ), 2494 file_id: FileId(
2510 full_range: 16..27, 2495 1,
2511 name: "S", 2496 ),
2512 kind: STRUCT_DEF, 2497 full_range: 16..27,
2513 focus_range: Some( 2498 focus_range: Some(
2514 23..24, 2499 23..24,
2515 ), 2500 ),
2516 container_name: None, 2501 name: "S",
2517 description: Some( 2502 kind: STRUCT,
2518 "struct S", 2503 container_name: None,
2519 ), 2504 description: Some(
2520 docs: None, 2505 "struct S",
2521 }, 2506 ),
2522 }, 2507 docs: None,
2523 ], 2508 },
2524 ), 2509 },
2525 ] 2510 ],
2526 "###); 2511 ),
2512 ]
2513 "#]],
2514 );
2527 } 2515 }
2528 2516
2529 #[test] 2517 #[test]
2530 fn test_hover_dyn_return_has_goto_type_action() { 2518 fn test_hover_dyn_return_has_goto_type_action() {
2531 let (_, actions) = check_hover_result( 2519 check_actions(
2532 " 2520 r#"
2533 //- /main.rs 2521trait Foo {}
2534 trait Foo {} 2522struct S;
2535 struct S; 2523impl Foo for S {}
2536 impl Foo for S {}
2537
2538 struct B<T>{}
2539 2524
2540 fn foo() -> B<dyn Foo> {} 2525struct B<T>{}
2526fn foo() -> B<dyn Foo> {}
2541 2527
2542 fn main() { 2528fn main() { let s<|>t = foo(); }
2543 let s<|>t = foo(); 2529"#,
2544 } 2530 expect![[r#"
2545 ",
2546 &["B<dyn Foo>"],
2547 );
2548 assert_debug_snapshot!(actions,
2549 @r###"
2550 [
2551 GoToType(
2552 [ 2531 [
2553 HoverGotoTypeData { 2532 GoToType(
2554 mod_path: "test::B", 2533 [
2555 nav: NavigationTarget { 2534 HoverGotoTypeData {
2556 file_id: FileId( 2535 mod_path: "B",
2557 1, 2536 nav: NavigationTarget {
2558 ), 2537 file_id: FileId(
2559 full_range: 42..55, 2538 1,
2560 name: "B", 2539 ),
2561 kind: STRUCT_DEF, 2540 full_range: 42..55,
2562 focus_range: Some( 2541 focus_range: Some(
2563 49..50, 2542 49..50,
2564 ), 2543 ),
2565 container_name: None, 2544 name: "B",
2566 description: Some( 2545 kind: STRUCT,
2567 "struct B", 2546 container_name: None,
2568 ), 2547 description: Some(
2569 docs: None, 2548 "struct B",
2570 }, 2549 ),
2571 }, 2550 docs: None,
2572 HoverGotoTypeData { 2551 },
2573 mod_path: "test::Foo", 2552 },
2574 nav: NavigationTarget { 2553 HoverGotoTypeData {
2575 file_id: FileId( 2554 mod_path: "Foo",
2576 1, 2555 nav: NavigationTarget {
2577 ), 2556 file_id: FileId(
2578 full_range: 0..12, 2557 1,
2579 name: "Foo", 2558 ),
2580 kind: TRAIT_DEF, 2559 full_range: 0..12,
2581 focus_range: Some( 2560 focus_range: Some(
2582 6..9, 2561 6..9,
2583 ), 2562 ),
2584 container_name: None, 2563 name: "Foo",
2585 description: Some( 2564 kind: TRAIT,
2586 "trait Foo", 2565 container_name: None,
2587 ), 2566 description: Some(
2588 docs: None, 2567 "trait Foo",
2589 }, 2568 ),
2590 }, 2569 docs: None,
2591 ], 2570 },
2592 ), 2571 },
2593 ] 2572 ],
2594 "###); 2573 ),
2574 ]
2575 "#]],
2576 );
2595 } 2577 }
2596 2578
2597 #[test] 2579 #[test]
2598 fn test_hover_dyn_arg_has_goto_type_action() { 2580 fn test_hover_dyn_arg_has_goto_type_action() {
2599 let (_, actions) = check_hover_result( 2581 check_actions(
2600 " 2582 r#"
2601 //- /lib.rs 2583trait Foo {}
2602 trait Foo {} 2584fn foo(ar<|>g: &dyn Foo) {}
2603 fn foo(ar<|>g: &dyn Foo) {} 2585"#,
2604 ", 2586 expect![[r#"
2605 &["&dyn Foo"],
2606 );
2607 assert_debug_snapshot!(actions,
2608 @r###"
2609 [
2610 GoToType(
2611 [ 2587 [
2612 HoverGotoTypeData { 2588 GoToType(
2613 mod_path: "test::Foo", 2589 [
2614 nav: NavigationTarget { 2590 HoverGotoTypeData {
2615 file_id: FileId( 2591 mod_path: "Foo",
2616 1, 2592 nav: NavigationTarget {
2617 ), 2593 file_id: FileId(
2618 full_range: 0..12, 2594 1,
2619 name: "Foo", 2595 ),
2620 kind: TRAIT_DEF, 2596 full_range: 0..12,
2621 focus_range: Some( 2597 focus_range: Some(
2622 6..9, 2598 6..9,
2623 ), 2599 ),
2624 container_name: None, 2600 name: "Foo",
2625 description: Some( 2601 kind: TRAIT,
2626 "trait Foo", 2602 container_name: None,
2627 ), 2603 description: Some(
2628 docs: None, 2604 "trait Foo",
2629 }, 2605 ),
2630 }, 2606 docs: None,
2631 ], 2607 },
2632 ), 2608 },
2633 ] 2609 ],
2634 "###); 2610 ),
2611 ]
2612 "#]],
2613 );
2635 } 2614 }
2636 2615
2637 #[test] 2616 #[test]
2638 fn test_hover_generic_dyn_arg_has_goto_type_action() { 2617 fn test_hover_generic_dyn_arg_has_goto_type_action() {
2639 let (_, actions) = check_hover_result( 2618 check_actions(
2640 " 2619 r#"
2641 //- /lib.rs 2620trait Foo<T> {}
2642 trait Foo<T> {} 2621struct S {}
2643 struct S {} 2622fn foo(ar<|>g: &dyn Foo<S>) {}
2644 fn foo(ar<|>g: &dyn Foo<S>) {} 2623"#,
2645 ", 2624 expect![[r#"
2646 &["&dyn Foo<S>"],
2647 );
2648 assert_debug_snapshot!(actions,
2649 @r###"
2650 [
2651 GoToType(
2652 [ 2625 [
2653 HoverGotoTypeData { 2626 GoToType(
2654 mod_path: "test::Foo", 2627 [
2655 nav: NavigationTarget { 2628 HoverGotoTypeData {
2656 file_id: FileId( 2629 mod_path: "Foo",
2657 1, 2630 nav: NavigationTarget {
2658 ), 2631 file_id: FileId(
2659 full_range: 0..15, 2632 1,
2660 name: "Foo", 2633 ),
2661 kind: TRAIT_DEF, 2634 full_range: 0..15,
2662 focus_range: Some( 2635 focus_range: Some(
2663 6..9, 2636 6..9,
2664 ), 2637 ),
2665 container_name: None, 2638 name: "Foo",
2666 description: Some( 2639 kind: TRAIT,
2667 "trait Foo", 2640 container_name: None,
2668 ), 2641 description: Some(
2669 docs: None, 2642 "trait Foo",
2670 }, 2643 ),
2671 }, 2644 docs: None,
2672 HoverGotoTypeData { 2645 },
2673 mod_path: "test::S", 2646 },
2674 nav: NavigationTarget { 2647 HoverGotoTypeData {
2675 file_id: FileId( 2648 mod_path: "S",
2676 1, 2649 nav: NavigationTarget {
2677 ), 2650 file_id: FileId(
2678 full_range: 16..27, 2651 1,
2679 name: "S", 2652 ),
2680 kind: STRUCT_DEF, 2653 full_range: 16..27,
2681 focus_range: Some( 2654 focus_range: Some(
2682 23..24, 2655 23..24,
2683 ), 2656 ),
2684 container_name: None, 2657 name: "S",
2685 description: Some( 2658 kind: STRUCT,
2686 "struct S", 2659 container_name: None,
2687 ), 2660 description: Some(
2688 docs: None, 2661 "struct S",
2689 }, 2662 ),
2690 }, 2663 docs: None,
2691 ], 2664 },
2692 ), 2665 },
2693 ] 2666 ],
2694 "###); 2667 ),
2668 ]
2669 "#]],
2670 );
2695 } 2671 }
2696 2672
2697 #[test] 2673 #[test]
2698 fn test_hover_goto_type_action_links_order() { 2674 fn test_hover_goto_type_action_links_order() {
2699 let (_, actions) = check_hover_result( 2675 check_actions(
2700 " 2676 r#"
2701 //- /lib.rs 2677trait ImplTrait<T> {}
2702 trait ImplTrait<T> {} 2678trait DynTrait<T> {}
2703 trait DynTrait<T> {} 2679struct B<T> {}
2704 struct B<T> {} 2680struct S {}
2705 struct S {}
2706 2681
2707 fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} 2682fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
2708 ", 2683 "#,
2709 &["&impl ImplTrait<B<dyn DynTrait<B<S>>>>"], 2684 expect![[r#"
2710 );
2711 assert_debug_snapshot!(actions,
2712 @r###"
2713 [
2714 GoToType(
2715 [ 2685 [
2716 HoverGotoTypeData { 2686 GoToType(
2717 mod_path: "test::ImplTrait", 2687 [
2718 nav: NavigationTarget { 2688 HoverGotoTypeData {
2719 file_id: FileId( 2689 mod_path: "ImplTrait",
2720 1, 2690 nav: NavigationTarget {
2721 ), 2691 file_id: FileId(
2722 full_range: 0..21, 2692 1,
2723 name: "ImplTrait", 2693 ),
2724 kind: TRAIT_DEF, 2694 full_range: 0..21,
2725 focus_range: Some( 2695 focus_range: Some(
2726 6..15, 2696 6..15,
2727 ), 2697 ),
2728 container_name: None, 2698 name: "ImplTrait",
2729 description: Some( 2699 kind: TRAIT,
2730 "trait ImplTrait", 2700 container_name: None,
2731 ), 2701 description: Some(
2732 docs: None, 2702 "trait ImplTrait",
2733 }, 2703 ),
2734 }, 2704 docs: None,
2735 HoverGotoTypeData { 2705 },
2736 mod_path: "test::B", 2706 },
2737 nav: NavigationTarget { 2707 HoverGotoTypeData {
2738 file_id: FileId( 2708 mod_path: "B",
2739 1, 2709 nav: NavigationTarget {
2740 ), 2710 file_id: FileId(
2741 full_range: 43..57, 2711 1,
2742 name: "B", 2712 ),
2743 kind: STRUCT_DEF, 2713 full_range: 43..57,
2744 focus_range: Some( 2714 focus_range: Some(
2745 50..51, 2715 50..51,
2746 ), 2716 ),
2747 container_name: None, 2717 name: "B",
2748 description: Some( 2718 kind: STRUCT,
2749 "struct B", 2719 container_name: None,
2750 ), 2720 description: Some(
2751 docs: None, 2721 "struct B",
2752 }, 2722 ),
2753 }, 2723 docs: None,
2754 HoverGotoTypeData { 2724 },
2755 mod_path: "test::DynTrait", 2725 },
2756 nav: NavigationTarget { 2726 HoverGotoTypeData {
2757 file_id: FileId( 2727 mod_path: "DynTrait",
2758 1, 2728 nav: NavigationTarget {
2759 ), 2729 file_id: FileId(
2760 full_range: 22..42, 2730 1,
2761 name: "DynTrait", 2731 ),
2762 kind: TRAIT_DEF, 2732 full_range: 22..42,
2763 focus_range: Some( 2733 focus_range: Some(
2764 28..36, 2734 28..36,
2765 ), 2735 ),
2766 container_name: None, 2736 name: "DynTrait",
2767 description: Some( 2737 kind: TRAIT,
2768 "trait DynTrait", 2738 container_name: None,
2769 ), 2739 description: Some(
2770 docs: None, 2740 "trait DynTrait",
2771 }, 2741 ),
2772 }, 2742 docs: None,
2773 HoverGotoTypeData { 2743 },
2774 mod_path: "test::S", 2744 },
2775 nav: NavigationTarget { 2745 HoverGotoTypeData {
2776 file_id: FileId( 2746 mod_path: "S",
2777 1, 2747 nav: NavigationTarget {
2778 ), 2748 file_id: FileId(
2779 full_range: 58..69, 2749 1,
2780 name: "S", 2750 ),
2781 kind: STRUCT_DEF, 2751 full_range: 58..69,
2782 focus_range: Some( 2752 focus_range: Some(
2783 65..66, 2753 65..66,
2784 ), 2754 ),
2785 container_name: None, 2755 name: "S",
2786 description: Some( 2756 kind: STRUCT,
2787 "struct S", 2757 container_name: None,
2788 ), 2758 description: Some(
2789 docs: None, 2759 "struct S",
2790 }, 2760 ),
2791 }, 2761 docs: None,
2792 ], 2762 },
2793 ), 2763 },
2794 ] 2764 ],
2795 "###); 2765 ),
2766 ]
2767 "#]],
2768 );
2796 } 2769 }
2797 2770
2798 #[test] 2771 #[test]
2799 fn test_hover_associated_type_has_goto_type_action() { 2772 fn test_hover_associated_type_has_goto_type_action() {
2800 let (_, actions) = check_hover_result( 2773 check_actions(
2801 " 2774 r#"
2802 //- /main.rs 2775trait Foo {
2803 trait Foo { 2776 type Item;
2804 type Item; 2777 fn get(self) -> Self::Item {}
2805 fn get(self) -> Self::Item {} 2778}
2806 }
2807 2779
2808 struct Bar{} 2780struct Bar{}
2809 struct S{} 2781struct S{}
2810 2782
2811 impl Foo for S{ 2783impl Foo for S { type Item = Bar; }
2812 type Item = Bar;
2813 }
2814 2784
2815 fn test() -> impl Foo { 2785fn test() -> impl Foo { S {} }
2816 S{}
2817 }
2818 2786
2819 fn main() { 2787fn main() { let s<|>t = test().get(); }
2820 let s<|>t = test().get(); 2788"#,
2821 } 2789 expect![[r#"
2822 ",
2823 &["Foo::Item<impl Foo>"],
2824 );
2825 assert_debug_snapshot!(actions,
2826 @r###"
2827 [
2828 GoToType(
2829 [ 2790 [
2830 HoverGotoTypeData { 2791 GoToType(
2831 mod_path: "test::Foo", 2792 [
2832 nav: NavigationTarget { 2793 HoverGotoTypeData {
2833 file_id: FileId( 2794 mod_path: "Foo",
2834 1, 2795 nav: NavigationTarget {
2835 ), 2796 file_id: FileId(
2836 full_range: 0..62, 2797 1,
2837 name: "Foo", 2798 ),
2838 kind: TRAIT_DEF, 2799 full_range: 0..62,
2839 focus_range: Some( 2800 focus_range: Some(
2840 6..9, 2801 6..9,
2841 ), 2802 ),
2842 container_name: None, 2803 name: "Foo",
2843 description: Some( 2804 kind: TRAIT,
2844 "trait Foo", 2805 container_name: None,
2845 ), 2806 description: Some(
2846 docs: None, 2807 "trait Foo",
2847 }, 2808 ),
2848 }, 2809 docs: None,
2849 ], 2810 },
2850 ), 2811 },
2851 ] 2812 ],
2852 "###); 2813 ),
2814 ]
2815 "#]],
2816 );
2853 } 2817 }
2854} 2818}
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index c87652555..4bbbcd258 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -1,14 +1,16 @@
1use hir::{Adt, HirDisplay, Semantics, Type}; 1use hir::{Adt, Callable, HirDisplay, Semantics, Type};
2use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
3use ra_prof::profile; 3use ra_prof::profile;
4use ra_syntax::{ 4use ra_syntax::{
5 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, 5 ast::{self, ArgListOwner, AstNode},
6 match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, 6 match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, T,
7}; 7};
8
9use crate::{FileId, FunctionSignature};
10use stdx::to_lower_snake_case; 8use stdx::to_lower_snake_case;
11 9
10use crate::FileId;
11use ast::NameOwner;
12use either::Either;
13
12#[derive(Clone, Debug, PartialEq, Eq)] 14#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct InlayHintsConfig { 15pub struct InlayHintsConfig {
14 pub type_hints: bool, 16 pub type_hints: bool,
@@ -94,7 +96,7 @@ fn get_chaining_hints(
94 return None; 96 return None;
95 } 97 }
96 98
97 if matches!(expr, ast::Expr::RecordLit(_)) { 99 if matches!(expr, ast::Expr::RecordExpr(_)) {
98 return None; 100 return None;
99 } 101 }
100 102
@@ -112,7 +114,7 @@ fn get_chaining_hints(
112 // Ignoring extra whitespace and comments 114 // Ignoring extra whitespace and comments
113 let next = tokens.next()?.kind(); 115 let next = tokens.next()?.kind();
114 let next_next = tokens.next()?.kind(); 116 let next_next = tokens.next()?.kind();
115 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { 117 if next == SyntaxKind::WHITESPACE && next_next == T![.] {
116 let ty = sema.type_of_expr(&expr)?; 118 let ty = sema.type_of_expr(&expr)?;
117 if ty.is_unknown() { 119 if ty.is_unknown() {
118 return None; 120 return None;
@@ -150,19 +152,22 @@ fn get_param_name_hints(
150 _ => return None, 152 _ => return None,
151 }; 153 };
152 154
153 let fn_signature = get_fn_signature(sema, &expr)?; 155 let callable = get_callable(sema, &expr)?;
154 let n_params_to_skip = 156 let hints = callable
155 if fn_signature.has_self_param && matches!(&expr, ast::Expr::MethodCallExpr(_)) { 157 .params(sema.db)
156 1 158 .into_iter()
157 } else {
158 0
159 };
160 let hints = fn_signature
161 .parameter_names
162 .iter()
163 .skip(n_params_to_skip)
164 .zip(args) 159 .zip(args)
165 .filter(|(param, arg)| should_show_param_name_hint(sema, &fn_signature, param, &arg)) 160 .filter_map(|((param, _ty), arg)| match param? {
161 Either::Left(self_param) => Some((self_param.to_string(), arg)),
162 Either::Right(pat) => {
163 let param_name = match pat {
164 ast::Pat::BindPat(it) => it.name()?.to_string(),
165 it => it.to_string(),
166 };
167 Some((param_name, arg))
168 }
169 })
170 .filter(|(param_name, arg)| should_show_param_name_hint(sema, &callable, &param_name, &arg))
166 .map(|(param_name, arg)| InlayHint { 171 .map(|(param_name, arg)| InlayHint {
167 range: arg.syntax().text_range(), 172 range: arg.syntax().text_range(),
168 kind: InlayKind::ParameterHint, 173 kind: InlayKind::ParameterHint,
@@ -225,10 +230,10 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_
225 match_ast! { 230 match_ast! {
226 match node { 231 match node {
227 ast::LetStmt(it) => { 232 ast::LetStmt(it) => {
228 return it.ascribed_type().is_some() 233 return it.ty().is_some()
229 }, 234 },
230 ast::Param(it) => { 235 ast::Param(it) => {
231 return it.ascribed_type().is_some() 236 return it.ty().is_some()
232 }, 237 },
233 ast::MatchArm(_it) => { 238 ast::MatchArm(_it) => {
234 return pat_is_enum_variant(db, bind_pat, pat_ty); 239 return pat_is_enum_variant(db, bind_pat, pat_ty);
@@ -250,28 +255,28 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_
250 255
251fn should_show_param_name_hint( 256fn should_show_param_name_hint(
252 sema: &Semantics<RootDatabase>, 257 sema: &Semantics<RootDatabase>,
253 fn_signature: &FunctionSignature, 258 callable: &Callable,
254 param_name: &str, 259 param_name: &str,
255 argument: &ast::Expr, 260 argument: &ast::Expr,
256) -> bool { 261) -> bool {
257 let param_name = param_name.trim_start_matches('_'); 262 let param_name = param_name.trim_start_matches('_');
263 let fn_name = match callable.kind() {
264 hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()),
265 hir::CallableKind::TupleStruct(_)
266 | hir::CallableKind::TupleEnumVariant(_)
267 | hir::CallableKind::Closure => None,
268 };
258 if param_name.is_empty() 269 if param_name.is_empty()
259 || Some(param_name) == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_')) 270 || Some(param_name) == fn_name.as_ref().map(|s| s.trim_start_matches('_'))
260 || is_argument_similar_to_param_name(sema, argument, param_name) 271 || is_argument_similar_to_param_name(sema, argument, param_name)
261 || param_name.starts_with("ra_fixture") 272 || param_name.starts_with("ra_fixture")
262 { 273 {
263 return false; 274 return false;
264 } 275 }
265 276
266 let parameters_len = if fn_signature.has_self_param {
267 fn_signature.parameters.len() - 1
268 } else {
269 fn_signature.parameters.len()
270 };
271
272 // avoid displaying hints for common functions like map, filter, etc. 277 // avoid displaying hints for common functions like map, filter, etc.
273 // or other obvious words used in std 278 // or other obvious words used in std
274 !(parameters_len == 1 && is_obvious_param(param_name)) 279 !(callable.n_params() == 1 && is_obvious_param(param_name))
275} 280}
276 281
277fn is_argument_similar_to_param_name( 282fn is_argument_similar_to_param_name(
@@ -318,610 +323,264 @@ fn is_obvious_param(param_name: &str) -> bool {
318 param_name.len() == 1 || is_obvious_param_name 323 param_name.len() == 1 || is_obvious_param_name
319} 324}
320 325
321fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<FunctionSignature> { 326fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<Callable> {
322 match expr { 327 match expr {
323 ast::Expr::CallExpr(expr) => { 328 ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db),
324 // FIXME: Type::as_callable is broken for closures 329 ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr),
325 let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?;
326 match callable_def {
327 hir::CallableDef::FunctionId(it) => {
328 Some(FunctionSignature::from_hir(sema.db, it.into()))
329 }
330 hir::CallableDef::StructId(it) => {
331 FunctionSignature::from_struct(sema.db, it.into())
332 }
333 hir::CallableDef::EnumVariantId(it) => {
334 FunctionSignature::from_enum_variant(sema.db, it.into())
335 }
336 }
337 }
338 ast::Expr::MethodCallExpr(expr) => {
339 let fn_def = sema.resolve_method_call(&expr)?;
340 Some(FunctionSignature::from_hir(sema.db, fn_def))
341 }
342 _ => None, 330 _ => None,
343 } 331 }
344} 332}
345 333
346#[cfg(test)] 334#[cfg(test)]
347mod tests { 335mod tests {
348 use crate::inlay_hints::InlayHintsConfig; 336 use expect::{expect, Expect};
349 use insta::assert_debug_snapshot; 337 use test_utils::extract_annotations;
338
339 use crate::{inlay_hints::InlayHintsConfig, mock_analysis::single_file};
340
341 fn check(ra_fixture: &str) {
342 check_with_config(InlayHintsConfig::default(), ra_fixture);
343 }
344
345 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
346 let (analysis, file_id) = single_file(ra_fixture);
347 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
348 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
349 let actual =
350 inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
351 assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual);
352 }
350 353
351 use crate::mock_analysis::single_file; 354 fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
355 let (analysis, file_id) = single_file(ra_fixture);
356 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
357 expect.assert_debug_eq(&inlay_hints)
358 }
352 359
353 #[test] 360 #[test]
354 fn param_hints_only() { 361 fn param_hints_only() {
355 let (analysis, file_id) = single_file( 362 check_with_config(
363 InlayHintsConfig {
364 parameter_hints: true,
365 type_hints: false,
366 chaining_hints: false,
367 max_length: None,
368 },
356 r#" 369 r#"
357 fn foo(a: i32, b: i32) -> i32 { a + b } 370fn foo(a: i32, b: i32) -> i32 { a + b }
358 fn main() { 371fn main() {
359 let _x = foo(4, 4); 372 let _x = foo(
360 }"#, 373 4,
374 //^ a
375 4,
376 //^ b
377 );
378}"#,
361 ); 379 );
362 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
363 [
364 InlayHint {
365 range: 69..70,
366 kind: ParameterHint,
367 label: "a",
368 },
369 InlayHint {
370 range: 72..73,
371 kind: ParameterHint,
372 label: "b",
373 },
374 ]
375 "###);
376 } 380 }
377 381
378 #[test] 382 #[test]
379 fn hints_disabled() { 383 fn hints_disabled() {
380 let (analysis, file_id) = single_file( 384 check_with_config(
385 InlayHintsConfig {
386 type_hints: false,
387 parameter_hints: false,
388 chaining_hints: false,
389 max_length: None,
390 },
381 r#" 391 r#"
382 fn foo(a: i32, b: i32) -> i32 { a + b } 392fn foo(a: i32, b: i32) -> i32 { a + b }
383 fn main() { 393fn main() {
384 let _x = foo(4, 4); 394 let _x = foo(4, 4);
385 }"#, 395}"#,
386 ); 396 );
387 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###);
388 } 397 }
389 398
390 #[test] 399 #[test]
391 fn type_hints_only() { 400 fn type_hints_only() {
392 let (analysis, file_id) = single_file( 401 check_with_config(
402 InlayHintsConfig {
403 type_hints: true,
404 parameter_hints: false,
405 chaining_hints: false,
406 max_length: None,
407 },
393 r#" 408 r#"
394 fn foo(a: i32, b: i32) -> i32 { a + b } 409fn foo(a: i32, b: i32) -> i32 { a + b }
395 fn main() { 410fn main() {
396 let _x = foo(4, 4); 411 let _x = foo(4, 4);
397 }"#, 412 //^^ i32
413}"#,
398 ); 414 );
399 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
400 [
401 InlayHint {
402 range: 60..62,
403 kind: TypeHint,
404 label: "i32",
405 },
406 ]
407 "###);
408 } 415 }
416
409 #[test] 417 #[test]
410 fn default_generic_types_should_not_be_displayed() { 418 fn default_generic_types_should_not_be_displayed() {
411 let (analysis, file_id) = single_file( 419 check(
412 r#" 420 r#"
413struct Test<K, T = u8> { 421struct Test<K, T = u8> { k: K, t: T }
414 k: K,
415 t: T,
416}
417 422
418fn main() { 423fn main() {
419 let zz = Test { t: 23u8, k: 33 }; 424 let zz = Test { t: 23u8, k: 33 };
425 //^^ Test<i32>
420 let zz_ref = &zz; 426 let zz_ref = &zz;
427 //^^^^^^ &Test<i32>
428 let test = || zz;
429 //^^^^ || -> Test<i32>
421}"#, 430}"#,
422 ); 431 );
423
424 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
425 [
426 InlayHint {
427 range: 68..70,
428 kind: TypeHint,
429 label: "Test<i32>",
430 },
431 InlayHint {
432 range: 106..112,
433 kind: TypeHint,
434 label: "&Test<i32>",
435 },
436 ]
437 "###
438 );
439 } 432 }
440 433
441 #[test] 434 #[test]
442 fn let_statement() { 435 fn let_statement() {
443 let (analysis, file_id) = single_file( 436 check(
444 r#" 437 r#"
445#[derive(PartialEq)] 438#[derive(PartialEq)]
446enum CustomOption<T> { 439enum Option<T> { None, Some(T) }
447 None,
448 Some(T),
449}
450 440
451#[derive(PartialEq)] 441#[derive(PartialEq)]
452struct Test { 442struct Test { a: Option<u32>, b: u8 }
453 a: CustomOption<u32>,
454 b: u8,
455}
456 443
457fn main() { 444fn main() {
458 struct InnerStruct {} 445 struct InnerStruct {}
459 446
460 let test = 54; 447 let test = 54;
448 //^^^^ i32
461 let test: i32 = 33; 449 let test: i32 = 33;
462 let mut test = 33; 450 let mut test = 33;
451 //^^^^^^^^ i32
463 let _ = 22; 452 let _ = 22;
464 let test = "test"; 453 let test = "test";
454 //^^^^ &str
465 let test = InnerStruct {}; 455 let test = InnerStruct {};
466 456
467 let test = vec![222]; 457 let test = unresolved();
468 let test: Vec<_> = (0..3).collect();
469 let test = (0..3).collect::<Vec<i128>>();
470 let test = (0..3).collect::<Vec<_>>();
471
472 let mut test = Vec::new();
473 test.push(333);
474 458
475 let test = (42, 'a'); 459 let test = (42, 'a');
476 let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5)); 460 //^^^^ (i32, char)
461 let (a, (b, (c,)) = (2, (3, (9.2,));
462 //^ i32 ^ i32 ^ f64
477 let &x = &92; 463 let &x = &92;
464 //^ i32
478}"#, 465}"#,
479 ); 466 );
480
481 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
482 [
483 InlayHint {
484 range: 192..196,
485 kind: TypeHint,
486 label: "i32",
487 },
488 InlayHint {
489 range: 235..243,
490 kind: TypeHint,
491 label: "i32",
492 },
493 InlayHint {
494 range: 274..278,
495 kind: TypeHint,
496 label: "&str",
497 },
498 InlayHint {
499 range: 538..542,
500 kind: TypeHint,
501 label: "(i32, char)",
502 },
503 InlayHint {
504 range: 565..566,
505 kind: TypeHint,
506 label: "i32",
507 },
508 InlayHint {
509 range: 569..570,
510 kind: TypeHint,
511 label: "i32",
512 },
513 InlayHint {
514 range: 572..573,
515 kind: TypeHint,
516 label: "i32",
517 },
518 InlayHint {
519 range: 576..577,
520 kind: TypeHint,
521 label: "f64",
522 },
523 InlayHint {
524 range: 579..580,
525 kind: TypeHint,
526 label: "f64",
527 },
528 InlayHint {
529 range: 583..584,
530 kind: TypeHint,
531 label: "i32",
532 },
533 InlayHint {
534 range: 626..627,
535 kind: TypeHint,
536 label: "i32",
537 },
538 ]
539 "###
540 );
541 } 467 }
542 468
543 #[test] 469 #[test]
544 fn closure_parameters() { 470 fn closure_parameters() {
545 let (analysis, file_id) = single_file( 471 check(
546 r#" 472 r#"
547fn main() { 473fn main() {
548 let mut start = 0; 474 let mut start = 0;
549 (0..2).for_each(|increment| { 475 //^^^^^^^^^ i32
550 start += increment; 476 (0..2).for_each(|increment| { start += increment; });
551 }); 477 //^^^^^^^^^ i32
478
479 let multiply =
480 //^^^^^^^^ |…| -> i32
481 | a, b| a * b
482 //^ i32 ^ i32
483 ;
552 484
553 let multiply = |a, b, c, d| a * b * c * d; 485 let _: i32 = multiply(1, 2);
554 let _: i32 = multiply(1, 2, 3, 4);
555 let multiply_ref = &multiply; 486 let multiply_ref = &multiply;
487 //^^^^^^^^^^^^ &|…| -> i32
556 488
557 let return_42 = || 42; 489 let return_42 = || 42;
490 //^^^^^^^^^ || -> i32
558}"#, 491}"#,
559 ); 492 );
560
561 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
562 [
563 InlayHint {
564 range: 20..29,
565 kind: TypeHint,
566 label: "i32",
567 },
568 InlayHint {
569 range: 56..65,
570 kind: TypeHint,
571 label: "i32",
572 },
573 InlayHint {
574 range: 114..122,
575 kind: TypeHint,
576 label: "|…| -> i32",
577 },
578 InlayHint {
579 range: 126..127,
580 kind: TypeHint,
581 label: "i32",
582 },
583 InlayHint {
584 range: 129..130,
585 kind: TypeHint,
586 label: "i32",
587 },
588 InlayHint {
589 range: 132..133,
590 kind: TypeHint,
591 label: "i32",
592 },
593 InlayHint {
594 range: 135..136,
595 kind: TypeHint,
596 label: "i32",
597 },
598 InlayHint {
599 range: 200..212,
600 kind: TypeHint,
601 label: "&|…| -> i32",
602 },
603 InlayHint {
604 range: 235..244,
605 kind: TypeHint,
606 label: "|| -> i32",
607 },
608 ]
609 "###
610 );
611 } 493 }
612 494
613 #[test] 495 #[test]
614 fn for_expression() { 496 fn for_expression() {
615 let (analysis, file_id) = single_file( 497 check(
616 r#" 498 r#"
617fn main() { 499fn main() {
618 let mut start = 0; 500 let mut start = 0;
619 for increment in 0..2 { 501 //^^^^^^^^^ i32
620 start += increment; 502 for increment in 0..2 { start += increment; }
621 } 503 //^^^^^^^^^ i32
622}"#, 504}"#,
623 ); 505 );
624
625 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
626 [
627 InlayHint {
628 range: 20..29,
629 kind: TypeHint,
630 label: "i32",
631 },
632 InlayHint {
633 range: 43..52,
634 kind: TypeHint,
635 label: "i32",
636 },
637 ]
638 "###
639 );
640 } 506 }
641 507
642 #[test] 508 #[test]
643 fn if_expr() { 509 fn if_expr() {
644 let (analysis, file_id) = single_file( 510 check(
645 r#" 511 r#"
646#[derive(PartialEq)] 512enum Option<T> { None, Some(T) }
647enum CustomOption<T> { 513use Option::*;
648 None,
649 Some(T),
650}
651
652#[derive(PartialEq)]
653struct Test {
654 a: CustomOption<u32>,
655 b: u8,
656}
657 514
658use CustomOption::*; 515struct Test { a: Option<u32>, b: u8 }
659 516
660fn main() { 517fn main() {
661 let test = Some(Test { a: Some(3), b: 1 }); 518 let test = Some(Test { a: Some(3), b: 1 });
519 //^^^^ Option<Test>
662 if let None = &test {}; 520 if let None = &test {};
663 if let test = &test {}; 521 if let test = &test {};
522 //^^^^ &Option<Test>
664 if let Some(test) = &test {}; 523 if let Some(test) = &test {};
665 if let Some(Test { a, b }) = &test {}; 524 //^^^^ &Test
666 if let Some(Test { a: x, b: y }) = &test {}; 525 if let Some(Test { a, b }) = &test {};
667 if let Some(Test { a: Some(x), b: y }) = &test {}; 526 //^ &Option<u32> ^ &u8
668 if let Some(Test { a: None, b: y }) = &test {}; 527 if let Some(Test { a: x, b: y }) = &test {};
528 //^ &Option<u32> ^ &u8
529 if let Some(Test { a: Some(x), b: y }) = &test {};
530 //^ &u32 ^ &u8
531 if let Some(Test { a: None, b: y }) = &test {};
532 //^ &u8
669 if let Some(Test { b: y, .. }) = &test {}; 533 if let Some(Test { b: y, .. }) = &test {};
670 534 //^ &u8
671 if test == None {} 535 if test == None {}
672}"#, 536}"#,
673 ); 537 );
674
675 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
676 [
677 InlayHint {
678 range: 187..191,
679 kind: TypeHint,
680 label: "CustomOption<Test>",
681 },
682 InlayHint {
683 range: 266..270,
684 kind: TypeHint,
685 label: "&CustomOption<Test>",
686 },
687 InlayHint {
688 range: 299..303,
689 kind: TypeHint,
690 label: "&Test",
691 },
692 InlayHint {
693 range: 340..341,
694 kind: TypeHint,
695 label: "&CustomOption<u32>",
696 },
697 InlayHint {
698 range: 343..344,
699 kind: TypeHint,
700 label: "&u8",
701 },
702 InlayHint {
703 range: 386..387,
704 kind: TypeHint,
705 label: "&CustomOption<u32>",
706 },
707 InlayHint {
708 range: 392..393,
709 kind: TypeHint,
710 label: "&u8",
711 },
712 InlayHint {
713 range: 440..441,
714 kind: TypeHint,
715 label: "&u32",
716 },
717 InlayHint {
718 range: 447..448,
719 kind: TypeHint,
720 label: "&u8",
721 },
722 InlayHint {
723 range: 499..500,
724 kind: TypeHint,
725 label: "&u8",
726 },
727 InlayHint {
728 range: 542..543,
729 kind: TypeHint,
730 label: "&u8",
731 },
732 ]
733 "###
734 );
735 } 538 }
736 539
737 #[test] 540 #[test]
738 fn while_expr() { 541 fn while_expr() {
739 let (analysis, file_id) = single_file( 542 check(
740 r#" 543 r#"
741#[derive(PartialEq)] 544enum Option<T> { None, Some(T) }
742enum CustomOption<T> { 545use Option::*;
743 None,
744 Some(T),
745}
746 546
747#[derive(PartialEq)] 547struct Test { a: Option<u32>, b: u8 }
748struct Test {
749 a: CustomOption<u32>,
750 b: u8,
751}
752
753use CustomOption::*;
754 548
755fn main() { 549fn main() {
756 let test = Some(Test { a: Some(3), b: 1 }); 550 let test = Some(Test { a: Some(3), b: 1 });
757 while let None = &test {}; 551 //^^^^ Option<Test>
758 while let test = &test {}; 552 while let Some(Test { a: Some(x), b: y }) = &test {};
759 while let Some(test) = &test {}; 553 //^ &u32 ^ &u8
760 while let Some(Test { a, b }) = &test {};
761 while let Some(Test { a: x, b: y }) = &test {};
762 while let Some(Test { a: Some(x), b: y }) = &test {};
763 while let Some(Test { a: None, b: y }) = &test {};
764 while let Some(Test { b: y, .. }) = &test {};
765
766 while test == None {}
767}"#, 554}"#,
768 ); 555 );
769
770 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
771 [
772 InlayHint {
773 range: 187..191,
774 kind: TypeHint,
775 label: "CustomOption<Test>",
776 },
777 InlayHint {
778 range: 272..276,
779 kind: TypeHint,
780 label: "&CustomOption<Test>",
781 },
782 InlayHint {
783 range: 308..312,
784 kind: TypeHint,
785 label: "&Test",
786 },
787 InlayHint {
788 range: 352..353,
789 kind: TypeHint,
790 label: "&CustomOption<u32>",
791 },
792 InlayHint {
793 range: 355..356,
794 kind: TypeHint,
795 label: "&u8",
796 },
797 InlayHint {
798 range: 401..402,
799 kind: TypeHint,
800 label: "&CustomOption<u32>",
801 },
802 InlayHint {
803 range: 407..408,
804 kind: TypeHint,
805 label: "&u8",
806 },
807 InlayHint {
808 range: 458..459,
809 kind: TypeHint,
810 label: "&u32",
811 },
812 InlayHint {
813 range: 465..466,
814 kind: TypeHint,
815 label: "&u8",
816 },
817 InlayHint {
818 range: 520..521,
819 kind: TypeHint,
820 label: "&u8",
821 },
822 InlayHint {
823 range: 566..567,
824 kind: TypeHint,
825 label: "&u8",
826 },
827 ]
828 "###
829 );
830 } 556 }
831 557
832 #[test] 558 #[test]
833 fn match_arm_list() { 559 fn match_arm_list() {
834 let (analysis, file_id) = single_file( 560 check(
835 r#" 561 r#"
836#[derive(PartialEq)] 562enum Option<T> { None, Some(T) }
837enum CustomOption<T> { 563use Option::*;
838 None,
839 Some(T),
840}
841 564
842#[derive(PartialEq)] 565struct Test { a: Option<u32>, b: u8 }
843struct Test {
844 a: CustomOption<u32>,
845 b: u8,
846}
847
848use CustomOption::*;
849 566
850fn main() { 567fn main() {
851 match Some(Test { a: Some(3), b: 1 }) { 568 match Some(Test { a: Some(3), b: 1 }) {
852 None => (), 569 None => (),
853 test => (), 570 test => (),
854 Some(test) => (), 571 //^^^^ Option<Test>
855 Some(Test { a, b }) => (),
856 Some(Test { a: x, b: y }) => (),
857 Some(Test { a: Some(x), b: y }) => (), 572 Some(Test { a: Some(x), b: y }) => (),
858 Some(Test { a: None, b: y }) => (), 573 //^ u32 ^ u8
859 Some(Test { b: y, .. }) => (),
860 _ => {} 574 _ => {}
861 } 575 }
862}"#, 576}"#,
863 ); 577 );
864
865 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
866 [
867 InlayHint {
868 range: 251..255,
869 kind: TypeHint,
870 label: "CustomOption<Test>",
871 },
872 InlayHint {
873 range: 276..280,
874 kind: TypeHint,
875 label: "Test",
876 },
877 InlayHint {
878 range: 309..310,
879 kind: TypeHint,
880 label: "CustomOption<u32>",
881 },
882 InlayHint {
883 range: 312..313,
884 kind: TypeHint,
885 label: "u8",
886 },
887 InlayHint {
888 range: 347..348,
889 kind: TypeHint,
890 label: "CustomOption<u32>",
891 },
892 InlayHint {
893 range: 353..354,
894 kind: TypeHint,
895 label: "u8",
896 },
897 InlayHint {
898 range: 393..394,
899 kind: TypeHint,
900 label: "u32",
901 },
902 InlayHint {
903 range: 400..401,
904 kind: TypeHint,
905 label: "u8",
906 },
907 InlayHint {
908 range: 444..445,
909 kind: TypeHint,
910 label: "u8",
911 },
912 InlayHint {
913 range: 479..480,
914 kind: TypeHint,
915 label: "u8",
916 },
917 ]
918 "###
919 );
920 } 578 }
921 579
922 #[test] 580 #[test]
923 fn hint_truncation() { 581 fn hint_truncation() {
924 let (analysis, file_id) = single_file( 582 check_with_config(
583 InlayHintsConfig { max_length: Some(8), ..Default::default() },
925 r#" 584 r#"
926struct Smol<T>(T); 585struct Smol<T>(T);
927 586
@@ -929,52 +588,25 @@ struct VeryLongOuterName<T>(T);
929 588
930fn main() { 589fn main() {
931 let a = Smol(0u32); 590 let a = Smol(0u32);
591 //^ Smol<u32>
932 let b = VeryLongOuterName(0usize); 592 let b = VeryLongOuterName(0usize);
593 //^ VeryLongOuterName<…>
933 let c = Smol(Smol(0u32)) 594 let c = Smol(Smol(0u32))
595 //^ Smol<Smol<…>>
934}"#, 596}"#,
935 ); 597 );
936
937 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
938 [
939 InlayHint {
940 range: 73..74,
941 kind: TypeHint,
942 label: "Smol<u32>",
943 },
944 InlayHint {
945 range: 97..98,
946 kind: TypeHint,
947 label: "VeryLongOuterName<…>",
948 },
949 InlayHint {
950 range: 136..137,
951 kind: TypeHint,
952 label: "Smol<Smol<…>>",
953 },
954 ]
955 "###
956 );
957 } 598 }
958 599
959 #[test] 600 #[test]
960 fn function_call_parameter_hint() { 601 fn function_call_parameter_hint() {
961 let (analysis, file_id) = single_file( 602 check(
962 r#" 603 r#"
963enum CustomOption<T> { 604enum Option<T> { None, Some(T) }
964 None, 605use Option::*;
965 Some(T),
966}
967use CustomOption::*;
968 606
969struct FileId {} 607struct FileId {}
970struct SmolStr {} 608struct SmolStr {}
971 609
972impl From<&str> for SmolStr {
973 fn from(_: &str) -> Self {
974 unimplemented!()
975 }
976}
977
978struct TextRange {} 610struct TextRange {}
979struct SyntaxKind {} 611struct SyntaxKind {}
980struct NavigationTarget {} 612struct NavigationTarget {}
@@ -982,18 +614,15 @@ struct NavigationTarget {}
982struct Test {} 614struct Test {}
983 615
984impl Test { 616impl Test {
985 fn method(&self, mut param: i32) -> i32 { 617 fn method(&self, mut param: i32) -> i32 { param * 2 }
986 param * 2
987 }
988 618
989 fn from_syntax( 619 fn from_syntax(
990 file_id: FileId, 620 file_id: FileId,
991 name: SmolStr, 621 name: SmolStr,
992 focus_range: CustomOption<TextRange>, 622 focus_range: Option<TextRange>,
993 full_range: TextRange, 623 full_range: TextRange,
994 kind: SyntaxKind, 624 kind: SyntaxKind,
995 docs: CustomOption<String>, 625 docs: Option<String>,
996 description: CustomOption<String>,
997 ) -> NavigationTarget { 626 ) -> NavigationTarget {
998 NavigationTarget {} 627 NavigationTarget {}
999 } 628 }
@@ -1005,108 +634,36 @@ fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 {
1005 634
1006fn main() { 635fn main() {
1007 let not_literal = 1; 636 let not_literal = 1;
1008 let _: i32 = test_func(1, 2, "hello", 3, not_literal); 637 //^^^^^^^^^^^ i32
638 let _: i32 = test_func(1, 2, "hello", 3, not_literal);
639 //^ foo ^ bar ^^^^^^^ msg ^^^^^^^^^^^ last
1009 let t: Test = Test {}; 640 let t: Test = Test {};
1010 t.method(123); 641 t.method(123);
1011 Test::method(&t, 3456); 642 //^^^ param
1012 643 Test::method(&t, 3456);
644 //^^ &self ^^^^ param
1013 Test::from_syntax( 645 Test::from_syntax(
1014 FileId {}, 646 FileId {},
647 //^^^^^^^^^ file_id
1015 "impl".into(), 648 "impl".into(),
649 //^^^^^^^^^^^^^ name
1016 None, 650 None,
651 //^^^^ focus_range
1017 TextRange {}, 652 TextRange {},
653 //^^^^^^^^^^^^ full_range
1018 SyntaxKind {}, 654 SyntaxKind {},
655 //^^^^^^^^^^^^^ kind
1019 None, 656 None,
1020 None, 657 //^^^^ docs
1021 ); 658 );
1022}"#, 659}"#,
1023 ); 660 );
1024
1025 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
1026 [
1027 InlayHint {
1028 range: 797..808,
1029 kind: TypeHint,
1030 label: "i32",
1031 },
1032 InlayHint {
1033 range: 841..842,
1034 kind: ParameterHint,
1035 label: "foo",
1036 },
1037 InlayHint {
1038 range: 844..845,
1039 kind: ParameterHint,
1040 label: "bar",
1041 },
1042 InlayHint {
1043 range: 847..854,
1044 kind: ParameterHint,
1045 label: "msg",
1046 },
1047 InlayHint {
1048 range: 859..870,
1049 kind: ParameterHint,
1050 label: "last",
1051 },
1052 InlayHint {
1053 range: 913..916,
1054 kind: ParameterHint,
1055 label: "param",
1056 },
1057 InlayHint {
1058 range: 936..938,
1059 kind: ParameterHint,
1060 label: "&self",
1061 },
1062 InlayHint {
1063 range: 940..944,
1064 kind: ParameterHint,
1065 label: "param",
1066 },
1067 InlayHint {
1068 range: 979..988,
1069 kind: ParameterHint,
1070 label: "file_id",
1071 },
1072 InlayHint {
1073 range: 998..1011,
1074 kind: ParameterHint,
1075 label: "name",
1076 },
1077 InlayHint {
1078 range: 1021..1025,
1079 kind: ParameterHint,
1080 label: "focus_range",
1081 },
1082 InlayHint {
1083 range: 1035..1047,
1084 kind: ParameterHint,
1085 label: "full_range",
1086 },
1087 InlayHint {
1088 range: 1057..1070,
1089 kind: ParameterHint,
1090 label: "kind",
1091 },
1092 InlayHint {
1093 range: 1080..1084,
1094 kind: ParameterHint,
1095 label: "docs",
1096 },
1097 InlayHint {
1098 range: 1094..1098,
1099 kind: ParameterHint,
1100 label: "description",
1101 },
1102 ]
1103 "###
1104 );
1105 } 661 }
1106 662
1107 #[test] 663 #[test]
1108 fn omitted_parameters_hints_heuristics() { 664 fn omitted_parameters_hints_heuristics() {
1109 let (analysis, file_id) = single_file( 665 check_with_config(
666 InlayHintsConfig { max_length: Some(8), ..Default::default() },
1110 r#" 667 r#"
1111fn map(f: i32) {} 668fn map(f: i32) {}
1112fn filter(predicate: i32) {} 669fn filter(predicate: i32) {}
@@ -1188,22 +745,15 @@ fn main() {
1188 let _: f64 = a.abs_sub(b); 745 let _: f64 = a.abs_sub(b);
1189}"#, 746}"#,
1190 ); 747 );
1191
1192 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1193 []
1194 "###
1195 );
1196 } 748 }
1197 749
1198 #[test] 750 #[test]
1199 fn unit_structs_have_no_type_hints() { 751 fn unit_structs_have_no_type_hints() {
1200 let (analysis, file_id) = single_file( 752 check_with_config(
753 InlayHintsConfig { max_length: Some(8), ..Default::default() },
1201 r#" 754 r#"
1202enum CustomResult<T, E> { 755enum Result<T, E> { Ok(T), Err(E) }
1203 Ok(T), 756use Result::*;
1204 Err(E),
1205}
1206use CustomResult::*;
1207 757
1208struct SyntheticSyntax; 758struct SyntheticSyntax;
1209 759
@@ -1214,135 +764,155 @@ fn main() {
1214 } 764 }
1215}"#, 765}"#,
1216 ); 766 );
1217
1218 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1219 []
1220 "###
1221 );
1222 } 767 }
1223 768
1224 #[test] 769 #[test]
1225 fn chaining_hints_ignore_comments() { 770 fn chaining_hints_ignore_comments() {
1226 let (analysis, file_id) = single_file( 771 check_expect(
772 InlayHintsConfig {
773 parameter_hints: false,
774 type_hints: false,
775 chaining_hints: true,
776 max_length: None,
777 },
1227 r#" 778 r#"
1228 struct A(B); 779struct A(B);
1229 impl A { fn into_b(self) -> B { self.0 } } 780impl A { fn into_b(self) -> B { self.0 } }
1230 struct B(C); 781struct B(C);
1231 impl B { fn into_c(self) -> C { self.0 } } 782impl B { fn into_c(self) -> C { self.0 } }
1232 struct C; 783struct C;
1233 784
1234 fn main() { 785fn main() {
1235 let c = A(B(C)) 786 let c = A(B(C))
1236 .into_b() // This is a comment 787 .into_b() // This is a comment
1237 .into_c(); 788 .into_c();
1238 }"#, 789}
790"#,
791 expect![[r#"
792 [
793 InlayHint {
794 range: 147..172,
795 kind: ChainingHint,
796 label: "B",
797 },
798 InlayHint {
799 range: 147..154,
800 kind: ChainingHint,
801 label: "A",
802 },
803 ]
804 "#]],
1239 ); 805 );
1240 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1241 [
1242 InlayHint {
1243 range: 147..172,
1244 kind: ChainingHint,
1245 label: "B",
1246 },
1247 InlayHint {
1248 range: 147..154,
1249 kind: ChainingHint,
1250 label: "A",
1251 },
1252 ]
1253 "###);
1254 } 806 }
1255 807
1256 #[test] 808 #[test]
1257 fn chaining_hints_without_newlines() { 809 fn chaining_hints_without_newlines() {
1258 let (analysis, file_id) = single_file( 810 check_with_config(
811 InlayHintsConfig {
812 parameter_hints: false,
813 type_hints: false,
814 chaining_hints: true,
815 max_length: None,
816 },
1259 r#" 817 r#"
1260 struct A(B); 818struct A(B);
1261 impl A { fn into_b(self) -> B { self.0 } } 819impl A { fn into_b(self) -> B { self.0 } }
1262 struct B(C); 820struct B(C);
1263 impl B { fn into_c(self) -> C { self.0 } } 821impl B { fn into_c(self) -> C { self.0 } }
1264 struct C; 822struct C;
1265 823
1266 fn main() { 824fn main() {
1267 let c = A(B(C)).into_b().into_c(); 825 let c = A(B(C)).into_b().into_c();
1268 }"#, 826}"#,
1269 ); 827 );
1270 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###);
1271 } 828 }
1272 829
1273 #[test] 830 #[test]
1274 fn struct_access_chaining_hints() { 831 fn struct_access_chaining_hints() {
1275 let (analysis, file_id) = single_file( 832 check_expect(
833 InlayHintsConfig {
834 parameter_hints: false,
835 type_hints: false,
836 chaining_hints: true,
837 max_length: None,
838 },
1276 r#" 839 r#"
1277 struct A { pub b: B } 840struct A { pub b: B }
1278 struct B { pub c: C } 841struct B { pub c: C }
1279 struct C(pub bool); 842struct C(pub bool);
1280 struct D; 843struct D;
1281 844
1282 impl D { 845impl D {
1283 fn foo(&self) -> i32 { 42 } 846 fn foo(&self) -> i32 { 42 }
1284 } 847}
1285 848
1286 fn main() { 849fn main() {
1287 let x = A { b: B { c: C(true) } } 850 let x = A { b: B { c: C(true) } }
1288 .b 851 .b
1289 .c 852 .c
1290 .0; 853 .0;
1291 let x = D 854 let x = D
1292 .foo(); 855 .foo();
1293 }"#, 856}"#,
857 expect![[r#"
858 [
859 InlayHint {
860 range: 143..190,
861 kind: ChainingHint,
862 label: "C",
863 },
864 InlayHint {
865 range: 143..179,
866 kind: ChainingHint,
867 label: "B",
868 },
869 ]
870 "#]],
1294 ); 871 );
1295 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1296 [
1297 InlayHint {
1298 range: 143..190,
1299 kind: ChainingHint,
1300 label: "C",
1301 },
1302 InlayHint {
1303 range: 143..179,
1304 kind: ChainingHint,
1305 label: "B",
1306 },
1307 ]
1308 "###);
1309 } 872 }
1310 873
1311 #[test] 874 #[test]
1312 fn generic_chaining_hints() { 875 fn generic_chaining_hints() {
1313 let (analysis, file_id) = single_file( 876 check_expect(
877 InlayHintsConfig {
878 parameter_hints: false,
879 type_hints: false,
880 chaining_hints: true,
881 max_length: None,
882 },
1314 r#" 883 r#"
1315 struct A<T>(T); 884struct A<T>(T);
1316 struct B<T>(T); 885struct B<T>(T);
1317 struct C<T>(T); 886struct C<T>(T);
1318 struct X<T,R>(T, R); 887struct X<T,R>(T, R);
1319 888
1320 impl<T> A<T> { 889impl<T> A<T> {
1321 fn new(t: T) -> Self { A(t) } 890 fn new(t: T) -> Self { A(t) }
1322 fn into_b(self) -> B<T> { B(self.0) } 891 fn into_b(self) -> B<T> { B(self.0) }
1323 } 892}
1324 impl<T> B<T> { 893impl<T> B<T> {
1325 fn into_c(self) -> C<T> { C(self.0) } 894 fn into_c(self) -> C<T> { C(self.0) }
1326 } 895}
1327 fn main() { 896fn main() {
1328 let c = A::new(X(42, true)) 897 let c = A::new(X(42, true))
1329 .into_b() 898 .into_b()
1330 .into_c(); 899 .into_c();
1331 }"#, 900}
901"#,
902 expect![[r#"
903 [
904 InlayHint {
905 range: 246..283,
906 kind: ChainingHint,
907 label: "B<X<i32, bool>>",
908 },
909 InlayHint {
910 range: 246..265,
911 kind: ChainingHint,
912 label: "A<X<i32, bool>>",
913 },
914 ]
915 "#]],
1332 ); 916 );
1333 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1334 [
1335 InlayHint {
1336 range: 246..283,
1337 kind: ChainingHint,
1338 label: "B<X<i32, bool>>",
1339 },
1340 InlayHint {
1341 range: 246..265,
1342 kind: ChainingHint,
1343 label: "A<X<i32, bool>>",
1344 },
1345 ]
1346 "###);
1347 } 917 }
1348} 918}
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index ecac5134e..0fede0d87 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -17,30 +17,33 @@ macro_rules! eprintln {
17 17
18pub mod mock_analysis; 18pub mod mock_analysis;
19 19
20mod markup;
20mod prime_caches; 21mod prime_caches;
21mod status; 22mod display;
23
24mod call_hierarchy;
25mod call_info;
22mod completion; 26mod completion;
23mod runnables; 27mod diagnostics;
28mod expand_macro;
29mod extend_selection;
30mod file_structure;
31mod folding_ranges;
24mod goto_definition; 32mod goto_definition;
25mod goto_type_definition;
26mod goto_implementation; 33mod goto_implementation;
27mod extend_selection; 34mod goto_type_definition;
28mod hover; 35mod hover;
29mod call_hierarchy; 36mod inlay_hints;
30mod call_info; 37mod join_lines;
31mod syntax_highlighting; 38mod matching_brace;
32mod parent_module; 39mod parent_module;
33mod references; 40mod references;
34mod diagnostics; 41mod runnables;
42mod ssr;
43mod status;
44mod syntax_highlighting;
35mod syntax_tree; 45mod syntax_tree;
36mod folding_ranges;
37mod join_lines;
38mod typing; 46mod typing;
39mod matching_brace;
40mod display;
41mod inlay_hints;
42mod expand_macro;
43mod ssr;
44 47
45use std::sync::Arc; 48use std::sync::Arc;
46 49
@@ -59,15 +62,18 @@ use crate::display::ToNav;
59 62
60pub use crate::{ 63pub use crate::{
61 call_hierarchy::CallItem, 64 call_hierarchy::CallItem,
65 call_info::CallInfo,
62 completion::{ 66 completion::{
63 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat, 67 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat,
64 }, 68 },
65 diagnostics::Severity, 69 diagnostics::Severity,
66 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, 70 display::NavigationTarget,
67 expand_macro::ExpandedMacro, 71 expand_macro::ExpandedMacro,
72 file_structure::StructureNode,
68 folding_ranges::{Fold, FoldKind}, 73 folding_ranges::{Fold, FoldKind},
69 hover::{HoverAction, HoverConfig, HoverGotoTypeData, HoverResult}, 74 hover::{HoverAction, HoverConfig, HoverGotoTypeData, HoverResult},
70 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, 75 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
76 markup::Markup,
71 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, 77 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult},
72 runnables::{Runnable, RunnableKind, TestId}, 78 runnables::{Runnable, RunnableKind, TestId},
73 syntax_highlighting::{ 79 syntax_highlighting::{
@@ -75,8 +81,8 @@ pub use crate::{
75 }, 81 },
76}; 82};
77 83
78pub use hir::Documentation; 84pub use hir::{Documentation, Semantics};
79pub use ra_assists::{Assist, AssistConfig, AssistId, ResolvedAssist}; 85pub use ra_assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist};
80pub use ra_db::{ 86pub use ra_db::{
81 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, 87 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot,
82 SourceRootId, 88 SourceRootId,
@@ -129,14 +135,6 @@ impl<T> RangeInfo<T> {
129 } 135 }
130} 136}
131 137
132/// Contains information about a call site. Specifically the
133/// `FunctionSignature`and current parameter.
134#[derive(Debug)]
135pub struct CallInfo {
136 pub signature: FunctionSignature,
137 pub active_parameter: Option<usize>,
138}
139
140/// `AnalysisHost` stores the current state of the world. 138/// `AnalysisHost` stores the current state of the world.
141#[derive(Debug)] 139#[derive(Debug)]
142pub struct AnalysisHost { 140pub struct AnalysisHost {
@@ -328,7 +326,7 @@ impl Analysis {
328 /// Returns a tree representation of symbols in the file. Useful to draw a 326 /// Returns a tree representation of symbols in the file. Useful to draw a
329 /// file outline. 327 /// file outline.
330 pub fn file_structure(&self, file_id: FileId) -> Cancelable<Vec<StructureNode>> { 328 pub fn file_structure(&self, file_id: FileId) -> Cancelable<Vec<StructureNode>> {
331 self.with_db(|db| file_structure(&db.parse(file_id).tree())) 329 self.with_db(|db| file_structure::file_structure(&db.parse(file_id).tree()))
332 } 330 }
333 331
334 /// Returns a list of the places in the file where type hints can be displayed. 332 /// Returns a list of the places in the file where type hints can be displayed.
@@ -385,7 +383,9 @@ impl Analysis {
385 position: FilePosition, 383 position: FilePosition,
386 search_scope: Option<SearchScope>, 384 search_scope: Option<SearchScope>,
387 ) -> Cancelable<Option<ReferenceSearchResult>> { 385 ) -> Cancelable<Option<ReferenceSearchResult>> {
388 self.with_db(|db| references::find_all_refs(db, position, search_scope).map(|it| it.info)) 386 self.with_db(|db| {
387 references::find_all_refs(&Semantics::new(db), position, search_scope).map(|it| it.info)
388 })
389 } 389 }
390 390
391 /// Returns a short text describing element at position. 391 /// Returns a short text describing element at position.
@@ -487,8 +487,12 @@ impl Analysis {
487 } 487 }
488 488
489 /// Computes the set of diagnostics for the given file. 489 /// Computes the set of diagnostics for the given file.
490 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { 490 pub fn diagnostics(
491 self.with_db(|db| diagnostics::diagnostics(db, file_id)) 491 &self,
492 file_id: FileId,
493 enable_experimental: bool,
494 ) -> Cancelable<Vec<Diagnostic>> {
495 self.with_db(|db| diagnostics::diagnostics(db, file_id, enable_experimental))
492 } 496 }
493 497
494 /// Returns the edit required to rename reference at the position to the new 498 /// Returns the edit required to rename reference at the position to the new
@@ -505,9 +509,11 @@ impl Analysis {
505 &self, 509 &self,
506 query: &str, 510 query: &str,
507 parse_only: bool, 511 parse_only: bool,
512 position: FilePosition,
513 selections: Vec<FileRange>,
508 ) -> Cancelable<Result<SourceChange, SsrError>> { 514 ) -> Cancelable<Result<SourceChange, SsrError>> {
509 self.with_db(|db| { 515 self.with_db(|db| {
510 let edits = ssr::parse_search_replace(query, parse_only, db)?; 516 let edits = ssr::parse_search_replace(query, parse_only, db, position, selections)?;
511 Ok(SourceChange::from(edits)) 517 Ok(SourceChange::from(edits))
512 }) 518 })
513 } 519 }
@@ -526,77 +532,3 @@ fn analysis_is_send() {
526 fn is_send<T: Send>() {} 532 fn is_send<T: Send>() {}
527 is_send::<Analysis>(); 533 is_send::<Analysis>();
528} 534}
529
530#[cfg(test)]
531mod tests {
532 use crate::{display::NavigationTarget, mock_analysis::single_file, Query};
533 use ra_syntax::{
534 SmolStr,
535 SyntaxKind::{FN_DEF, STRUCT_DEF},
536 };
537
538 #[test]
539 fn test_world_symbols_with_no_container() {
540 let code = r#"
541 enum FooInner { }
542 "#;
543
544 let mut symbols = get_symbols_matching(code, "FooInner");
545
546 let s = symbols.pop().unwrap();
547
548 assert_eq!(s.name(), "FooInner");
549 assert!(s.container_name().is_none());
550 }
551
552 #[test]
553 fn test_world_symbols_include_container_name() {
554 let code = r#"
555fn foo() {
556 enum FooInner { }
557}
558 "#;
559
560 let mut symbols = get_symbols_matching(code, "FooInner");
561
562 let s = symbols.pop().unwrap();
563
564 assert_eq!(s.name(), "FooInner");
565 assert_eq!(s.container_name(), Some(&SmolStr::new("foo")));
566
567 let code = r#"
568mod foo {
569 struct FooInner;
570}
571 "#;
572
573 let mut symbols = get_symbols_matching(code, "FooInner");
574
575 let s = symbols.pop().unwrap();
576
577 assert_eq!(s.name(), "FooInner");
578 assert_eq!(s.container_name(), Some(&SmolStr::new("foo")));
579 }
580
581 #[test]
582 fn test_world_symbols_are_case_sensitive() {
583 let code = r#"
584fn foo() {}
585
586struct Foo;
587 "#;
588
589 let symbols = get_symbols_matching(code, "Foo");
590
591 let fn_match = symbols.iter().find(|s| s.name() == "foo").map(|s| s.kind());
592 let struct_match = symbols.iter().find(|s| s.name() == "Foo").map(|s| s.kind());
593
594 assert_eq!(fn_match, Some(FN_DEF));
595 assert_eq!(struct_match, Some(STRUCT_DEF));
596 }
597
598 fn get_symbols_matching(text: &str, query: &str) -> Vec<NavigationTarget> {
599 let (analysis, _) = single_file(text);
600 analysis.symbol_search(Query::new(query.into())).unwrap()
601 }
602}
diff --git a/crates/ra_ide/src/markup.rs b/crates/ra_ide/src/markup.rs
new file mode 100644
index 000000000..60c193c40
--- /dev/null
+++ b/crates/ra_ide/src/markup.rs
@@ -0,0 +1,38 @@
1//! Markdown formatting.
2//!
3//! Sometimes, we want to display a "rich text" in the UI. At the moment, we use
4//! markdown for this purpose. It doesn't feel like a right option, but that's
5//! what is used by LSP, so let's keep it simple.
6use std::fmt;
7
8#[derive(Default, Debug)]
9pub struct Markup {
10 text: String,
11}
12
13impl From<Markup> for String {
14 fn from(markup: Markup) -> Self {
15 markup.text
16 }
17}
18
19impl From<String> for Markup {
20 fn from(text: String) -> Self {
21 Markup { text }
22 }
23}
24
25impl fmt::Display for Markup {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 fmt::Display::fmt(&self.text, f)
28 }
29}
30
31impl Markup {
32 pub fn as_str(&self) -> &str {
33 self.text.as_str()
34 }
35 pub fn fenced_block(contents: &impl fmt::Display) -> Markup {
36 format!("```rust\n{}\n```", contents).into()
37 }
38}
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs
index 490ee0dc3..cf2ee1bfa 100644
--- a/crates/ra_ide/src/mock_analysis.rs
+++ b/crates/ra_ide/src/mock_analysis.rs
@@ -2,8 +2,10 @@
2use std::sync::Arc; 2use std::sync::Arc;
3 3
4use ra_cfg::CfgOptions; 4use ra_cfg::CfgOptions;
5use ra_db::{CrateName, Env, FileSet, SourceRoot, VfsPath}; 5use ra_db::{CrateName, FileSet, SourceRoot, VfsPath};
6use test_utils::{extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER}; 6use test_utils::{
7 extract_annotations, extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER,
8};
7 9
8use crate::{ 10use crate::{
9 Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange, 11 Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange,
@@ -69,13 +71,27 @@ impl MockAnalysis {
69 } 71 }
70 72
71 pub fn id_of(&self, path: &str) -> FileId { 73 pub fn id_of(&self, path: &str) -> FileId {
72 let (idx, _) = self 74 let (file_id, _) =
73 .files 75 self.files().find(|(_, data)| path == data.path).expect("no file in this mock");
74 .iter() 76 file_id
75 .enumerate() 77 }
76 .find(|(_, data)| path == data.path) 78 pub fn annotations(&self) -> Vec<(FileRange, String)> {
77 .expect("no file in this mock"); 79 self.files()
78 FileId(idx as u32 + 1) 80 .flat_map(|(file_id, fixture)| {
81 let annotations = extract_annotations(&fixture.text);
82 annotations
83 .into_iter()
84 .map(move |(range, data)| (FileRange { file_id, range }, data))
85 })
86 .collect()
87 }
88 pub fn files(&self) -> impl Iterator<Item = (FileId, &Fixture)> + '_ {
89 self.files.iter().enumerate().map(|(idx, fixture)| (FileId(idx as u32 + 1), fixture))
90 }
91 pub fn annotation(&self) -> (FileRange, String) {
92 let mut all = self.annotations();
93 assert_eq!(all.len(), 1);
94 all.pop().unwrap()
79 } 95 }
80 pub fn analysis_host(self) -> AnalysisHost { 96 pub fn analysis_host(self) -> AnalysisHost {
81 let mut host = AnalysisHost::default(); 97 let mut host = AnalysisHost::default();
@@ -94,12 +110,12 @@ impl MockAnalysis {
94 data.edition.and_then(|it| it.parse().ok()).unwrap_or(Edition::Edition2018); 110 data.edition.and_then(|it| it.parse().ok()).unwrap_or(Edition::Edition2018);
95 111
96 let file_id = FileId(i as u32 + 1); 112 let file_id = FileId(i as u32 + 1);
97 let env = Env::from(data.env.iter()); 113 let env = data.env.into_iter().collect();
98 if path == "/lib.rs" || path == "/main.rs" { 114 if path == "/lib.rs" || path == "/main.rs" {
99 root_crate = Some(crate_graph.add_crate_root( 115 root_crate = Some(crate_graph.add_crate_root(
100 file_id, 116 file_id,
101 edition, 117 edition,
102 Some(CrateName::new("test").unwrap()), 118 Some("test".to_string()),
103 cfg, 119 cfg,
104 env, 120 env,
105 Default::default(), 121 Default::default(),
@@ -110,7 +126,7 @@ impl MockAnalysis {
110 let other_crate = crate_graph.add_crate_root( 126 let other_crate = crate_graph.add_crate_root(
111 file_id, 127 file_id,
112 edition, 128 edition,
113 Some(CrateName::new(crate_name).unwrap()), 129 Some(crate_name.to_string()),
114 cfg, 130 cfg,
115 env, 131 env,
116 Default::default(), 132 Default::default(),
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index 3433fdae3..519e4bf1a 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -74,8 +74,8 @@ impl IntoIterator for ReferenceSearchResult {
74 let mut v = Vec::with_capacity(self.len()); 74 let mut v = Vec::with_capacity(self.len());
75 v.push(Reference { 75 v.push(Reference {
76 file_range: FileRange { 76 file_range: FileRange {
77 file_id: self.declaration.nav.file_id(), 77 file_id: self.declaration.nav.file_id,
78 range: self.declaration.nav.range(), 78 range: self.declaration.nav.focus_or_full_range(),
79 }, 79 },
80 kind: self.declaration.kind, 80 kind: self.declaration.kind,
81 access: self.declaration.access, 81 access: self.declaration.access,
@@ -86,12 +86,11 @@ impl IntoIterator for ReferenceSearchResult {
86} 86}
87 87
88pub(crate) fn find_all_refs( 88pub(crate) fn find_all_refs(
89 db: &RootDatabase, 89 sema: &Semantics<RootDatabase>,
90 position: FilePosition, 90 position: FilePosition,
91 search_scope: Option<SearchScope>, 91 search_scope: Option<SearchScope>,
92) -> Option<RangeInfo<ReferenceSearchResult>> { 92) -> Option<RangeInfo<ReferenceSearchResult>> {
93 let _p = profile("find_all_refs"); 93 let _p = profile("find_all_refs");
94 let sema = Semantics::new(db);
95 let syntax = sema.parse(position.file_id).syntax().clone(); 94 let syntax = sema.parse(position.file_id).syntax().clone();
96 95
97 let (opt_name, search_kind) = if let Some(name) = 96 let (opt_name, search_kind) = if let Some(name) =
@@ -108,15 +107,15 @@ pub(crate) fn find_all_refs(
108 let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; 107 let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?;
109 108
110 let references = def 109 let references = def
111 .find_usages(db, search_scope) 110 .find_usages(sema, search_scope)
112 .into_iter() 111 .into_iter()
113 .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) 112 .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)
114 .collect(); 113 .collect();
115 114
116 let decl_range = def.try_to_nav(db)?.range(); 115 let decl_range = def.try_to_nav(sema.db)?.focus_or_full_range();
117 116
118 let declaration = Declaration { 117 let declaration = Declaration {
119 nav: def.try_to_nav(db)?, 118 nav: def.try_to_nav(sema.db)?,
120 kind: ReferenceKind::Other, 119 kind: ReferenceKind::Other,
121 access: decl_access(&def, &syntax, decl_range), 120 access: decl_access(&def, &syntax, decl_range),
122 }; 121 };
@@ -173,16 +172,16 @@ fn get_struct_def_name_for_struct_literal_search(
173 if let Some(name) = 172 if let Some(name) =
174 sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, left.text_range().start()) 173 sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, left.text_range().start())
175 { 174 {
176 return name.syntax().ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name()); 175 return name.syntax().ancestors().find_map(ast::Struct::cast).and_then(|l| l.name());
177 } 176 }
178 if sema 177 if sema
179 .find_node_at_offset_with_descend::<ast::TypeParamList>( 178 .find_node_at_offset_with_descend::<ast::GenericParamList>(
180 &syntax, 179 &syntax,
181 left.text_range().start(), 180 left.text_range().start(),
182 ) 181 )
183 .is_some() 182 .is_some()
184 { 183 {
185 return left.ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name()); 184 return left.ancestors().find_map(ast::Struct::cast).and_then(|l| l.name());
186 } 185 }
187 } 186 }
188 None 187 None
@@ -213,7 +212,7 @@ fn main() {
213 ); 212 );
214 check_result( 213 check_result(
215 refs, 214 refs,
216 "Foo STRUCT_DEF FileId(1) 0..26 7..10 Other", 215 "Foo STRUCT FileId(1) 0..26 7..10 Other",
217 &["FileId(1) 101..104 StructLiteral"], 216 &["FileId(1) 101..104 StructLiteral"],
218 ); 217 );
219 } 218 }
@@ -231,7 +230,7 @@ struct Foo<|> {}
231 ); 230 );
232 check_result( 231 check_result(
233 refs, 232 refs,
234 "Foo STRUCT_DEF FileId(1) 0..13 7..10 Other", 233 "Foo STRUCT FileId(1) 0..13 7..10 Other",
235 &["FileId(1) 41..44 Other", "FileId(1) 54..57 StructLiteral"], 234 &["FileId(1) 41..44 Other", "FileId(1) 54..57 StructLiteral"],
236 ); 235 );
237 } 236 }
@@ -249,7 +248,7 @@ struct Foo<T> <|>{}
249 ); 248 );
250 check_result( 249 check_result(
251 refs, 250 refs,
252 "Foo STRUCT_DEF FileId(1) 0..16 7..10 Other", 251 "Foo STRUCT FileId(1) 0..16 7..10 Other",
253 &["FileId(1) 64..67 StructLiteral"], 252 &["FileId(1) 64..67 StructLiteral"],
254 ); 253 );
255 } 254 }
@@ -268,7 +267,7 @@ fn main() {
268 ); 267 );
269 check_result( 268 check_result(
270 refs, 269 refs,
271 "Foo STRUCT_DEF FileId(1) 0..16 7..10 Other", 270 "Foo STRUCT FileId(1) 0..16 7..10 Other",
272 &["FileId(1) 54..57 StructLiteral"], 271 &["FileId(1) 54..57 StructLiteral"],
273 ); 272 );
274 } 273 }
@@ -362,7 +361,7 @@ fn main(s: Foo) {
362 ); 361 );
363 check_result( 362 check_result(
364 refs, 363 refs,
365 "spam RECORD_FIELD_DEF FileId(1) 17..30 21..25 Other", 364 "spam RECORD_FIELD FileId(1) 17..30 21..25 Other",
366 &["FileId(1) 67..71 Other Read"], 365 &["FileId(1) 67..71 Other Read"],
367 ); 366 );
368 } 367 }
@@ -377,7 +376,7 @@ impl Foo {
377} 376}
378"#, 377"#,
379 ); 378 );
380 check_result(refs, "f FN_DEF FileId(1) 27..43 30..31 Other", &[]); 379 check_result(refs, "f FN FileId(1) 27..43 30..31 Other", &[]);
381 } 380 }
382 381
383 #[test] 382 #[test]
@@ -391,7 +390,7 @@ enum Foo {
391} 390}
392"#, 391"#,
393 ); 392 );
394 check_result(refs, "B ENUM_VARIANT FileId(1) 22..23 22..23 Other", &[]); 393 check_result(refs, "B VARIANT FileId(1) 22..23 22..23 Other", &[]);
395 } 394 }
396 395
397 #[test] 396 #[test]
@@ -432,7 +431,7 @@ fn f() {
432 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 431 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
433 check_result( 432 check_result(
434 refs, 433 refs,
435 "Foo STRUCT_DEF FileId(2) 17..51 28..31 Other", 434 "Foo STRUCT FileId(2) 17..51 28..31 Other",
436 &["FileId(1) 53..56 StructLiteral", "FileId(3) 79..82 StructLiteral"], 435 &["FileId(1) 53..56 StructLiteral", "FileId(3) 79..82 StructLiteral"],
437 ); 436 );
438 } 437 }
@@ -487,7 +486,7 @@ pub(super) struct Foo<|> {
487 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 486 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
488 check_result( 487 check_result(
489 refs, 488 refs,
490 "Foo STRUCT_DEF FileId(3) 0..41 18..21 Other", 489 "Foo STRUCT FileId(3) 0..41 18..21 Other",
491 &["FileId(2) 20..23 Other", "FileId(2) 47..50 StructLiteral"], 490 &["FileId(2) 20..23 Other", "FileId(2) 47..50 StructLiteral"],
492 ); 491 );
493 } 492 }
@@ -515,7 +514,7 @@ pub(super) struct Foo<|> {
515 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 514 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
516 check_result( 515 check_result(
517 refs, 516 refs,
518 "quux FN_DEF FileId(1) 19..35 26..30 Other", 517 "quux FN FileId(1) 19..35 26..30 Other",
519 &["FileId(2) 16..20 StructLiteral", "FileId(3) 16..20 StructLiteral"], 518 &["FileId(2) 16..20 StructLiteral", "FileId(3) 16..20 StructLiteral"],
520 ); 519 );
521 520
@@ -523,7 +522,7 @@ pub(super) struct Foo<|> {
523 analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap(); 522 analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap();
524 check_result( 523 check_result(
525 refs, 524 refs,
526 "quux FN_DEF FileId(1) 19..35 26..30 Other", 525 "quux FN FileId(1) 19..35 26..30 Other",
527 &["FileId(3) 16..20 StructLiteral"], 526 &["FileId(3) 16..20 StructLiteral"],
528 ); 527 );
529 } 528 }
@@ -581,7 +580,7 @@ fn foo() {
581 ); 580 );
582 check_result( 581 check_result(
583 refs, 582 refs,
584 "f RECORD_FIELD_DEF FileId(1) 15..21 15..16 Other", 583 "f RECORD_FIELD FileId(1) 15..21 15..16 Other",
585 &["FileId(1) 55..56 Other Read", "FileId(1) 68..69 Other Write"], 584 &["FileId(1) 55..56 Other Read", "FileId(1) 68..69 Other Write"],
586 ); 585 );
587 } 586 }
@@ -620,7 +619,7 @@ fn main() {
620 ); 619 );
621 check_result( 620 check_result(
622 refs, 621 refs,
623 "new FN_DEF FileId(1) 54..101 61..64 Other", 622 "new FN FileId(1) 54..101 61..64 Other",
624 &["FileId(1) 146..149 StructLiteral"], 623 &["FileId(1) 146..149 StructLiteral"],
625 ); 624 );
626 } 625 }
@@ -647,7 +646,7 @@ fn main() {
647 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 646 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
648 check_result( 647 check_result(
649 refs, 648 refs,
650 "f FN_DEF FileId(1) 26..35 29..30 Other", 649 "f FN FileId(1) 26..35 29..30 Other",
651 &["FileId(2) 11..12 Other", "FileId(2) 28..29 StructLiteral"], 650 &["FileId(2) 11..12 Other", "FileId(2) 28..29 StructLiteral"],
652 ); 651 );
653 } 652 }
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 7ebc0adcf..31654bf79 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -7,7 +7,8 @@ use ra_ide_db::{
7 RootDatabase, 7 RootDatabase,
8}; 8};
9use ra_syntax::{ 9use ra_syntax::{
10 algo::find_node_at_offset, ast, ast::NameOwner, ast::TypeAscriptionOwner, 10 algo::find_node_at_offset,
11 ast::{self, NameOwner},
11 lex_single_valid_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken, 12 lex_single_valid_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken,
12}; 13};
13use ra_text_edit::TextEdit; 14use ra_text_edit::TextEdit;
@@ -24,23 +25,24 @@ pub(crate) fn rename(
24 position: FilePosition, 25 position: FilePosition,
25 new_name: &str, 26 new_name: &str,
26) -> Option<RangeInfo<SourceChange>> { 27) -> Option<RangeInfo<SourceChange>> {
28 let sema = Semantics::new(db);
29
27 match lex_single_valid_syntax_kind(new_name)? { 30 match lex_single_valid_syntax_kind(new_name)? {
28 SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (), 31 SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (),
29 SyntaxKind::SELF_KW => return rename_to_self(db, position), 32 SyntaxKind::SELF_KW => return rename_to_self(&sema, position),
30 _ => return None, 33 _ => return None,
31 } 34 }
32 35
33 let sema = Semantics::new(db);
34 let source_file = sema.parse(position.file_id); 36 let source_file = sema.parse(position.file_id);
35 let syntax = source_file.syntax(); 37 let syntax = source_file.syntax();
36 if let Some(module) = find_module_at_offset(&sema, position, syntax) { 38 if let Some(module) = find_module_at_offset(&sema, position, syntax) {
37 rename_mod(db, position, module, new_name) 39 rename_mod(&sema, position, module, new_name)
38 } else if let Some(self_token) = 40 } else if let Some(self_token) =
39 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) 41 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
40 { 42 {
41 rename_self_to_param(db, position, self_token, new_name) 43 rename_self_to_param(&sema, position, self_token, new_name)
42 } else { 44 } else {
43 rename_reference(sema.db, position, new_name) 45 rename_reference(&sema, position, new_name)
44 } 46 }
45} 47}
46 48
@@ -97,7 +99,7 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil
97} 99}
98 100
99fn rename_mod( 101fn rename_mod(
100 db: &RootDatabase, 102 sema: &Semantics<RootDatabase>,
101 position: FilePosition, 103 position: FilePosition,
102 module: Module, 104 module: Module,
103 new_name: &str, 105 new_name: &str,
@@ -105,34 +107,33 @@ fn rename_mod(
105 let mut source_file_edits = Vec::new(); 107 let mut source_file_edits = Vec::new();
106 let mut file_system_edits = Vec::new(); 108 let mut file_system_edits = Vec::new();
107 109
108 let src = module.definition_source(db); 110 let src = module.definition_source(sema.db);
109 let file_id = src.file_id.original_file(db); 111 let file_id = src.file_id.original_file(sema.db);
110 match src.value { 112 match src.value {
111 ModuleSource::SourceFile(..) => { 113 ModuleSource::SourceFile(..) => {
112 // mod is defined in path/to/dir/mod.rs 114 // mod is defined in path/to/dir/mod.rs
113 let dst = if module.is_mod_rs(db) { 115 let dst = if module.is_mod_rs(sema.db) {
114 format!("../{}/mod.rs", new_name) 116 format!("../{}/mod.rs", new_name)
115 } else { 117 } else {
116 format!("{}.rs", new_name) 118 format!("{}.rs", new_name)
117 }; 119 };
118 let move_file = 120 let move_file = FileSystemEdit::MoveFile { src: file_id, anchor: file_id, dst };
119 FileSystemEdit::MoveFile { src: file_id, anchor: position.file_id, dst };
120 file_system_edits.push(move_file); 121 file_system_edits.push(move_file);
121 } 122 }
122 ModuleSource::Module(..) => {} 123 ModuleSource::Module(..) => {}
123 } 124 }
124 125
125 if let Some(src) = module.declaration_source(db) { 126 if let Some(src) = module.declaration_source(sema.db) {
126 let file_id = src.file_id.original_file(db); 127 let file_id = src.file_id.original_file(sema.db);
127 let name = src.value.name()?; 128 let name = src.value.name()?;
128 let edit = SourceFileEdit { 129 let edit = SourceFileEdit {
129 file_id: file_id, 130 file_id,
130 edit: TextEdit::replace(name.syntax().text_range(), new_name.into()), 131 edit: TextEdit::replace(name.syntax().text_range(), new_name.into()),
131 }; 132 };
132 source_file_edits.push(edit); 133 source_file_edits.push(edit);
133 } 134 }
134 135
135 let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; 136 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
136 let ref_edits = refs 137 let ref_edits = refs
137 .references 138 .references
138 .into_iter() 139 .into_iter()
@@ -142,23 +143,25 @@ fn rename_mod(
142 Some(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) 143 Some(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
143} 144}
144 145
145fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { 146fn rename_to_self(
146 let sema = Semantics::new(db); 147 sema: &Semantics<RootDatabase>,
148 position: FilePosition,
149) -> Option<RangeInfo<SourceChange>> {
147 let source_file = sema.parse(position.file_id); 150 let source_file = sema.parse(position.file_id);
148 let syn = source_file.syntax(); 151 let syn = source_file.syntax();
149 152
150 let fn_def = find_node_at_offset::<ast::FnDef>(syn, position.offset)?; 153 let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset)?;
151 let params = fn_def.param_list()?; 154 let params = fn_def.param_list()?;
152 if params.self_param().is_some() { 155 if params.self_param().is_some() {
153 return None; // method already has self param 156 return None; // method already has self param
154 } 157 }
155 let first_param = params.params().next()?; 158 let first_param = params.params().next()?;
156 let mutable = match first_param.ascribed_type() { 159 let mutable = match first_param.ty() {
157 Some(ast::TypeRef::ReferenceType(rt)) => rt.mut_token().is_some(), 160 Some(ast::TypeRef::ReferenceType(rt)) => rt.mut_token().is_some(),
158 _ => return None, // not renaming other types 161 _ => return None, // not renaming other types
159 }; 162 };
160 163
161 let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; 164 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
162 165
163 let param_range = first_param.syntax().text_range(); 166 let param_range = first_param.syntax().text_range();
164 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs 167 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs
@@ -190,15 +193,14 @@ fn text_edit_from_self_param(
190 self_param: &ast::SelfParam, 193 self_param: &ast::SelfParam,
191 new_name: &str, 194 new_name: &str,
192) -> Option<TextEdit> { 195) -> Option<TextEdit> {
193 fn target_type_name(impl_def: &ast::ImplDef) -> Option<String> { 196 fn target_type_name(impl_def: &ast::Impl) -> Option<String> {
194 if let Some(ast::TypeRef::PathType(p)) = impl_def.target_type() { 197 if let Some(ast::TypeRef::PathType(p)) = impl_def.target_type() {
195 return Some(p.path()?.segment()?.name_ref()?.text().to_string()); 198 return Some(p.path()?.segment()?.name_ref()?.text().to_string());
196 } 199 }
197 None 200 None
198 } 201 }
199 202
200 let impl_def = 203 let impl_def = find_node_at_offset::<ast::Impl>(syn, self_param.syntax().text_range().start())?;
201 find_node_at_offset::<ast::ImplDef>(syn, self_param.syntax().text_range().start())?;
202 let type_name = target_type_name(&impl_def)?; 204 let type_name = target_type_name(&impl_def)?;
203 205
204 let mut replacement_text = String::from(new_name); 206 let mut replacement_text = String::from(new_name);
@@ -210,17 +212,16 @@ fn text_edit_from_self_param(
210} 212}
211 213
212fn rename_self_to_param( 214fn rename_self_to_param(
213 db: &RootDatabase, 215 sema: &Semantics<RootDatabase>,
214 position: FilePosition, 216 position: FilePosition,
215 self_token: SyntaxToken, 217 self_token: SyntaxToken,
216 new_name: &str, 218 new_name: &str,
217) -> Option<RangeInfo<SourceChange>> { 219) -> Option<RangeInfo<SourceChange>> {
218 let sema = Semantics::new(db);
219 let source_file = sema.parse(position.file_id); 220 let source_file = sema.parse(position.file_id);
220 let syn = source_file.syntax(); 221 let syn = source_file.syntax();
221 222
222 let text = db.file_text(position.file_id); 223 let text = sema.db.file_text(position.file_id);
223 let fn_def = find_node_at_offset::<ast::FnDef>(syn, position.offset)?; 224 let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset)?;
224 let search_range = fn_def.syntax().text_range(); 225 let search_range = fn_def.syntax().text_range();
225 226
226 let mut edits: Vec<SourceFileEdit> = vec![]; 227 let mut edits: Vec<SourceFileEdit> = vec![];
@@ -249,11 +250,11 @@ fn rename_self_to_param(
249} 250}
250 251
251fn rename_reference( 252fn rename_reference(
252 db: &RootDatabase, 253 sema: &Semantics<RootDatabase>,
253 position: FilePosition, 254 position: FilePosition,
254 new_name: &str, 255 new_name: &str,
255) -> Option<RangeInfo<SourceChange>> { 256) -> Option<RangeInfo<SourceChange>> {
256 let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; 257 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
257 258
258 let edit = refs 259 let edit = refs
259 .into_iter() 260 .into_iter()
@@ -269,51 +270,51 @@ fn rename_reference(
269 270
270#[cfg(test)] 271#[cfg(test)]
271mod tests { 272mod tests {
272 use insta::assert_debug_snapshot; 273 use expect::{expect, Expect};
273 use ra_text_edit::TextEditBuilder; 274 use ra_text_edit::TextEditBuilder;
274 use stdx::trim_indent; 275 use stdx::trim_indent;
275 use test_utils::{assert_eq_text, mark}; 276 use test_utils::{assert_eq_text, mark};
276 277
277 use crate::{mock_analysis::analysis_and_position, FileId}; 278 use crate::{mock_analysis::analysis_and_position, FileId};
278 279
280 fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
281 let ra_fixture_after = &trim_indent(ra_fixture_after);
282 let (analysis, position) = analysis_and_position(ra_fixture_before);
283 let source_change = analysis.rename(position, new_name).unwrap();
284 let mut text_edit_builder = TextEditBuilder::default();
285 let mut file_id: Option<FileId> = None;
286 if let Some(change) = source_change {
287 for edit in change.info.source_file_edits {
288 file_id = Some(edit.file_id);
289 for indel in edit.edit.into_iter() {
290 text_edit_builder.replace(indel.delete, indel.insert);
291 }
292 }
293 }
294 let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string();
295 text_edit_builder.finish().apply(&mut result);
296 assert_eq_text!(ra_fixture_after, &*result);
297 }
298
299 fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) {
300 let (analysis, position) = analysis_and_position(ra_fixture);
301 let source_change = analysis.rename(position, new_name).unwrap().unwrap();
302 expect.assert_debug_eq(&source_change)
303 }
304
279 #[test] 305 #[test]
280 fn test_rename_to_underscore() { 306 fn test_rename_to_underscore() {
281 test_rename( 307 check("_", r#"fn main() { let i<|> = 1; }"#, r#"fn main() { let _ = 1; }"#);
282 r#"
283 fn main() {
284 let i<|> = 1;
285 }"#,
286 "_",
287 r#"
288 fn main() {
289 let _ = 1;
290 }"#,
291 );
292 } 308 }
293 309
294 #[test] 310 #[test]
295 fn test_rename_to_raw_identifier() { 311 fn test_rename_to_raw_identifier() {
296 test_rename( 312 check("r#fn", r#"fn main() { let i<|> = 1; }"#, r#"fn main() { let r#fn = 1; }"#);
297 r#"
298 fn main() {
299 let i<|> = 1;
300 }"#,
301 "r#fn",
302 r#"
303 fn main() {
304 let r#fn = 1;
305 }"#,
306 );
307 } 313 }
308 314
309 #[test] 315 #[test]
310 fn test_rename_to_invalid_identifier() { 316 fn test_rename_to_invalid_identifier() {
311 let (analysis, position) = analysis_and_position( 317 let (analysis, position) = analysis_and_position(r#"fn main() { let i<|> = 1; }"#);
312 "
313 fn main() {
314 let i<|> = 1;
315 }",
316 );
317 let new_name = "invalid!"; 318 let new_name = "invalid!";
318 let source_change = analysis.rename(position, new_name).unwrap(); 319 let source_change = analysis.rename(position, new_name).unwrap();
319 assert!(source_change.is_none()); 320 assert!(source_change.is_none());
@@ -321,318 +322,269 @@ mod tests {
321 322
322 #[test] 323 #[test]
323 fn test_rename_for_local() { 324 fn test_rename_for_local() {
324 test_rename( 325 check(
326 "k",
325 r#" 327 r#"
326 fn main() { 328fn main() {
327 let mut i = 1; 329 let mut i = 1;
328 let j = 1; 330 let j = 1;
329 i = i<|> + j; 331 i = i<|> + j;
330 332
331 { 333 { i = 0; }
332 i = 0;
333 }
334 334
335 i = 5; 335 i = 5;
336 }"#, 336}
337 "k", 337"#,
338 r#" 338 r#"
339 fn main() { 339fn main() {
340 let mut k = 1; 340 let mut k = 1;
341 let j = 1; 341 let j = 1;
342 k = k + j; 342 k = k + j;
343 343
344 { 344 { k = 0; }
345 k = 0;
346 }
347 345
348 k = 5; 346 k = 5;
349 }"#, 347}
348"#,
350 ); 349 );
351 } 350 }
352 351
353 #[test] 352 #[test]
354 fn test_rename_for_macro_args() { 353 fn test_rename_for_macro_args() {
355 test_rename( 354 check(
356 r#"
357 macro_rules! foo {($i:ident) => {$i} }
358 fn main() {
359 let a<|> = "test";
360 foo!(a);
361 }"#,
362 "b", 355 "b",
363 r#" 356 r#"
364 macro_rules! foo {($i:ident) => {$i} } 357macro_rules! foo {($i:ident) => {$i} }
365 fn main() { 358fn main() {
366 let b = "test"; 359 let a<|> = "test";
367 foo!(b); 360 foo!(a);
368 }"#, 361}
362"#,
363 r#"
364macro_rules! foo {($i:ident) => {$i} }
365fn main() {
366 let b = "test";
367 foo!(b);
368}
369"#,
369 ); 370 );
370 } 371 }
371 372
372 #[test] 373 #[test]
373 fn test_rename_for_macro_args_rev() { 374 fn test_rename_for_macro_args_rev() {
374 test_rename( 375 check(
375 r#"
376 macro_rules! foo {($i:ident) => {$i} }
377 fn main() {
378 let a = "test";
379 foo!(a<|>);
380 }"#,
381 "b", 376 "b",
382 r#" 377 r#"
383 macro_rules! foo {($i:ident) => {$i} } 378macro_rules! foo {($i:ident) => {$i} }
384 fn main() { 379fn main() {
385 let b = "test"; 380 let a = "test";
386 foo!(b); 381 foo!(a<|>);
387 }"#, 382}
383"#,
384 r#"
385macro_rules! foo {($i:ident) => {$i} }
386fn main() {
387 let b = "test";
388 foo!(b);
389}
390"#,
388 ); 391 );
389 } 392 }
390 393
391 #[test] 394 #[test]
392 fn test_rename_for_macro_define_fn() { 395 fn test_rename_for_macro_define_fn() {
393 test_rename( 396 check(
394 r#"
395 macro_rules! define_fn {($id:ident) => { fn $id{} }}
396 define_fn!(foo);
397 fn main() {
398 fo<|>o();
399 }"#,
400 "bar", 397 "bar",
401 r#" 398 r#"
402 macro_rules! define_fn {($id:ident) => { fn $id{} }} 399macro_rules! define_fn {($id:ident) => { fn $id{} }}
403 define_fn!(bar); 400define_fn!(foo);
404 fn main() { 401fn main() {
405 bar(); 402 fo<|>o();
406 }"#, 403}
404"#,
405 r#"
406macro_rules! define_fn {($id:ident) => { fn $id{} }}
407define_fn!(bar);
408fn main() {
409 bar();
410}
411"#,
407 ); 412 );
408 } 413 }
409 414
410 #[test] 415 #[test]
411 fn test_rename_for_macro_define_fn_rev() { 416 fn test_rename_for_macro_define_fn_rev() {
412 test_rename( 417 check(
413 r#"
414 macro_rules! define_fn {($id:ident) => { fn $id{} }}
415 define_fn!(fo<|>o);
416 fn main() {
417 foo();
418 }"#,
419 "bar", 418 "bar",
420 r#" 419 r#"
421 macro_rules! define_fn {($id:ident) => { fn $id{} }} 420macro_rules! define_fn {($id:ident) => { fn $id{} }}
422 define_fn!(bar); 421define_fn!(fo<|>o);
423 fn main() { 422fn main() {
424 bar(); 423 foo();
425 }"#, 424}
425"#,
426 r#"
427macro_rules! define_fn {($id:ident) => { fn $id{} }}
428define_fn!(bar);
429fn main() {
430 bar();
431}
432"#,
426 ); 433 );
427 } 434 }
428 435
429 #[test] 436 #[test]
430 fn test_rename_for_param_inside() { 437 fn test_rename_for_param_inside() {
431 test_rename( 438 check("j", r#"fn foo(i : u32) -> u32 { i<|> }"#, r#"fn foo(j : u32) -> u32 { j }"#);
432 r#"
433 fn foo(i : u32) -> u32 {
434 i<|>
435 }"#,
436 "j",
437 r#"
438 fn foo(j : u32) -> u32 {
439 j
440 }"#,
441 );
442 } 439 }
443 440
444 #[test] 441 #[test]
445 fn test_rename_refs_for_fn_param() { 442 fn test_rename_refs_for_fn_param() {
446 test_rename( 443 check("j", r#"fn foo(i<|> : u32) -> u32 { i }"#, r#"fn foo(j : u32) -> u32 { j }"#);
447 r#"
448 fn foo(i<|> : u32) -> u32 {
449 i
450 }"#,
451 "new_name",
452 r#"
453 fn foo(new_name : u32) -> u32 {
454 new_name
455 }"#,
456 );
457 } 444 }
458 445
459 #[test] 446 #[test]
460 fn test_rename_for_mut_param() { 447 fn test_rename_for_mut_param() {
461 test_rename( 448 check("j", r#"fn foo(mut i<|> : u32) -> u32 { i }"#, r#"fn foo(mut j : u32) -> u32 { j }"#);
462 r#"
463 fn foo(mut i<|> : u32) -> u32 {
464 i
465 }"#,
466 "new_name",
467 r#"
468 fn foo(mut new_name : u32) -> u32 {
469 new_name
470 }"#,
471 );
472 } 449 }
473 450
474 #[test] 451 #[test]
475 fn test_rename_struct_field() { 452 fn test_rename_struct_field() {
476 test_rename( 453 check(
454 "j",
477 r#" 455 r#"
478 struct Foo { 456struct Foo { i<|>: i32 }
479 i<|>: i32,
480 }
481 457
482 impl Foo { 458impl Foo {
483 fn new(i: i32) -> Self { 459 fn new(i: i32) -> Self {
484 Self { i: i } 460 Self { i: i }
485 }
486 } 461 }
487 "#, 462}
488 "j", 463"#,
489 r#" 464 r#"
490 struct Foo { 465struct Foo { j: i32 }
491 j: i32,
492 }
493 466
494 impl Foo { 467impl Foo {
495 fn new(i: i32) -> Self { 468 fn new(i: i32) -> Self {
496 Self { j: i } 469 Self { j: i }
497 }
498 } 470 }
499 "#, 471}
472"#,
500 ); 473 );
501 } 474 }
502 475
503 #[test] 476 #[test]
504 fn test_rename_struct_field_for_shorthand() { 477 fn test_rename_struct_field_for_shorthand() {
505 mark::check!(test_rename_struct_field_for_shorthand); 478 mark::check!(test_rename_struct_field_for_shorthand);
506 test_rename( 479 check(
480 "j",
507 r#" 481 r#"
508 struct Foo { 482struct Foo { i<|>: i32 }
509 i<|>: i32,
510 }
511 483
512 impl Foo { 484impl Foo {
513 fn new(i: i32) -> Self { 485 fn new(i: i32) -> Self {
514 Self { i } 486 Self { i }
515 }
516 } 487 }
517 "#, 488}
518 "j", 489"#,
519 r#" 490 r#"
520 struct Foo { 491struct Foo { j: i32 }
521 j: i32,
522 }
523 492
524 impl Foo { 493impl Foo {
525 fn new(i: i32) -> Self { 494 fn new(i: i32) -> Self {
526 Self { j: i } 495 Self { j: i }
527 }
528 } 496 }
529 "#, 497}
498"#,
530 ); 499 );
531 } 500 }
532 501
533 #[test] 502 #[test]
534 fn test_rename_local_for_field_shorthand() { 503 fn test_rename_local_for_field_shorthand() {
535 mark::check!(test_rename_local_for_field_shorthand); 504 mark::check!(test_rename_local_for_field_shorthand);
536 test_rename( 505 check(
506 "j",
537 r#" 507 r#"
538 struct Foo { 508struct Foo { i: i32 }
539 i: i32,
540 }
541 509
542 impl Foo { 510impl Foo {
543 fn new(i<|>: i32) -> Self { 511 fn new(i<|>: i32) -> Self {
544 Self { i } 512 Self { i }
545 }
546 } 513 }
547 "#, 514}
548 "j", 515"#,
549 r#" 516 r#"
550 struct Foo { 517struct Foo { i: i32 }
551 i: i32,
552 }
553 518
554 impl Foo { 519impl Foo {
555 fn new(j: i32) -> Self { 520 fn new(j: i32) -> Self {
556 Self { i: j } 521 Self { i: j }
557 }
558 } 522 }
559 "#, 523}
524"#,
560 ); 525 );
561 } 526 }
562 527
563 #[test] 528 #[test]
564 fn test_field_shorthand_correct_struct() { 529 fn test_field_shorthand_correct_struct() {
565 test_rename( 530 check(
566 r#"
567 struct Foo {
568 i<|>: i32,
569 }
570
571 struct Bar {
572 i: i32,
573 }
574
575 impl Bar {
576 fn new(i: i32) -> Self {
577 Self { i }
578 }
579 }
580 "#,
581 "j", 531 "j",
582 r#" 532 r#"
583 struct Foo { 533struct Foo { i<|>: i32 }
584 j: i32, 534struct Bar { i: i32 }
585 }
586 535
587 struct Bar { 536impl Bar {
588 i: i32, 537 fn new(i: i32) -> Self {
538 Self { i }
589 } 539 }
540}
541"#,
542 r#"
543struct Foo { j: i32 }
544struct Bar { i: i32 }
590 545
591 impl Bar { 546impl Bar {
592 fn new(i: i32) -> Self { 547 fn new(i: i32) -> Self {
593 Self { i } 548 Self { i }
594 }
595 } 549 }
596 "#, 550}
551"#,
597 ); 552 );
598 } 553 }
599 554
600 #[test] 555 #[test]
601 fn test_shadow_local_for_struct_shorthand() { 556 fn test_shadow_local_for_struct_shorthand() {
602 test_rename( 557 check(
558 "j",
603 r#" 559 r#"
604 struct Foo { 560struct Foo { i: i32 }
605 i: i32,
606 }
607 561
608 fn baz(i<|>: i32) -> Self { 562fn baz(i<|>: i32) -> Self {
609 let x = Foo { i }; 563 let x = Foo { i };
610 { 564 {
611 let i = 0; 565 let i = 0;
612 Foo { i } 566 Foo { i }
613 }
614 } 567 }
615 "#, 568}
616 "j", 569"#,
617 r#" 570 r#"
618 struct Foo { 571struct Foo { i: i32 }
619 i: i32,
620 }
621 572
622 fn baz(j: i32) -> Self { 573fn baz(j: i32) -> Self {
623 let x = Foo { i: j }; 574 let x = Foo { i: j };
624 { 575 {
625 let i = 0; 576 let i = 0;
626 Foo { i } 577 Foo { i }
627 }
628 } 578 }
629 "#, 579}
580"#,
630 ); 581 );
631 } 582 }
632 583
633 #[test] 584 #[test]
634 fn test_rename_mod() { 585 fn test_rename_mod() {
635 let (analysis, position) = analysis_and_position( 586 check_expect(
587 "foo2",
636 r#" 588 r#"
637//- /lib.rs 589//- /lib.rs
638mod bar; 590mod bar;
@@ -641,53 +593,49 @@ mod bar;
641mod foo<|>; 593mod foo<|>;
642 594
643//- /bar/foo.rs 595//- /bar/foo.rs
644// emtpy 596// empty
645 "#, 597"#,
646 ); 598 expect![[r#"
647 let new_name = "foo2"; 599 RangeInfo {
648 let source_change = analysis.rename(position, new_name).unwrap(); 600 range: 4..7,
649 assert_debug_snapshot!(&source_change, 601 info: SourceChange {
650@r###" 602 source_file_edits: [
651 Some( 603 SourceFileEdit {
652 RangeInfo { 604 file_id: FileId(
653 range: 4..7, 605 2,
654 info: SourceChange { 606 ),
655 source_file_edits: [ 607 edit: TextEdit {
656 SourceFileEdit { 608 indels: [
657 file_id: FileId( 609 Indel {
658 2, 610 insert: "foo2",
659 ), 611 delete: 4..7,
660 edit: TextEdit { 612 },
661 indels: [ 613 ],
662 Indel { 614 },
663 insert: "foo2",
664 delete: 4..7,
665 },
666 ],
667 }, 615 },
668 }, 616 ],
669 ], 617 file_system_edits: [
670 file_system_edits: [ 618 MoveFile {
671 MoveFile { 619 src: FileId(
672 src: FileId( 620 3,
673 3, 621 ),
674 ), 622 anchor: FileId(
675 anchor: FileId( 623 3,
676 2, 624 ),
677 ), 625 dst: "foo2.rs",
678 dst: "foo2.rs", 626 },
679 }, 627 ],
680 ], 628 is_snippet: false,
681 is_snippet: false, 629 },
682 }, 630 }
683 }, 631 "#]],
684 ) 632 );
685 "###);
686 } 633 }
687 634
688 #[test] 635 #[test]
689 fn test_rename_mod_in_use_tree() { 636 fn test_rename_mod_in_use_tree() {
690 let (analysis, position) = analysis_and_position( 637 check_expect(
638 "quux",
691 r#" 639 r#"
692//- /main.rs 640//- /main.rs
693pub mod foo; 641pub mod foo;
@@ -699,140 +647,173 @@ pub struct FooContent;
699 647
700//- /bar.rs 648//- /bar.rs
701use crate::foo<|>::FooContent; 649use crate::foo<|>::FooContent;
702 "#, 650"#,
703 ); 651 expect![[r#"
704 let new_name = "qux"; 652 RangeInfo {
705 let source_change = analysis.rename(position, new_name).unwrap(); 653 range: 11..14,
706 assert_debug_snapshot!(&source_change, 654 info: SourceChange {
707@r###" 655 source_file_edits: [
708 Some( 656 SourceFileEdit {
709 RangeInfo { 657 file_id: FileId(
710 range: 11..14, 658 1,
711 info: SourceChange { 659 ),
712 source_file_edits: [ 660 edit: TextEdit {
713 SourceFileEdit { 661 indels: [
714 file_id: FileId( 662 Indel {
715 1, 663 insert: "quux",
716 ), 664 delete: 8..11,
717 edit: TextEdit { 665 },
718 indels: [ 666 ],
719 Indel { 667 },
720 insert: "qux",
721 delete: 8..11,
722 },
723 ],
724 }, 668 },
725 }, 669 SourceFileEdit {
726 SourceFileEdit { 670 file_id: FileId(
727 file_id: FileId( 671 3,
728 3, 672 ),
729 ), 673 edit: TextEdit {
730 edit: TextEdit { 674 indels: [
731 indels: [ 675 Indel {
732 Indel { 676 insert: "quux",
733 insert: "qux", 677 delete: 11..14,
734 delete: 11..14, 678 },
735 }, 679 ],
736 ], 680 },
737 }, 681 },
738 }, 682 ],
739 ], 683 file_system_edits: [
740 file_system_edits: [ 684 MoveFile {
741 MoveFile { 685 src: FileId(
742 src: FileId( 686 2,
743 2, 687 ),
744 ), 688 anchor: FileId(
745 anchor: FileId( 689 2,
746 3, 690 ),
747 ), 691 dst: "quux.rs",
748 dst: "qux.rs", 692 },
749 }, 693 ],
750 ], 694 is_snippet: false,
751 is_snippet: false, 695 },
752 }, 696 }
753 }, 697 "#]],
754 ) 698 );
755 "###);
756 } 699 }
757 700
758 #[test] 701 #[test]
759 fn test_rename_mod_in_dir() { 702 fn test_rename_mod_in_dir() {
760 let (analysis, position) = analysis_and_position( 703 check_expect(
704 "foo2",
761 r#" 705 r#"
762//- /lib.rs 706//- /lib.rs
763mod fo<|>o; 707mod fo<|>o;
764//- /foo/mod.rs 708//- /foo/mod.rs
765// emtpy 709// emtpy
766 "#, 710"#,
767 ); 711 expect![[r#"
768 let new_name = "foo2"; 712 RangeInfo {
769 let source_change = analysis.rename(position, new_name).unwrap(); 713 range: 4..7,
770 assert_debug_snapshot!(&source_change, 714 info: SourceChange {
771 @r###" 715 source_file_edits: [
772 Some( 716 SourceFileEdit {
773 RangeInfo { 717 file_id: FileId(
774 range: 4..7, 718 1,
775 info: SourceChange { 719 ),
776 source_file_edits: [ 720 edit: TextEdit {
777 SourceFileEdit { 721 indels: [
778 file_id: FileId( 722 Indel {
779 1, 723 insert: "foo2",
780 ), 724 delete: 4..7,
781 edit: TextEdit { 725 },
782 indels: [ 726 ],
783 Indel { 727 },
784 insert: "foo2",
785 delete: 4..7,
786 },
787 ],
788 }, 728 },
789 }, 729 ],
790 ], 730 file_system_edits: [
791 file_system_edits: [ 731 MoveFile {
792 MoveFile { 732 src: FileId(
793 src: FileId( 733 2,
794 2, 734 ),
795 ), 735 anchor: FileId(
796 anchor: FileId( 736 2,
797 1, 737 ),
798 ), 738 dst: "../foo2/mod.rs",
799 dst: "../foo2/mod.rs", 739 },
800 }, 740 ],
801 ], 741 is_snippet: false,
802 is_snippet: false, 742 },
803 }, 743 }
804 }, 744 "#]],
805 ) 745 );
806 "###
807 );
808 } 746 }
809 747
810 #[test] 748 #[test]
811 fn test_module_rename_in_path() { 749 fn test_rename_unusually_nested_mod() {
812 test_rename( 750 check_expect(
751 "bar",
813 r#" 752 r#"
814 mod <|>foo { 753//- /lib.rs
815 pub fn bar() {} 754mod outer { mod fo<|>o; }
755
756//- /outer/foo.rs
757// emtpy
758"#,
759 expect![[r#"
760 RangeInfo {
761 range: 16..19,
762 info: SourceChange {
763 source_file_edits: [
764 SourceFileEdit {
765 file_id: FileId(
766 1,
767 ),
768 edit: TextEdit {
769 indels: [
770 Indel {
771 insert: "bar",
772 delete: 16..19,
773 },
774 ],
775 },
776 },
777 ],
778 file_system_edits: [
779 MoveFile {
780 src: FileId(
781 2,
782 ),
783 anchor: FileId(
784 2,
785 ),
786 dst: "bar.rs",
787 },
788 ],
789 is_snippet: false,
790 },
791 }
792 "#]],
793 );
816 } 794 }
817 795
818 fn main() { 796 #[test]
819 foo::bar(); 797 fn test_module_rename_in_path() {
820 }"#, 798 check(
821 "baz", 799 "baz",
822 r#" 800 r#"
823 mod baz { 801mod <|>foo { pub fn bar() {} }
824 pub fn bar() {} 802
825 } 803fn main() { foo::bar(); }
804"#,
805 r#"
806mod baz { pub fn bar() {} }
826 807
827 fn main() { 808fn main() { baz::bar(); }
828 baz::bar(); 809"#,
829 }"#,
830 ); 810 );
831 } 811 }
832 812
833 #[test] 813 #[test]
834 fn test_rename_mod_filename_and_path() { 814 fn test_rename_mod_filename_and_path() {
835 let (analysis, position) = analysis_and_position( 815 check_expect(
816 "foo2",
836 r#" 817 r#"
837//- /lib.rs 818//- /lib.rs
838mod bar; 819mod bar;
@@ -845,229 +826,185 @@ pub mod foo<|>;
845 826
846//- /bar/foo.rs 827//- /bar/foo.rs
847// pub fn fun() {} 828// pub fn fun() {}
848 "#, 829"#,
849 ); 830 expect![[r#"
850 let new_name = "foo2"; 831 RangeInfo {
851 let source_change = analysis.rename(position, new_name).unwrap(); 832 range: 8..11,
852 assert_debug_snapshot!(&source_change, 833 info: SourceChange {
853@r###" 834 source_file_edits: [
854 Some( 835 SourceFileEdit {
855 RangeInfo { 836 file_id: FileId(
856 range: 8..11, 837 2,
857 info: SourceChange { 838 ),
858 source_file_edits: [ 839 edit: TextEdit {
859 SourceFileEdit { 840 indels: [
860 file_id: FileId( 841 Indel {
861 2, 842 insert: "foo2",
862 ), 843 delete: 8..11,
863 edit: TextEdit { 844 },
864 indels: [ 845 ],
865 Indel { 846 },
866 insert: "foo2",
867 delete: 8..11,
868 },
869 ],
870 }, 847 },
871 }, 848 SourceFileEdit {
872 SourceFileEdit { 849 file_id: FileId(
873 file_id: FileId( 850 1,
874 1, 851 ),
875 ), 852 edit: TextEdit {
876 edit: TextEdit { 853 indels: [
877 indels: [ 854 Indel {
878 Indel { 855 insert: "foo2",
879 insert: "foo2", 856 delete: 27..30,
880 delete: 27..30, 857 },
881 }, 858 ],
882 ], 859 },
883 }, 860 },
884 }, 861 ],
885 ], 862 file_system_edits: [
886 file_system_edits: [ 863 MoveFile {
887 MoveFile { 864 src: FileId(
888 src: FileId( 865 3,
889 3, 866 ),
890 ), 867 anchor: FileId(
891 anchor: FileId( 868 3,
892 2, 869 ),
893 ), 870 dst: "foo2.rs",
894 dst: "foo2.rs", 871 },
895 }, 872 ],
896 ], 873 is_snippet: false,
897 is_snippet: false, 874 },
898 }, 875 }
899 }, 876 "#]],
900 ) 877 );
901 "###);
902 } 878 }
903 879
904 #[test] 880 #[test]
905 fn test_enum_variant_from_module_1() { 881 fn test_enum_variant_from_module_1() {
906 test_rename( 882 check(
883 "Baz",
907 r#" 884 r#"
908 mod foo { 885mod foo {
909 pub enum Foo { 886 pub enum Foo { Bar<|> }
910 Bar<|>, 887}
911 }
912 }
913 888
914 fn func(f: foo::Foo) { 889fn func(f: foo::Foo) {
915 match f { 890 match f {
916 foo::Foo::Bar => {} 891 foo::Foo::Bar => {}
917 }
918 } 892 }
919 "#, 893}
920 "Baz", 894"#,
921 r#" 895 r#"
922 mod foo { 896mod foo {
923 pub enum Foo { 897 pub enum Foo { Baz }
924 Baz, 898}
925 }
926 }
927 899
928 fn func(f: foo::Foo) { 900fn func(f: foo::Foo) {
929 match f { 901 match f {
930 foo::Foo::Baz => {} 902 foo::Foo::Baz => {}
931 }
932 } 903 }
933 "#, 904}
905"#,
934 ); 906 );
935 } 907 }
936 908
937 #[test] 909 #[test]
938 fn test_enum_variant_from_module_2() { 910 fn test_enum_variant_from_module_2() {
939 test_rename( 911 check(
912 "baz",
940 r#" 913 r#"
941 mod foo { 914mod foo {
942 pub struct Foo { 915 pub struct Foo { pub bar<|>: uint }
943 pub bar<|>: uint, 916}
944 }
945 }
946 917
947 fn foo(f: foo::Foo) { 918fn foo(f: foo::Foo) {
948 let _ = f.bar; 919 let _ = f.bar;
949 } 920}
950 "#, 921"#,
951 "baz",
952 r#" 922 r#"
953 mod foo { 923mod foo {
954 pub struct Foo { 924 pub struct Foo { pub baz: uint }
955 pub baz: uint, 925}
956 }
957 }
958 926
959 fn foo(f: foo::Foo) { 927fn foo(f: foo::Foo) {
960 let _ = f.baz; 928 let _ = f.baz;
961 } 929}
962 "#, 930"#,
963 ); 931 );
964 } 932 }
965 933
966 #[test] 934 #[test]
967 fn test_parameter_to_self() { 935 fn test_parameter_to_self() {
968 test_rename( 936 check(
937 "self",
969 r#" 938 r#"
970 struct Foo { 939struct Foo { i: i32 }
971 i: i32,
972 }
973 940
974 impl Foo { 941impl Foo {
975 fn f(foo<|>: &mut Foo) -> i32 { 942 fn f(foo<|>: &mut Foo) -> i32 {
976 foo.i 943 foo.i
977 }
978 } 944 }
979 "#, 945}
980 "self", 946"#,
981 r#" 947 r#"
982 struct Foo { 948struct Foo { i: i32 }
983 i: i32,
984 }
985 949
986 impl Foo { 950impl Foo {
987 fn f(&mut self) -> i32 { 951 fn f(&mut self) -> i32 {
988 self.i 952 self.i
989 }
990 } 953 }
991 "#, 954}
955"#,
992 ); 956 );
993 } 957 }
994 958
995 #[test] 959 #[test]
996 fn test_self_to_parameter() { 960 fn test_self_to_parameter() {
997 test_rename( 961 check(
962 "foo",
998 r#" 963 r#"
999 struct Foo { 964struct Foo { i: i32 }
1000 i: i32,
1001 }
1002 965
1003 impl Foo { 966impl Foo {
1004 fn f(&mut <|>self) -> i32 { 967 fn f(&mut <|>self) -> i32 {
1005 self.i 968 self.i
1006 }
1007 } 969 }
1008 "#, 970}
1009 "foo", 971"#,
1010 r#" 972 r#"
1011 struct Foo { 973struct Foo { i: i32 }
1012 i: i32,
1013 }
1014 974
1015 impl Foo { 975impl Foo {
1016 fn f(foo: &mut Foo) -> i32 { 976 fn f(foo: &mut Foo) -> i32 {
1017 foo.i 977 foo.i
1018 }
1019 } 978 }
1020 "#, 979}
980"#,
1021 ); 981 );
1022 } 982 }
1023 983
1024 #[test] 984 #[test]
1025 fn test_self_in_path_to_parameter() { 985 fn test_self_in_path_to_parameter() {
1026 test_rename( 986 check(
987 "foo",
1027 r#" 988 r#"
1028 struct Foo { 989struct Foo { i: i32 }
1029 i: i32,
1030 }
1031 990
1032 impl Foo { 991impl Foo {
1033 fn f(&self) -> i32 { 992 fn f(&self) -> i32 {
1034 let self_var = 1; 993 let self_var = 1;
1035 self<|>.i 994 self<|>.i
1036 }
1037 } 995 }
1038 "#, 996}
1039 "foo", 997"#,
1040 r#" 998 r#"
1041 struct Foo { 999struct Foo { i: i32 }
1042 i: i32,
1043 }
1044 1000
1045 impl Foo { 1001impl Foo {
1046 fn f(foo: &Foo) -> i32 { 1002 fn f(foo: &Foo) -> i32 {
1047 let self_var = 1; 1003 let self_var = 1;
1048 foo.i 1004 foo.i
1049 }
1050 } 1005 }
1051 "#, 1006}
1007"#,
1052 ); 1008 );
1053 } 1009 }
1054
1055 fn test_rename(ra_fixture_before: &str, new_name: &str, ra_fixture_after: &str) {
1056 let ra_fixture_after = &trim_indent(ra_fixture_after);
1057 let (analysis, position) = analysis_and_position(ra_fixture_before);
1058 let source_change = analysis.rename(position, new_name).unwrap();
1059 let mut text_edit_builder = TextEditBuilder::default();
1060 let mut file_id: Option<FileId> = None;
1061 if let Some(change) = source_change {
1062 for edit in change.info.source_file_edits {
1063 file_id = Some(edit.file_id);
1064 for indel in edit.edit.into_iter() {
1065 text_edit_builder.replace(indel.delete, indel.insert);
1066 }
1067 }
1068 }
1069 let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string();
1070 text_edit_builder.finish().apply(&mut result);
1071 assert_eq_text!(ra_fixture_after, &*result);
1072 }
1073} 1010}
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index f569a3f17..3b7162b84 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -102,7 +102,7 @@ pub(crate) fn runnable(
102) -> Option<Runnable> { 102) -> Option<Runnable> {
103 match_ast! { 103 match_ast! {
104 match item { 104 match item {
105 ast::FnDef(it) => runnable_fn(sema, it, file_id), 105 ast::Fn(it) => runnable_fn(sema, it, file_id),
106 ast::Module(it) => runnable_mod(sema, it, file_id), 106 ast::Module(it) => runnable_mod(sema, it, file_id),
107 _ => None, 107 _ => None,
108 } 108 }
@@ -111,7 +111,7 @@ pub(crate) fn runnable(
111 111
112fn runnable_fn( 112fn runnable_fn(
113 sema: &Semantics<RootDatabase>, 113 sema: &Semantics<RootDatabase>,
114 fn_def: ast::FnDef, 114 fn_def: ast::Fn,
115 file_id: FileId, 115 file_id: FileId,
116) -> Option<Runnable> { 116) -> Option<Runnable> {
117 let name_string = fn_def.name()?.text().to_string(); 117 let name_string = fn_def.name()?.text().to_string();
@@ -168,8 +168,7 @@ fn runnable_fn(
168 }; 168 };
169 169
170 let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def)); 170 let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def));
171 let cfg_exprs = 171 let cfg_exprs = attrs.cfg().collect();
172 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
173 172
174 let nav = if let RunnableKind::DocTest { .. } = kind { 173 let nav = if let RunnableKind::DocTest { .. } = kind {
175 NavigationTarget::from_doc_commented( 174 NavigationTarget::from_doc_commented(
@@ -189,7 +188,7 @@ pub struct TestAttr {
189} 188}
190 189
191impl TestAttr { 190impl TestAttr {
192 fn from_fn(fn_def: &ast::FnDef) -> TestAttr { 191 fn from_fn(fn_def: &ast::Fn) -> TestAttr {
193 let ignore = fn_def 192 let ignore = fn_def
194 .attrs() 193 .attrs()
195 .filter_map(|attr| attr.simple_name()) 194 .filter_map(|attr| attr.simple_name())
@@ -204,7 +203,7 @@ impl TestAttr {
204/// 203///
205/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test, 204/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
206/// but it's better than not to have the runnables for the tests at all. 205/// but it's better than not to have the runnables for the tests at all.
207fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool { 206fn has_test_related_attribute(fn_def: &ast::Fn) -> bool {
208 fn_def 207 fn_def
209 .attrs() 208 .attrs()
210 .filter_map(|attr| attr.path()) 209 .filter_map(|attr| attr.path())
@@ -212,7 +211,7 @@ fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool {
212 .any(|attribute_text| attribute_text.contains("test")) 211 .any(|attribute_text| attribute_text.contains("test"))
213} 212}
214 213
215fn has_doc_test(fn_def: &ast::FnDef) -> bool { 214fn has_doc_test(fn_def: &ast::Fn) -> bool {
216 fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) 215 fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```"))
217} 216}
218 217
@@ -221,15 +220,7 @@ fn runnable_mod(
221 module: ast::Module, 220 module: ast::Module,
222 file_id: FileId, 221 file_id: FileId,
223) -> Option<Runnable> { 222) -> Option<Runnable> {
224 let has_test_function = module 223 if !has_test_function_or_multiple_test_submodules(&module) {
225 .item_list()?
226 .items()
227 .filter_map(|it| match it {
228 ast::ModuleItem::FnDef(it) => Some(it),
229 _ => None,
230 })
231 .any(|f| has_test_related_attribute(&f));
232 if !has_test_function {
233 return None; 224 return None;
234 } 225 }
235 let module_def = sema.to_def(&module)?; 226 let module_def = sema.to_def(&module)?;
@@ -242,22 +233,56 @@ fn runnable_mod(
242 .join("::"); 233 .join("::");
243 234
244 let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module)); 235 let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module));
245 let cfg_exprs = 236 let cfg_exprs = attrs.cfg().collect();
246 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
247
248 let nav = module_def.to_nav(sema.db); 237 let nav = module_def.to_nav(sema.db);
249 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs }) 238 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs })
250} 239}
251 240
241// We could create runnables for modules with number_of_test_submodules > 0,
242// but that bloats the runnables for no real benefit, since all tests can be run by the submodule already
243fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool {
244 if let Some(item_list) = module.item_list() {
245 let mut number_of_test_submodules = 0;
246
247 for item in item_list.items() {
248 match item {
249 ast::Item::Fn(f) => {
250 if has_test_related_attribute(&f) {
251 return true;
252 }
253 }
254 ast::Item::Module(submodule) => {
255 if has_test_function_or_multiple_test_submodules(&submodule) {
256 number_of_test_submodules += 1;
257 }
258 }
259 _ => (),
260 }
261 }
262
263 number_of_test_submodules > 1
264 } else {
265 false
266 }
267}
268
252#[cfg(test)] 269#[cfg(test)]
253mod tests { 270mod tests {
254 use insta::assert_debug_snapshot; 271 use expect::{expect, Expect};
255 272
256 use crate::mock_analysis::analysis_and_position; 273 use crate::mock_analysis::analysis_and_position;
257 274
258 use super::{Runnable, RunnableAction, BENCH, BIN, DOCTEST, TEST}; 275 use super::{RunnableAction, BENCH, BIN, DOCTEST, TEST};
259 276
260 fn assert_actions(runnables: &[Runnable], actions: &[&RunnableAction]) { 277 fn check(
278 ra_fixture: &str,
279 // FIXME: fold this into `expect` as well
280 actions: &[&RunnableAction],
281 expect: Expect,
282 ) {
283 let (analysis, position) = analysis_and_position(ra_fixture);
284 let runnables = analysis.runnables(position.file_id).unwrap();
285 expect.assert_debug_eq(&runnables);
261 assert_eq!( 286 assert_eq!(
262 actions, 287 actions,
263 runnables.into_iter().map(|it| it.action()).collect::<Vec<_>>().as_slice() 288 runnables.into_iter().map(|it| it.action()).collect::<Vec<_>>().as_slice()
@@ -266,579 +291,593 @@ mod tests {
266 291
267 #[test] 292 #[test]
268 fn test_runnables() { 293 fn test_runnables() {
269 let (analysis, pos) = analysis_and_position( 294 check(
270 r#" 295 r#"
271 //- /lib.rs 296//- /lib.rs
272 <|> //empty 297<|>
273 fn main() {} 298fn main() {}
274 299
275 #[test] 300#[test]
276 fn test_foo() {} 301fn test_foo() {}
277 302
278 #[test] 303#[test]
279 #[ignore] 304#[ignore]
280 fn test_foo() {} 305fn test_foo() {}
281 306
282 #[bench] 307#[bench]
283 fn bench() {} 308fn bench() {}
284 "#, 309"#,
285 ); 310 &[&BIN, &TEST, &TEST, &BENCH],
286 let runnables = analysis.runnables(pos.file_id).unwrap(); 311 expect![[r#"
287 assert_debug_snapshot!(&runnables, 312 [
288 @r###" 313 Runnable {
289 [ 314 nav: NavigationTarget {
290 Runnable { 315 file_id: FileId(
291 nav: NavigationTarget { 316 1,
292 file_id: FileId( 317 ),
293 1, 318 full_range: 1..13,
294 ), 319 focus_range: Some(
295 full_range: 1..21, 320 4..8,
296 name: "main", 321 ),
297 kind: FN_DEF, 322 name: "main",
298 focus_range: Some( 323 kind: FN,
299 12..16, 324 container_name: None,
300 ), 325 description: None,
301 container_name: None, 326 docs: None,
302 description: None, 327 },
303 docs: None, 328 kind: Bin,
304 }, 329 cfg_exprs: [],
305 kind: Bin, 330 },
306 cfg_exprs: [], 331 Runnable {
307 }, 332 nav: NavigationTarget {
308 Runnable { 333 file_id: FileId(
309 nav: NavigationTarget { 334 1,
310 file_id: FileId( 335 ),
311 1, 336 full_range: 15..39,
312 ), 337 focus_range: Some(
313 full_range: 23..47, 338 26..34,
314 name: "test_foo", 339 ),
315 kind: FN_DEF, 340 name: "test_foo",
316 focus_range: Some( 341 kind: FN,
317 34..42, 342 container_name: None,
318 ), 343 description: None,
319 container_name: None, 344 docs: None,
320 description: None, 345 },
321 docs: None, 346 kind: Test {
322 }, 347 test_id: Path(
323 kind: Test { 348 "test_foo",
324 test_id: Path( 349 ),
325 "test_foo", 350 attr: TestAttr {
326 ), 351 ignore: false,
327 attr: TestAttr { 352 },
328 ignore: false, 353 },
354 cfg_exprs: [],
355 },
356 Runnable {
357 nav: NavigationTarget {
358 file_id: FileId(
359 1,
360 ),
361 full_range: 41..75,
362 focus_range: Some(
363 62..70,
364 ),
365 name: "test_foo",
366 kind: FN,
367 container_name: None,
368 description: None,
369 docs: None,
370 },
371 kind: Test {
372 test_id: Path(
373 "test_foo",
374 ),
375 attr: TestAttr {
376 ignore: true,
377 },
378 },
379 cfg_exprs: [],
329 }, 380 },
330 }, 381 Runnable {
331 cfg_exprs: [], 382 nav: NavigationTarget {
332 }, 383 file_id: FileId(
333 Runnable { 384 1,
334 nav: NavigationTarget { 385 ),
335 file_id: FileId( 386 full_range: 77..99,
336 1, 387 focus_range: Some(
337 ), 388 89..94,
338 full_range: 49..83, 389 ),
339 name: "test_foo", 390 name: "bench",
340 kind: FN_DEF, 391 kind: FN,
341 focus_range: Some( 392 container_name: None,
342 70..78, 393 description: None,
343 ), 394 docs: None,
344 container_name: None, 395 },
345 description: None, 396 kind: Bench {
346 docs: None, 397 test_id: Path(
347 }, 398 "bench",
348 kind: Test { 399 ),
349 test_id: Path( 400 },
350 "test_foo", 401 cfg_exprs: [],
351 ),
352 attr: TestAttr {
353 ignore: true,
354 }, 402 },
355 }, 403 ]
356 cfg_exprs: [], 404 "#]],
357 }, 405 );
358 Runnable {
359 nav: NavigationTarget {
360 file_id: FileId(
361 1,
362 ),
363 full_range: 85..107,
364 name: "bench",
365 kind: FN_DEF,
366 focus_range: Some(
367 97..102,
368 ),
369 container_name: None,
370 description: None,
371 docs: None,
372 },
373 kind: Bench {
374 test_id: Path(
375 "bench",
376 ),
377 },
378 cfg_exprs: [],
379 },
380 ]
381 "###
382 );
383 assert_actions(&runnables, &[&BIN, &TEST, &TEST, &BENCH]);
384 } 406 }
385 407
386 #[test] 408 #[test]
387 fn test_runnables_doc_test() { 409 fn test_runnables_doc_test() {
388 let (analysis, pos) = analysis_and_position( 410 check(
389 r#" 411 r#"
390 //- /lib.rs 412//- /lib.rs
391 <|> //empty 413<|>
392 fn main() {} 414fn main() {}
393 415
394 /// ``` 416/// ```
395 /// let x = 5; 417/// let x = 5;
396 /// ``` 418/// ```
397 fn foo() {} 419fn foo() {}
398 "#, 420"#,
421 &[&BIN, &DOCTEST],
422 expect![[r#"
423 [
424 Runnable {
425 nav: NavigationTarget {
426 file_id: FileId(
427 1,
428 ),
429 full_range: 1..13,
430 focus_range: Some(
431 4..8,
432 ),
433 name: "main",
434 kind: FN,
435 container_name: None,
436 description: None,
437 docs: None,
438 },
439 kind: Bin,
440 cfg_exprs: [],
441 },
442 Runnable {
443 nav: NavigationTarget {
444 file_id: FileId(
445 1,
446 ),
447 full_range: 15..57,
448 focus_range: None,
449 name: "foo",
450 kind: FN,
451 container_name: None,
452 description: None,
453 docs: None,
454 },
455 kind: DocTest {
456 test_id: Path(
457 "foo",
458 ),
459 },
460 cfg_exprs: [],
461 },
462 ]
463 "#]],
399 ); 464 );
400 let runnables = analysis.runnables(pos.file_id).unwrap();
401 assert_debug_snapshot!(&runnables,
402 @r###"
403 [
404 Runnable {
405 nav: NavigationTarget {
406 file_id: FileId(
407 1,
408 ),
409 full_range: 1..21,
410 name: "main",
411 kind: FN_DEF,
412 focus_range: Some(
413 12..16,
414 ),
415 container_name: None,
416 description: None,
417 docs: None,
418 },
419 kind: Bin,
420 cfg_exprs: [],
421 },
422 Runnable {
423 nav: NavigationTarget {
424 file_id: FileId(
425 1,
426 ),
427 full_range: 23..65,
428 name: "foo",
429 kind: FN_DEF,
430 focus_range: None,
431 container_name: None,
432 description: None,
433 docs: None,
434 },
435 kind: DocTest {
436 test_id: Path(
437 "foo",
438 ),
439 },
440 cfg_exprs: [],
441 },
442 ]
443 "###
444 );
445 assert_actions(&runnables, &[&BIN, &DOCTEST]);
446 } 465 }
447 466
448 #[test] 467 #[test]
449 fn test_runnables_doc_test_in_impl() { 468 fn test_runnables_doc_test_in_impl() {
450 let (analysis, pos) = analysis_and_position( 469 check(
451 r#" 470 r#"
452 //- /lib.rs 471//- /lib.rs
453 <|> //empty 472<|>
454 fn main() {} 473fn main() {}
455 474
456 struct Data; 475struct Data;
457 impl Data { 476impl Data {
458 /// ``` 477 /// ```
459 /// let x = 5; 478 /// let x = 5;
460 /// ``` 479 /// ```
461 fn foo() {} 480 fn foo() {}
462 } 481}
463 "#, 482"#,
483 &[&BIN, &DOCTEST],
484 expect![[r#"
485 [
486 Runnable {
487 nav: NavigationTarget {
488 file_id: FileId(
489 1,
490 ),
491 full_range: 1..13,
492 focus_range: Some(
493 4..8,
494 ),
495 name: "main",
496 kind: FN,
497 container_name: None,
498 description: None,
499 docs: None,
500 },
501 kind: Bin,
502 cfg_exprs: [],
503 },
504 Runnable {
505 nav: NavigationTarget {
506 file_id: FileId(
507 1,
508 ),
509 full_range: 44..98,
510 focus_range: None,
511 name: "foo",
512 kind: FN,
513 container_name: None,
514 description: None,
515 docs: None,
516 },
517 kind: DocTest {
518 test_id: Path(
519 "Data::foo",
520 ),
521 },
522 cfg_exprs: [],
523 },
524 ]
525 "#]],
464 ); 526 );
465 let runnables = analysis.runnables(pos.file_id).unwrap();
466 assert_debug_snapshot!(&runnables,
467 @r###"
468 [
469 Runnable {
470 nav: NavigationTarget {
471 file_id: FileId(
472 1,
473 ),
474 full_range: 1..21,
475 name: "main",
476 kind: FN_DEF,
477 focus_range: Some(
478 12..16,
479 ),
480 container_name: None,
481 description: None,
482 docs: None,
483 },
484 kind: Bin,
485 cfg_exprs: [],
486 },
487 Runnable {
488 nav: NavigationTarget {
489 file_id: FileId(
490 1,
491 ),
492 full_range: 52..106,
493 name: "foo",
494 kind: FN_DEF,
495 focus_range: None,
496 container_name: None,
497 description: None,
498 docs: None,
499 },
500 kind: DocTest {
501 test_id: Path(
502 "Data::foo",
503 ),
504 },
505 cfg_exprs: [],
506 },
507 ]
508 "###
509 );
510 assert_actions(&runnables, &[&BIN, &DOCTEST]);
511 } 527 }
512 528
513 #[test] 529 #[test]
514 fn test_runnables_module() { 530 fn test_runnables_module() {
515 let (analysis, pos) = analysis_and_position( 531 check(
516 r#" 532 r#"
517 //- /lib.rs 533//- /lib.rs
518 <|> //empty 534<|>
519 mod test_mod { 535mod test_mod {
520 #[test] 536 #[test]
521 fn test_foo1() {} 537 fn test_foo1() {}
522 } 538}
523 "#, 539"#,
524 ); 540 &[&TEST, &TEST],
525 let runnables = analysis.runnables(pos.file_id).unwrap(); 541 expect![[r#"
526 assert_debug_snapshot!(&runnables, 542 [
527 @r###" 543 Runnable {
528 [ 544 nav: NavigationTarget {
529 Runnable { 545 file_id: FileId(
530 nav: NavigationTarget { 546 1,
531 file_id: FileId( 547 ),
532 1, 548 full_range: 1..51,
533 ), 549 focus_range: Some(
534 full_range: 1..59, 550 5..13,
535 name: "test_mod", 551 ),
536 kind: MODULE, 552 name: "test_mod",
537 focus_range: Some( 553 kind: MODULE,
538 13..21, 554 container_name: None,
539 ), 555 description: None,
540 container_name: None, 556 docs: None,
541 description: None, 557 },
542 docs: None, 558 kind: TestMod {
543 }, 559 path: "test_mod",
544 kind: TestMod { 560 },
545 path: "test_mod", 561 cfg_exprs: [],
546 },
547 cfg_exprs: [],
548 },
549 Runnable {
550 nav: NavigationTarget {
551 file_id: FileId(
552 1,
553 ),
554 full_range: 28..57,
555 name: "test_foo1",
556 kind: FN_DEF,
557 focus_range: Some(
558 43..52,
559 ),
560 container_name: None,
561 description: None,
562 docs: None,
563 },
564 kind: Test {
565 test_id: Path(
566 "test_mod::test_foo1",
567 ),
568 attr: TestAttr {
569 ignore: false,
570 }, 562 },
571 }, 563 Runnable {
572 cfg_exprs: [], 564 nav: NavigationTarget {
573 }, 565 file_id: FileId(
574 ] 566 1,
575 "### 567 ),
576 ); 568 full_range: 20..49,
577 assert_actions(&runnables, &[&TEST, &TEST]); 569 focus_range: Some(
570 35..44,
571 ),
572 name: "test_foo1",
573 kind: FN,
574 container_name: None,
575 description: None,
576 docs: None,
577 },
578 kind: Test {
579 test_id: Path(
580 "test_mod::test_foo1",
581 ),
582 attr: TestAttr {
583 ignore: false,
584 },
585 },
586 cfg_exprs: [],
587 },
588 ]
589 "#]],
590 );
578 } 591 }
579 592
580 #[test] 593 #[test]
581 fn test_runnables_one_depth_layer_module() { 594 fn only_modules_with_test_functions_or_more_than_one_test_submodule_have_runners() {
582 let (analysis, pos) = analysis_and_position( 595 check(
583 r#" 596 r#"
584 //- /lib.rs 597//- /lib.rs
585 <|> //empty 598<|>
586 mod foo { 599mod root_tests {
587 mod test_mod { 600 mod nested_tests_0 {
588 #[test] 601 mod nested_tests_1 {
589 fn test_foo1() {} 602 #[test]
590 } 603 fn nested_test_11() {}
604
605 #[test]
606 fn nested_test_12() {}
591 } 607 }
592 "#,
593 );
594 let runnables = analysis.runnables(pos.file_id).unwrap();
595 assert_debug_snapshot!(&runnables,
596 @r###"
597 [
598 Runnable {
599 nav: NavigationTarget {
600 file_id: FileId(
601 1,
602 ),
603 full_range: 23..85,
604 name: "test_mod",
605 kind: MODULE,
606 focus_range: Some(
607 27..35,
608 ),
609 container_name: None,
610 description: None,
611 docs: None,
612 },
613 kind: TestMod {
614 path: "foo::test_mod",
615 },
616 cfg_exprs: [],
617 },
618 Runnable {
619 nav: NavigationTarget {
620 file_id: FileId(
621 1,
622 ),
623 full_range: 46..79,
624 name: "test_foo1",
625 kind: FN_DEF,
626 focus_range: Some(
627 65..74,
628 ),
629 container_name: None,
630 description: None,
631 docs: None,
632 },
633 kind: Test {
634 test_id: Path(
635 "foo::test_mod::test_foo1",
636 ),
637 attr: TestAttr {
638 ignore: false,
639 },
640 },
641 cfg_exprs: [],
642 },
643 ]
644 "###
645 );
646 assert_actions(&runnables, &[&TEST, &TEST]);
647 }
648 608
649 #[test] 609 mod nested_tests_2 {
650 fn test_runnables_multiple_depth_module() { 610 #[test]
651 let (analysis, pos) = analysis_and_position( 611 fn nested_test_2() {}
652 r#"
653 //- /lib.rs
654 <|> //empty
655 mod foo {
656 mod bar {
657 mod test_mod {
658 #[test]
659 fn test_foo1() {}
660 }
661 }
662 } 612 }
663 "#, 613
664 ); 614 mod nested_tests_3 {}
665 let runnables = analysis.runnables(pos.file_id).unwrap();
666 assert_debug_snapshot!(&runnables,
667 @r###"
668 [
669 Runnable {
670 nav: NavigationTarget {
671 file_id: FileId(
672 1,
673 ),
674 full_range: 41..115,
675 name: "test_mod",
676 kind: MODULE,
677 focus_range: Some(
678 45..53,
679 ),
680 container_name: None,
681 description: None,
682 docs: None,
683 },
684 kind: TestMod {
685 path: "foo::bar::test_mod",
686 },
687 cfg_exprs: [],
688 },
689 Runnable {
690 nav: NavigationTarget {
691 file_id: FileId(
692 1,
693 ),
694 full_range: 68..105,
695 name: "test_foo1",
696 kind: FN_DEF,
697 focus_range: Some(
698 91..100,
699 ),
700 container_name: None,
701 description: None,
702 docs: None,
703 },
704 kind: Test {
705 test_id: Path(
706 "foo::bar::test_mod::test_foo1",
707 ),
708 attr: TestAttr {
709 ignore: false,
710 },
711 },
712 cfg_exprs: [],
713 },
714 ]
715 "###
716 );
717 assert_actions(&runnables, &[&TEST, &TEST]);
718 } 615 }
719 616
720 #[test] 617 mod nested_tests_4 {}
721 fn test_runnables_with_feature() { 618}
722 let (analysis, pos) = analysis_and_position( 619"#,
723 r#" 620 &[&TEST, &TEST, &TEST, &TEST, &TEST, &TEST],
724 //- /lib.rs crate:foo cfg:feature=foo 621 expect![[r#"
725 <|> //empty 622 [
726 #[test] 623 Runnable {
727 #[cfg(feature = "foo")] 624 nav: NavigationTarget {
728 fn test_foo1() {} 625 file_id: FileId(
729 "#, 626 1,
730 ); 627 ),
731 let runnables = analysis.runnables(pos.file_id).unwrap(); 628 full_range: 22..323,
732 assert_debug_snapshot!(&runnables, 629 focus_range: Some(
733 @r###" 630 26..40,
734 [ 631 ),
735 Runnable { 632 name: "nested_tests_0",
736 nav: NavigationTarget { 633 kind: MODULE,
737 file_id: FileId( 634 container_name: None,
738 1, 635 description: None,
739 ), 636 docs: None,
740 full_range: 1..58, 637 },
741 name: "test_foo1", 638 kind: TestMod {
742 kind: FN_DEF, 639 path: "root_tests::nested_tests_0",
743 focus_range: Some( 640 },
744 44..53, 641 cfg_exprs: [],
745 ), 642 },
746 container_name: None, 643 Runnable {
747 description: None, 644 nav: NavigationTarget {
748 docs: None, 645 file_id: FileId(
749 }, 646 1,
750 kind: Test { 647 ),
751 test_id: Path( 648 full_range: 51..192,
752 "test_foo1", 649 focus_range: Some(
753 ), 650 55..69,
754 attr: TestAttr { 651 ),
755 ignore: false, 652 name: "nested_tests_1",
653 kind: MODULE,
654 container_name: None,
655 description: None,
656 docs: None,
657 },
658 kind: TestMod {
659 path: "root_tests::nested_tests_0::nested_tests_1",
660 },
661 cfg_exprs: [],
662 },
663 Runnable {
664 nav: NavigationTarget {
665 file_id: FileId(
666 1,
667 ),
668 full_range: 84..126,
669 focus_range: Some(
670 107..121,
671 ),
672 name: "nested_test_11",
673 kind: FN,
674 container_name: None,
675 description: None,
676 docs: None,
677 },
678 kind: Test {
679 test_id: Path(
680 "root_tests::nested_tests_0::nested_tests_1::nested_test_11",
681 ),
682 attr: TestAttr {
683 ignore: false,
684 },
685 },
686 cfg_exprs: [],
687 },
688 Runnable {
689 nav: NavigationTarget {
690 file_id: FileId(
691 1,
692 ),
693 full_range: 140..182,
694 focus_range: Some(
695 163..177,
696 ),
697 name: "nested_test_12",
698 kind: FN,
699 container_name: None,
700 description: None,
701 docs: None,
702 },
703 kind: Test {
704 test_id: Path(
705 "root_tests::nested_tests_0::nested_tests_1::nested_test_12",
706 ),
707 attr: TestAttr {
708 ignore: false,
709 },
710 },
711 cfg_exprs: [],
712 },
713 Runnable {
714 nav: NavigationTarget {
715 file_id: FileId(
716 1,
717 ),
718 full_range: 202..286,
719 focus_range: Some(
720 206..220,
721 ),
722 name: "nested_tests_2",
723 kind: MODULE,
724 container_name: None,
725 description: None,
726 docs: None,
727 },
728 kind: TestMod {
729 path: "root_tests::nested_tests_0::nested_tests_2",
730 },
731 cfg_exprs: [],
756 }, 732 },
757 }, 733 Runnable {
758 cfg_exprs: [ 734 nav: NavigationTarget {
759 KeyValue { 735 file_id: FileId(
760 key: "feature", 736 1,
761 value: "foo", 737 ),
738 full_range: 235..276,
739 focus_range: Some(
740 258..271,
741 ),
742 name: "nested_test_2",
743 kind: FN,
744 container_name: None,
745 description: None,
746 docs: None,
747 },
748 kind: Test {
749 test_id: Path(
750 "root_tests::nested_tests_0::nested_tests_2::nested_test_2",
751 ),
752 attr: TestAttr {
753 ignore: false,
754 },
755 },
756 cfg_exprs: [],
762 }, 757 },
763 ], 758 ]
764 }, 759 "#]],
765 ] 760 );
766 "###
767 );
768 assert_actions(&runnables, &[&TEST]);
769 } 761 }
770 762
771 #[test] 763 #[test]
772 fn test_runnables_with_features() { 764 fn test_runnables_with_feature() {
773 let (analysis, pos) = analysis_and_position( 765 check(
774 r#" 766 r#"
775 //- /lib.rs crate:foo cfg:feature=foo,feature=bar 767//- /lib.rs crate:foo cfg:feature=foo
776 <|> //empty 768<|>
777 #[test] 769#[test]
778 #[cfg(all(feature = "foo", feature = "bar"))] 770#[cfg(feature = "foo")]
779 fn test_foo1() {} 771fn test_foo1() {}
780 "#, 772"#,
781 ); 773 &[&TEST],
782 let runnables = analysis.runnables(pos.file_id).unwrap(); 774 expect![[r#"
783 assert_debug_snapshot!(&runnables, 775 [
784 @r###" 776 Runnable {
785 [ 777 nav: NavigationTarget {
786 Runnable { 778 file_id: FileId(
787 nav: NavigationTarget { 779 1,
788 file_id: FileId( 780 ),
789 1, 781 full_range: 1..50,
790 ), 782 focus_range: Some(
791 full_range: 1..80, 783 36..45,
792 name: "test_foo1", 784 ),
793 kind: FN_DEF, 785 name: "test_foo1",
794 focus_range: Some( 786 kind: FN,
795 66..75, 787 container_name: None,
796 ), 788 description: None,
797 container_name: None, 789 docs: None,
798 description: None, 790 },
799 docs: None, 791 kind: Test {
800 }, 792 test_id: Path(
801 kind: Test { 793 "test_foo1",
802 test_id: Path( 794 ),
803 "test_foo1", 795 attr: TestAttr {
804 ), 796 ignore: false,
805 attr: TestAttr { 797 },
806 ignore: false, 798 },
807 }, 799 cfg_exprs: [
808 },
809 cfg_exprs: [
810 All(
811 [
812 KeyValue { 800 KeyValue {
813 key: "feature", 801 key: "feature",
814 value: "foo", 802 value: "foo",
815 }, 803 },
816 KeyValue { 804 ],
817 key: "feature", 805 },
818 value: "bar", 806 ]
807 "#]],
808 );
809 }
810
811 #[test]
812 fn test_runnables_with_features() {
813 check(
814 r#"
815//- /lib.rs crate:foo cfg:feature=foo,feature=bar
816<|>
817#[test]
818#[cfg(all(feature = "foo", feature = "bar"))]
819fn test_foo1() {}
820"#,
821 &[&TEST],
822 expect![[r#"
823 [
824 Runnable {
825 nav: NavigationTarget {
826 file_id: FileId(
827 1,
828 ),
829 full_range: 1..72,
830 focus_range: Some(
831 58..67,
832 ),
833 name: "test_foo1",
834 kind: FN,
835 container_name: None,
836 description: None,
837 docs: None,
838 },
839 kind: Test {
840 test_id: Path(
841 "test_foo1",
842 ),
843 attr: TestAttr {
844 ignore: false,
819 }, 845 },
846 },
847 cfg_exprs: [
848 All(
849 [
850 KeyValue {
851 key: "feature",
852 value: "foo",
853 },
854 KeyValue {
855 key: "feature",
856 value: "bar",
857 },
858 ],
859 ),
820 ], 860 ],
821 ), 861 },
822 ], 862 ]
823 }, 863 "#]],
824 ] 864 );
825 "###
826 );
827 assert_actions(&runnables, &[&TEST]);
828 } 865 }
829 866
830 #[test] 867 #[test]
831 fn test_runnables_no_test_function_in_module() { 868 fn test_runnables_no_test_function_in_module() {
832 let (analysis, pos) = analysis_and_position( 869 check(
833 r#" 870 r#"
834 //- /lib.rs 871//- /lib.rs
835 <|> //empty 872<|>
836 mod test_mod { 873mod test_mod {
837 fn foo1() {} 874 fn foo1() {}
838 } 875}
839 "#, 876"#,
877 &[],
878 expect![[r#"
879 []
880 "#]],
840 ); 881 );
841 let runnables = analysis.runnables(pos.file_id).unwrap();
842 assert!(runnables.is_empty())
843 } 882 }
844} 883}
diff --git a/crates/ra_ide/src/snapshots/highlight_doctest.html b/crates/ra_ide/src/snapshots/highlight_doctest.html
deleted file mode 100644
index e8155def7..000000000
--- a/crates/ra_ide/src/snapshots/highlight_doctest.html
+++ /dev/null
@@ -1,101 +0,0 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.documentation { color: #629755; }
9.injected { opacity: 0.65 ; }
10.struct, .enum { color: #7CB8BB; }
11.enum_variant { color: #BDE0F3; }
12.string_literal { color: #CC9393; }
13.field { color: #94BFF3; }
14.function { color: #93E0E3; }
15.function.unsafe { color: #BC8383; }
16.operator.unsafe { color: #BC8383; }
17.parameter { color: #94BFF3; }
18.text { color: #DCDCCC; }
19.type { color: #7CB8BB; }
20.builtin_type { color: #8CD0D3; }
21.type_param { color: #DFAF8F; }
22.attribute { color: #94BFF3; }
23.numeric_literal { color: #BFEBBF; }
24.bool_literal { color: #BFE6EB; }
25.macro { color: #94BFF3; }
26.module { color: #AFD8AF; }
27.variable { color: #DCDCCC; }
28.format_specifier { color: #CC696B; }
29.mutable { text-decoration: underline; }
30.unresolved_reference { color: #FC5555; }
31.escape_sequence { color: #94BFF3; }
32
33.keyword { color: #F0DFAF; font-weight: bold; }
34.keyword.unsafe { color: #BC8383; font-weight: bold; }
35.control { font-style: italic; }
36</style>
37<pre><code><span class="comment documentation">/// ```</span>
38<span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> _ = </span><span class="string_literal injected">"early doctests should not go boom"</span><span class="generic injected">;</span>
39<span class="comment documentation">/// ```</span>
40<span class="keyword">struct</span> <span class="struct declaration">Foo</span> {
41 <span class="field declaration">bar</span>: <span class="builtin_type">bool</span>,
42}
43
44<span class="keyword">impl</span> <span class="struct">Foo</span> {
45 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="constant declaration">bar</span>: <span class="builtin_type">bool</span> = <span class="bool_literal">true</span>;
46
47 <span class="comment documentation">/// Constructs a new `Foo`.</span>
48 <span class="comment documentation">///</span>
49 <span class="comment documentation">/// # Examples</span>
50 <span class="comment documentation">///</span>
51 <span class="comment documentation">/// ```</span>
52 <span class="comment documentation">/// #</span><span class="generic injected"> </span><span class="attribute injected">#![</span><span class="function attribute injected">allow</span><span class="attribute injected">(unused_mut)]</span>
53 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="generic injected">: </span><span class="struct injected">Foo</span><span class="generic injected"> = </span><span class="struct injected">Foo</span><span class="generic injected">::</span><span class="function injected">new</span><span class="generic injected">();</span>
54 <span class="comment documentation">/// ```</span>
55 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration">new</span>() -&gt; <span class="struct">Foo</span> {
56 <span class="struct">Foo</span> { <span class="field">bar</span>: <span class="bool_literal">true</span> }
57 }
58
59 <span class="comment documentation">/// `bar` method on `Foo`.</span>
60 <span class="comment documentation">///</span>
61 <span class="comment documentation">/// # Examples</span>
62 <span class="comment documentation">///</span>
63 <span class="comment documentation">/// ```</span>
64 <span class="comment documentation">/// </span><span class="keyword injected">use</span><span class="generic injected"> </span><span class="module injected">x</span><span class="generic injected">::</span><span class="module injected">y</span><span class="generic injected">;</span>
65 <span class="comment documentation">///</span>
66 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">foo</span><span class="generic injected"> = </span><span class="struct injected">Foo</span><span class="generic injected">::</span><span class="function injected">new</span><span class="generic injected">();</span>
67 <span class="comment documentation">///</span>
68 <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span>
69 <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="generic injected">(foo.bar());</span>
70 <span class="comment documentation">///</span>
71 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">bar</span><span class="generic injected"> = </span><span class="variable injected">foo</span><span class="generic injected">.</span><span class="field injected">bar</span><span class="generic injected"> || </span><span class="struct injected">Foo</span><span class="generic injected">::</span><span class="constant injected">bar</span><span class="generic injected">;</span>
72 <span class="comment documentation">///</span>
73 <span class="comment documentation">/// </span><span class="comment injected">/* multi-line
74 </span><span class="comment documentation">/// </span><span class="comment injected"> comment */</span>
75 <span class="comment documentation">///</span>
76 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">multi_line_string</span><span class="generic injected"> = </span><span class="string_literal injected">"Foo
77 </span><span class="comment documentation">/// </span><span class="string_literal injected"> bar
78 </span><span class="comment documentation">/// </span><span class="string_literal injected"> "</span><span class="generic injected">;</span>
79 <span class="comment documentation">///</span>
80 <span class="comment documentation">/// ```</span>
81 <span class="comment documentation">///</span>
82 <span class="comment documentation">/// ```rust,no_run</span>
83 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">foobar</span><span class="generic injected"> = </span><span class="struct injected">Foo</span><span class="generic injected">::</span><span class="function injected">new</span><span class="generic injected">().</span><span class="function injected">bar</span><span class="generic injected">();</span>
84 <span class="comment documentation">/// ```</span>
85 <span class="comment documentation">///</span>
86 <span class="comment documentation">/// ```sh</span>
87 <span class="comment documentation">/// echo 1</span>
88 <span class="comment documentation">/// ```</span>
89 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">bool</span> {
90 <span class="bool_literal">true</span>
91 }
92}
93
94<span class="comment documentation">/// ```</span>
95<span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="generic injected">(</span><span class="numeric_literal injected">1</span><span class="generic injected">);</span>
96<span class="comment documentation">/// ```</span>
97<span class="macro">macro_rules!</span> <span class="macro declaration">noop</span> {
98 ($expr:expr) =&gt; {
99 $expr
100 }
101}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html
deleted file mode 100644
index 1b0349bae..000000000
--- a/crates/ra_ide/src/snapshots/highlight_injection.html
+++ /dev/null
@@ -1,47 +0,0 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.documentation { color: #629755; }
9.injected { opacity: 0.65 ; }
10.struct, .enum { color: #7CB8BB; }
11.enum_variant { color: #BDE0F3; }
12.string_literal { color: #CC9393; }
13.field { color: #94BFF3; }
14.function { color: #93E0E3; }
15.function.unsafe { color: #BC8383; }
16.operator.unsafe { color: #BC8383; }
17.parameter { color: #94BFF3; }
18.text { color: #DCDCCC; }
19.type { color: #7CB8BB; }
20.builtin_type { color: #8CD0D3; }
21.type_param { color: #DFAF8F; }
22.attribute { color: #94BFF3; }
23.numeric_literal { color: #BFEBBF; }
24.bool_literal { color: #BFE6EB; }
25.macro { color: #94BFF3; }
26.module { color: #AFD8AF; }
27.variable { color: #DCDCCC; }
28.format_specifier { color: #CC696B; }
29.mutable { text-decoration: underline; }
30.unresolved_reference { color: #FC5555; }
31.escape_sequence { color: #94BFF3; }
32
33.keyword { color: #F0DFAF; font-weight: bold; }
34.keyword.unsafe { color: #BC8383; font-weight: bold; }
35.control { font-style: italic; }
36</style>
37<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span>(<span class="variable declaration">ra_fixture</span>: &<span class="builtin_type">str</span>) {}
38
39<span class="keyword">fn</span> <span class="function declaration">main</span>() {
40 <span class="function">fixture</span>(<span class="string_literal">r#"</span>
41 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> {
42 <span class="keyword">fn</span> <span class="function declaration">foo</span>() {
43 <span class="macro">println!</span>(<span class="string_literal">"2 + 2 = {}"</span>, <span class="numeric_literal">4</span>);
44 }
45 }<span class="string_literal">"#</span>
46 );
47}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html
deleted file mode 100644
index d184b5691..000000000
--- a/crates/ra_ide/src/snapshots/highlight_strings.html
+++ /dev/null
@@ -1,95 +0,0 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.documentation { color: #629755; }
9.injected { opacity: 0.65 ; }
10.struct, .enum { color: #7CB8BB; }
11.enum_variant { color: #BDE0F3; }
12.string_literal { color: #CC9393; }
13.field { color: #94BFF3; }
14.function { color: #93E0E3; }
15.function.unsafe { color: #BC8383; }
16.operator.unsafe { color: #BC8383; }
17.parameter { color: #94BFF3; }
18.text { color: #DCDCCC; }
19.type { color: #7CB8BB; }
20.builtin_type { color: #8CD0D3; }
21.type_param { color: #DFAF8F; }
22.attribute { color: #94BFF3; }
23.numeric_literal { color: #BFEBBF; }
24.bool_literal { color: #BFE6EB; }
25.macro { color: #94BFF3; }
26.module { color: #AFD8AF; }
27.variable { color: #DCDCCC; }
28.format_specifier { color: #CC696B; }
29.mutable { text-decoration: underline; }
30.unresolved_reference { color: #FC5555; }
31.escape_sequence { color: #94BFF3; }
32
33.keyword { color: #F0DFAF; font-weight: bold; }
34.keyword.unsafe { color: #BC8383; font-weight: bold; }
35.control { font-style: italic; }
36</style>
37<pre><code><span class="macro">macro_rules!</span> <span class="macro declaration">println</span> {
38 ($($arg:tt)*) =&gt; ({
39 $<span class="keyword">crate</span>::io::_print($<span class="keyword">crate</span>::format_args_nl!($($arg)*));
40 })
41}
42#[rustc_builtin_macro]
43<span class="macro">macro_rules!</span> <span class="macro declaration">format_args_nl</span> {
44 ($fmt:expr) =&gt; {{ <span class="comment">/* compiler built-in */</span> }};
45 ($fmt:expr, $($args:tt)*) =&gt; {{ <span class="comment">/* compiler built-in */</span> }};
46}
47
48<span class="keyword">fn</span> <span class="function declaration">main</span>() {
49 <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span>
50 <span class="macro">println!</span>(<span class="string_literal">"Hello"</span>); <span class="comment">// =&gt; "Hello"</span>
51 <span class="macro">println!</span>(<span class="string_literal">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>); <span class="comment">// =&gt; "Hello, world!"</span>
52 <span class="macro">println!</span>(<span class="string_literal">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>); <span class="comment">// =&gt; "The number is 1"</span>
53 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal">"</span>, (<span class="numeric_literal">3</span>, <span class="numeric_literal">4</span>)); <span class="comment">// =&gt; "(3, 4)"</span>
54 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal">"</span>, value=<span class="numeric_literal">4</span>); <span class="comment">// =&gt; "4"</span>
55 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "1 2"</span>
56 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">42</span>); <span class="comment">// =&gt; "0042" with leading zerosV</span>
57 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "2 1 1 2"</span>
58 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// =&gt; "test"</span>
59 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "2 1"</span>
60 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// =&gt; "a 3 b"</span>
61 <span class="macro">println!</span>(<span class="string_literal">"{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">}}"</span>, <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "{2}"</span>
62 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
63 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>);
64 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>);
65 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, width = <span class="numeric_literal">5</span>);
66 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
67 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
68 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
69 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
70 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
71 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>);
72 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
73 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, -<span class="numeric_literal">5</span>);
74 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>);
75 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>);
76 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>);
77 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
78 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
79 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
80 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, prec = <span class="numeric_literal">5</span>, number = <span class="numeric_literal">0.01</span>);
81 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 fractional digits"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="numeric_literal">1234.56</span>);
82 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>);
83 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 right-aligned characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>);
84 <span class="macro">println!</span>(<span class="string_literal">"Hello {{}}"</span>);
85 <span class="macro">println!</span>(<span class="string_literal">"{{ Hello"</span>);
86
87 <span class="macro">println!</span>(<span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>);
88
89 <span class="comment">// escape sequences</span>
90 <span class="macro">println!</span>(<span class="string_literal">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal">World"</span>);
91 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal"> World"</span>);
92
93 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span>, A = <span class="numeric_literal">92</span>);
94 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span>, ничоси = <span class="numeric_literal">92</span>);
95}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlight_unsafe.html b/crates/ra_ide/src/snapshots/highlight_unsafe.html
deleted file mode 100644
index 6936e949f..000000000
--- a/crates/ra_ide/src/snapshots/highlight_unsafe.html
+++ /dev/null
@@ -1,53 +0,0 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.documentation { color: #629755; }
9.injected { opacity: 0.65 ; }
10.struct, .enum { color: #7CB8BB; }
11.enum_variant { color: #BDE0F3; }
12.string_literal { color: #CC9393; }
13.field { color: #94BFF3; }
14.function { color: #93E0E3; }
15.function.unsafe { color: #BC8383; }
16.operator.unsafe { color: #BC8383; }
17.parameter { color: #94BFF3; }
18.text { color: #DCDCCC; }
19.type { color: #7CB8BB; }
20.builtin_type { color: #8CD0D3; }
21.type_param { color: #DFAF8F; }
22.attribute { color: #94BFF3; }
23.numeric_literal { color: #BFEBBF; }
24.bool_literal { color: #BFE6EB; }
25.macro { color: #94BFF3; }
26.module { color: #AFD8AF; }
27.variable { color: #DCDCCC; }
28.format_specifier { color: #CC696B; }
29.mutable { text-decoration: underline; }
30.unresolved_reference { color: #FC5555; }
31.escape_sequence { color: #94BFF3; }
32
33.keyword { color: #F0DFAF; font-weight: bold; }
34.keyword.unsafe { color: #BC8383; font-weight: bold; }
35.control { font-style: italic; }
36</style>
37<pre><code><span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span>() {}
38
39<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span>;
40
41<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> {
42 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_method</span>(&<span class="self_keyword">self</span>) {}
43}
44
45<span class="keyword">fn</span> <span class="function declaration">main</span>() {
46 <span class="keyword">let</span> <span class="variable declaration">x</span> = &<span class="numeric_literal">5</span> <span class="keyword">as</span> *<span class="keyword">const</span> <span class="builtin_type">usize</span>;
47 <span class="keyword unsafe">unsafe</span> {
48 <span class="function unsafe">unsafe_fn</span>();
49 <span class="struct">HasUnsafeFn</span>.<span class="function unsafe">unsafe_method</span>();
50 <span class="keyword">let</span> <span class="variable declaration">y</span> = <span class="operator unsafe">*</span>(<span class="variable">x</span>);
51 <span class="keyword">let</span> <span class="variable declaration">z</span> = -<span class="variable">x</span>;
52 }
53}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
deleted file mode 100644
index 8d0b38f95..000000000
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ /dev/null
@@ -1,117 +0,0 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.documentation { color: #629755; }
9.injected { opacity: 0.65 ; }
10.struct, .enum { color: #7CB8BB; }
11.enum_variant { color: #BDE0F3; }
12.string_literal { color: #CC9393; }
13.field { color: #94BFF3; }
14.function { color: #93E0E3; }
15.function.unsafe { color: #BC8383; }
16.operator.unsafe { color: #BC8383; }
17.parameter { color: #94BFF3; }
18.text { color: #DCDCCC; }
19.type { color: #7CB8BB; }
20.builtin_type { color: #8CD0D3; }
21.type_param { color: #DFAF8F; }
22.attribute { color: #94BFF3; }
23.numeric_literal { color: #BFEBBF; }
24.bool_literal { color: #BFE6EB; }
25.macro { color: #94BFF3; }
26.module { color: #AFD8AF; }
27.variable { color: #DCDCCC; }
28.format_specifier { color: #CC696B; }
29.mutable { text-decoration: underline; }
30.unresolved_reference { color: #FC5555; }
31.escape_sequence { color: #94BFF3; }
32
33.keyword { color: #F0DFAF; font-weight: bold; }
34.keyword.unsafe { color: #BC8383; font-weight: bold; }
35.control { font-style: italic; }
36</style>
37<pre><code><span class="attribute">#[</span><span class="function attribute">derive</span><span class="attribute">(Clone, Debug)]</span>
38<span class="keyword">struct</span> <span class="struct declaration">Foo</span> {
39 <span class="keyword">pub</span> <span class="field declaration">x</span>: <span class="builtin_type">i32</span>,
40 <span class="keyword">pub</span> <span class="field declaration">y</span>: <span class="builtin_type">i32</span>,
41}
42
43<span class="keyword">trait</span> <span class="trait declaration">Bar</span> {
44 <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">i32</span>;
45}
46
47<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> {
48 <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">i32</span> {
49 <span class="self_keyword">self</span>.<span class="field">x</span>
50 }
51}
52
53<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable">STATIC_MUT</span>: <span class="builtin_type">i32</span> = <span class="numeric_literal">0</span>;
54
55<span class="keyword">fn</span> <span class="function declaration">foo</span>&lt;<span class="lifetime declaration">'a</span>, <span class="type_param declaration">T</span>&gt;() -&gt; <span class="type_param">T</span> {
56 <span class="function">foo</span>::&lt;<span class="lifetime">'a</span>, <span class="builtin_type">i32</span>&gt;()
57}
58
59<span class="macro">macro_rules!</span> <span class="macro declaration">def_fn</span> {
60 ($($tt:tt)*) =&gt; {$($tt)*}
61}
62
63<span class="macro">def_fn!</span> {
64 <span class="keyword">fn</span> <span class="function declaration">bar</span>() -&gt; <span class="builtin_type">u32</span> {
65 <span class="numeric_literal">100</span>
66 }
67}
68
69<span class="macro">macro_rules!</span> <span class="macro declaration">noop</span> {
70 ($expr:expr) =&gt; {
71 $expr
72 }
73}
74
75<span class="comment">// comment</span>
76<span class="keyword">fn</span> <span class="function declaration">main</span>() {
77 <span class="macro">println!</span>(<span class="string_literal">"Hello, {}!"</span>, <span class="numeric_literal">92</span>);
78
79 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> = <span class="unresolved_reference">Vec</span>::<span class="unresolved_reference">new</span>();
80 <span class="keyword control">if</span> <span class="bool_literal">true</span> {
81 <span class="keyword">let</span> <span class="variable declaration">x</span> = <span class="numeric_literal">92</span>;
82 <span class="variable mutable">vec</span>.<span class="unresolved_reference">push</span>(<span class="struct">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="numeric_literal">1</span> });
83 }
84 <span class="keyword unsafe">unsafe</span> {
85 <span class="variable mutable">vec</span>.<span class="unresolved_reference">set_len</span>(<span class="numeric_literal">0</span>);
86 <span class="static mutable">STATIC_MUT</span> = <span class="numeric_literal">1</span>;
87 }
88
89 <span class="keyword control">for</span> <span class="variable declaration">e</span> <span class="keyword control">in</span> <span class="variable mutable">vec</span> {
90 <span class="comment">// Do nothing</span>
91 }
92
93 <span class="macro">noop!</span>(<span class="macro">noop</span><span class="macro">!</span>(<span class="numeric_literal">1</span>));
94
95 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> = <span class="numeric_literal">42</span>;
96 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>;
97 <span class="keyword">let</span> <span class="variable declaration">z</span> = &<span class="variable mutable">y</span>;
98
99 <span class="keyword">let</span> <span class="struct">Foo</span> { <span class="field">x</span>: <span class="variable declaration">z</span>, <span class="field">y</span> } = <span class="struct">Foo</span> { <span class="field">x</span>: <span class="variable">z</span>, <span class="field">y</span> };
100
101 <span class="variable">y</span>;
102}
103
104<span class="keyword">enum</span> <span class="enum declaration">Option</span>&lt;<span class="type_param declaration">T</span>&gt; {
105 <span class="enum_variant declaration">Some</span>(<span class="type_param">T</span>),
106 <span class="enum_variant declaration">None</span>,
107}
108<span class="keyword">use</span> <span class="enum">Option</span>::*;
109
110<span class="keyword">impl</span>&lt;<span class="type_param declaration">T</span>&gt; <span class="enum">Option</span>&lt;<span class="type_param">T</span>&gt; {
111 <span class="keyword">fn</span> <span class="function declaration">and</span>&lt;<span class="type_param declaration">U</span>&gt;(<span class="self_keyword">self</span>, <span class="variable declaration">other</span>: <span class="enum">Option</span>&lt;<span class="type_param">U</span>&gt;) -&gt; <span class="enum">Option</span>&lt;(<span class="type_param">T</span>, <span class="type_param">U</span>)&gt; {
112 <span class="keyword control">match</span> <span class="variable">other</span> {
113 <span class="enum_variant">None</span> =&gt; <span class="macro">unimplemented!</span>(),
114 <span class="variable declaration">Nope</span> =&gt; <span class="variable">Nope</span>,
115 }
116 }
117}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
deleted file mode 100644
index 9516c7441..000000000
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ /dev/null
@@ -1,48 +0,0 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.documentation { color: #629755; }
9.injected { opacity: 0.65 ; }
10.struct, .enum { color: #7CB8BB; }
11.enum_variant { color: #BDE0F3; }
12.string_literal { color: #CC9393; }
13.field { color: #94BFF3; }
14.function { color: #93E0E3; }
15.function.unsafe { color: #BC8383; }
16.operator.unsafe { color: #BC8383; }
17.parameter { color: #94BFF3; }
18.text { color: #DCDCCC; }
19.type { color: #7CB8BB; }
20.builtin_type { color: #8CD0D3; }
21.type_param { color: #DFAF8F; }
22.attribute { color: #94BFF3; }
23.numeric_literal { color: #BFEBBF; }
24.bool_literal { color: #BFE6EB; }
25.macro { color: #94BFF3; }
26.module { color: #AFD8AF; }
27.variable { color: #DCDCCC; }
28.format_specifier { color: #CC696B; }
29.mutable { text-decoration: underline; }
30.unresolved_reference { color: #FC5555; }
31.escape_sequence { color: #94BFF3; }
32
33.keyword { color: #F0DFAF; font-weight: bold; }
34.keyword.unsafe { color: #BC8383; font-weight: bold; }
35.control { font-style: italic; }
36</style>
37<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span>() {
38 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string_literal">"hello"</span>;
39 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(17,51%,74%);">x</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.<span class="unresolved_reference">to_string</span>();
40 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(127,76%,66%);">y</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.<span class="unresolved_reference">to_string</span>();
41
42 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span> = <span class="string_literal">"other color please!"</span>;
43 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(85,49%,84%);">y</span> = <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span>.<span class="unresolved_reference">to_string</span>();
44}
45
46<span class="keyword">fn</span> <span class="function declaration">bar</span>() {
47 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string_literal">"hello"</span>;
48}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 6cb96608b..4348b43be 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -1,15 +1,43 @@
1use ra_db::SourceDatabaseExt; 1use ra_db::{FilePosition, FileRange};
2use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; 2use ra_ide_db::RootDatabase;
3 3
4use crate::SourceFileEdit; 4use crate::SourceFileEdit;
5use ra_ssr::{MatchFinder, SsrError, SsrRule}; 5use ra_ssr::{MatchFinder, SsrError, SsrRule};
6 6
7// Feature: Structural Seach and Replace 7// Feature: Structural Search and Replace
8// 8//
9// Search and replace with named wildcards that will match any expression, type, path, pattern or item. 9// Search and replace with named wildcards that will match any expression, type, path, pattern or item.
10// The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`. 10// The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`.
11// A `$<name>` placeholder in the search pattern will match any AST node and `$<name>` will reference it in the replacement. 11// A `$<name>` placeholder in the search pattern will match any AST node and `$<name>` will reference it in the replacement.
12// Within a macro call, a placeholder will match up until whatever token follows the placeholder. 12// Within a macro call, a placeholder will match up until whatever token follows the placeholder.
13//
14// All paths in both the search pattern and the replacement template must resolve in the context
15// in which this command is invoked. Paths in the search pattern will then match the code if they
16// resolve to the same item, even if they're written differently. For example if we invoke the
17// command in the module `foo` with a pattern of `Bar`, then code in the parent module that refers
18// to `foo::Bar` will match.
19//
20// Paths in the replacement template will be rendered appropriately for the context in which the
21// replacement occurs. For example if our replacement template is `foo::Bar` and we match some
22// code in the `foo` module, we'll insert just `Bar`.
23//
24// Method calls should generally be written in UFCS form. e.g. `foo::Bar::baz($s, $a)` will match
25// `$s.baz($a)`, provided the method call `baz` resolves to the method `foo::Bar::baz`.
26//
27// The scope of the search / replace will be restricted to the current selection if any, otherwise
28// it will apply to the whole workspace.
29//
30// Placeholders may be given constraints by writing them as `${<name>:<constraint1>:<constraint2>...}`.
31//
32// Supported constraints:
33//
34// |===
35// | Constraint | Restricts placeholder
36//
37// | kind(literal) | Is a literal (e.g. `42` or `"forty two"`)
38// | not(a) | Negates the constraint `a`
39// |===
40//
13// Available via the command `rust-analyzer.ssr`. 41// Available via the command `rust-analyzer.ssr`.
14// 42//
15// ```rust 43// ```rust
@@ -31,21 +59,14 @@ pub fn parse_search_replace(
31 rule: &str, 59 rule: &str,
32 parse_only: bool, 60 parse_only: bool,
33 db: &RootDatabase, 61 db: &RootDatabase,
62 resolve_context: FilePosition,
63 selections: Vec<FileRange>,
34) -> Result<Vec<SourceFileEdit>, SsrError> { 64) -> Result<Vec<SourceFileEdit>, SsrError> {
35 let mut edits = vec![];
36 let rule: SsrRule = rule.parse()?; 65 let rule: SsrRule = rule.parse()?;
66 let mut match_finder = MatchFinder::in_context(db, resolve_context, selections);
67 match_finder.add_rule(rule)?;
37 if parse_only { 68 if parse_only {
38 return Ok(edits); 69 return Ok(Vec::new());
39 }
40 let mut match_finder = MatchFinder::new(db);
41 match_finder.add_rule(rule);
42 for &root in db.local_roots().iter() {
43 let sr = db.source_root(root);
44 for file_id in sr.iter() {
45 if let Some(edit) = match_finder.edits_for_file(file_id) {
46 edits.push(SourceFileEdit { file_id, edit });
47 }
48 }
49 } 70 }
50 Ok(edits) 71 Ok(match_finder.edits())
51} 72}
diff --git a/crates/ra_ide/src/status.rs b/crates/ra_ide/src/status.rs
index 45411b357..08e6f69cb 100644
--- a/crates/ra_ide/src/status.rs
+++ b/crates/ra_ide/src/status.rs
@@ -2,10 +2,7 @@ use std::{fmt, iter::FromIterator, sync::Arc};
2 2
3use hir::MacroFile; 3use hir::MacroFile;
4use ra_db::{ 4use ra_db::{
5 salsa::{ 5 salsa::debug::{DebugQueryTable, TableEntry},
6 debug::{DebugQueryTable, TableEntry},
7 Database,
8 },
9 FileTextQuery, SourceRootId, 6 FileTextQuery, SourceRootId,
10}; 7};
11use ra_ide_db::{ 8use ra_ide_db::{
@@ -14,15 +11,15 @@ use ra_ide_db::{
14}; 11};
15use ra_prof::{memory_usage, Bytes}; 12use ra_prof::{memory_usage, Bytes};
16use ra_syntax::{ast, Parse, SyntaxNode}; 13use ra_syntax::{ast, Parse, SyntaxNode};
14use rustc_hash::FxHashMap;
17 15
18use crate::FileId; 16use crate::FileId;
19use rustc_hash::FxHashMap;
20 17
21fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { 18fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
22 db.query(ra_db::ParseQuery).entries::<SyntaxTreeStats>() 19 ra_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>()
23} 20}
24fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { 21fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
25 db.query(hir::db::ParseMacroQuery).entries::<SyntaxTreeStats>() 22 hir::db::ParseMacroQuery.in_db(db).entries::<SyntaxTreeStats>()
26} 23}
27 24
28// Feature: Status 25// Feature: Status
@@ -35,10 +32,10 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
35// | VS Code | **Rust Analyzer: Status** 32// | VS Code | **Rust Analyzer: Status**
36// |=== 33// |===
37pub(crate) fn status(db: &RootDatabase) -> String { 34pub(crate) fn status(db: &RootDatabase) -> String {
38 let files_stats = db.query(FileTextQuery).entries::<FilesStats>(); 35 let files_stats = FileTextQuery.in_db(db).entries::<FilesStats>();
39 let syntax_tree_stats = syntax_tree_stats(db); 36 let syntax_tree_stats = syntax_tree_stats(db);
40 let macro_syntax_tree_stats = macro_syntax_tree_stats(db); 37 let macro_syntax_tree_stats = macro_syntax_tree_stats(db);
41 let symbols_stats = db.query(LibrarySymbolsQuery).entries::<LibrarySymbolsStats>(); 38 let symbols_stats = LibrarySymbolsQuery.in_db(db).entries::<LibrarySymbolsStats>();
42 format!( 39 format!(
43 "{}\n{}\n{}\n{} (macros)\n\n\nmemory:\n{}\ngc {:?} seconds ago", 40 "{}\n{}\n{}\n{} (macros)\n\n\nmemory:\n{}\ngc {:?} seconds ago",
44 files_stats, 41 files_stats,
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 028b55902..e3a96f9d5 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -464,7 +464,7 @@ fn highlight_element(
464 let db = sema.db; 464 let db = sema.db;
465 let mut binding_hash = None; 465 let mut binding_hash = None;
466 let highlight: Highlight = match element.kind() { 466 let highlight: Highlight = match element.kind() {
467 FN_DEF => { 467 FN => {
468 bindings_shadow_count.clear(); 468 bindings_shadow_count.clear();
469 return None; 469 return None;
470 } 470 }
@@ -539,20 +539,52 @@ fn highlight_element(
539 _ => h, 539 _ => h,
540 } 540 }
541 } 541 }
542 T![*] => { 542 p if p.is_punct() => match p {
543 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; 543 T![::] | T![->] | T![=>] | T![&] | T![..] | T![=] | T![@] => {
544 544 HighlightTag::Operator.into()
545 let expr = prefix_expr.expr()?;
546 let ty = sema.type_of_expr(&expr)?;
547 if !ty.is_raw_ptr() {
548 return None;
549 } else {
550 HighlightTag::Operator | HighlightModifier::Unsafe
551 } 545 }
552 } 546 T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => {
553 T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => { 547 HighlightTag::Macro.into()
554 Highlight::new(HighlightTag::Macro) 548 }
555 } 549 T![*] if element.parent().and_then(ast::PointerType::cast).is_some() => {
550 HighlightTag::Keyword.into()
551 }
552 T![*] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
553 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
554
555 let expr = prefix_expr.expr()?;
556 let ty = sema.type_of_expr(&expr)?;
557 if ty.is_raw_ptr() {
558 HighlightTag::Operator | HighlightModifier::Unsafe
559 } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() {
560 HighlightTag::Operator.into()
561 } else {
562 HighlightTag::Punctuation.into()
563 }
564 }
565 T![-] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
566 HighlightTag::NumericLiteral.into()
567 }
568 _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
569 HighlightTag::Operator.into()
570 }
571 _ if element.parent().and_then(ast::BinExpr::cast).is_some() => {
572 HighlightTag::Operator.into()
573 }
574 _ if element.parent().and_then(ast::RangeExpr::cast).is_some() => {
575 HighlightTag::Operator.into()
576 }
577 _ if element.parent().and_then(ast::RangePat::cast).is_some() => {
578 HighlightTag::Operator.into()
579 }
580 _ if element.parent().and_then(ast::DotDotPat::cast).is_some() => {
581 HighlightTag::Operator.into()
582 }
583 _ if element.parent().and_then(ast::Attr::cast).is_some() => {
584 HighlightTag::Attribute.into()
585 }
586 _ => HighlightTag::Punctuation.into(),
587 },
556 588
557 k if k.is_keyword() => { 589 k if k.is_keyword() => {
558 let h = Highlight::new(HighlightTag::Keyword); 590 let h = Highlight::new(HighlightTag::Keyword);
@@ -566,10 +598,31 @@ fn highlight_element(
566 | T![return] 598 | T![return]
567 | T![while] 599 | T![while]
568 | T![in] => h | HighlightModifier::ControlFlow, 600 | T![in] => h | HighlightModifier::ControlFlow,
569 T![for] if !is_child_of_impl(element) => h | HighlightModifier::ControlFlow, 601 T![for] if !is_child_of_impl(&element) => h | HighlightModifier::ControlFlow,
570 T![unsafe] => h | HighlightModifier::Unsafe, 602 T![unsafe] => h | HighlightModifier::Unsafe,
571 T![true] | T![false] => HighlightTag::BoolLiteral.into(), 603 T![true] | T![false] => HighlightTag::BoolLiteral.into(),
572 T![self] => HighlightTag::SelfKeyword.into(), 604 T![self] => {
605 let self_param_is_mut = element
606 .parent()
607 .and_then(ast::SelfParam::cast)
608 .and_then(|p| p.mut_token())
609 .is_some();
610 // closure to enforce lazyness
611 let self_path = || {
612 sema.resolve_path(&element.parent()?.parent().and_then(ast::Path::cast)?)
613 };
614 if self_param_is_mut
615 || matches!(self_path(),
616 Some(hir::PathResolution::Local(local))
617 if local.is_self(db)
618 && (local.is_mut(db) || local.ty(db).is_mutable_reference())
619 )
620 {
621 HighlightTag::SelfKeyword | HighlightModifier::Mutable
622 } else {
623 HighlightTag::SelfKeyword.into()
624 }
625 }
573 _ => h, 626 _ => h,
574 } 627 }
575 } 628 }
@@ -592,9 +645,9 @@ fn highlight_element(
592 } 645 }
593} 646}
594 647
595fn is_child_of_impl(element: SyntaxElement) -> bool { 648fn is_child_of_impl(element: &SyntaxElement) -> bool {
596 match element.parent() { 649 match element.parent() {
597 Some(e) => e.kind() == IMPL_DEF, 650 Some(e) => e.kind() == IMPL,
598 _ => false, 651 _ => false,
599 } 652 }
600} 653}
@@ -630,9 +683,10 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight {
630 }, 683 },
631 Definition::SelfType(_) => HighlightTag::SelfType, 684 Definition::SelfType(_) => HighlightTag::SelfType,
632 Definition::TypeParam(_) => HighlightTag::TypeParam, 685 Definition::TypeParam(_) => HighlightTag::TypeParam,
633 // FIXME: distinguish between locals and parameters
634 Definition::Local(local) => { 686 Definition::Local(local) => {
635 let mut h = Highlight::new(HighlightTag::Local); 687 let tag =
688 if local.is_param(db) { HighlightTag::ValueParam } else { HighlightTag::Local };
689 let mut h = Highlight::new(tag);
636 if local.is_mut(db) || local.ty(db).is_mutable_reference() { 690 if local.is_mut(db) || local.ty(db).is_mutable_reference() {
637 h |= HighlightModifier::Mutable; 691 h |= HighlightModifier::Mutable;
638 } 692 }
@@ -651,18 +705,18 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
651 }; 705 };
652 706
653 let tag = match parent.kind() { 707 let tag = match parent.kind() {
654 STRUCT_DEF => HighlightTag::Struct, 708 STRUCT => HighlightTag::Struct,
655 ENUM_DEF => HighlightTag::Enum, 709 ENUM => HighlightTag::Enum,
656 UNION_DEF => HighlightTag::Union, 710 UNION => HighlightTag::Union,
657 TRAIT_DEF => HighlightTag::Trait, 711 TRAIT => HighlightTag::Trait,
658 TYPE_ALIAS_DEF => HighlightTag::TypeAlias, 712 TYPE_ALIAS => HighlightTag::TypeAlias,
659 TYPE_PARAM => HighlightTag::TypeParam, 713 TYPE_PARAM => HighlightTag::TypeParam,
660 RECORD_FIELD_DEF => HighlightTag::Field, 714 RECORD_FIELD => HighlightTag::Field,
661 MODULE => HighlightTag::Module, 715 MODULE => HighlightTag::Module,
662 FN_DEF => HighlightTag::Function, 716 FN => HighlightTag::Function,
663 CONST_DEF => HighlightTag::Constant, 717 CONST => HighlightTag::Constant,
664 STATIC_DEF => HighlightTag::Static, 718 STATIC => HighlightTag::Static,
665 ENUM_VARIANT => HighlightTag::EnumVariant, 719 VARIANT => HighlightTag::EnumVariant,
666 BIND_PAT => HighlightTag::Local, 720 BIND_PAT => HighlightTag::Local,
667 _ => default, 721 _ => default,
668 }; 722 };
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs
index 0c74f7370..a5e7d2867 100644
--- a/crates/ra_ide/src/syntax_highlighting/html.rs
+++ b/crates/ra_ide/src/syntax_highlighting/html.rs
@@ -1,5 +1,6 @@
1//! Renders a bit of code as HTML. 1//! Renders a bit of code as HTML.
2 2
3use oorandom::Rand32;
3use ra_db::SourceDatabase; 4use ra_db::SourceDatabase;
4use ra_syntax::{AstNode, TextRange, TextSize}; 5use ra_syntax::{AstNode, TextRange, TextSize};
5 6
@@ -9,13 +10,12 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
9 let parse = db.parse(file_id); 10 let parse = db.parse(file_id);
10 11
11 fn rainbowify(seed: u64) -> String { 12 fn rainbowify(seed: u64) -> String {
12 use rand::prelude::*; 13 let mut rng = Rand32::new(seed);
13 let mut rng = SmallRng::seed_from_u64(seed);
14 format!( 14 format!(
15 "hsl({h},{s}%,{l}%)", 15 "hsl({h},{s}%,{l}%)",
16 h = rng.gen_range::<u16, _, _>(0, 361), 16 h = rng.rand_range(0..361),
17 s = rng.gen_range::<u16, _, _>(42, 99), 17 s = rng.rand_range(42..99),
18 l = rng.gen_range::<u16, _, _>(40, 91), 18 l = rng.rand_range(40..91),
19 ) 19 )
20 } 20 }
21 21
@@ -83,14 +83,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
83.bool_literal { color: #BFE6EB; } 83.bool_literal { color: #BFE6EB; }
84.macro { color: #94BFF3; } 84.macro { color: #94BFF3; }
85.module { color: #AFD8AF; } 85.module { color: #AFD8AF; }
86.value_param { color: #DCDCCC; }
86.variable { color: #DCDCCC; } 87.variable { color: #DCDCCC; }
87.format_specifier { color: #CC696B; } 88.format_specifier { color: #CC696B; }
88.mutable { text-decoration: underline; } 89.mutable { text-decoration: underline; }
89.unresolved_reference { color: #FC5555; }
90.escape_sequence { color: #94BFF3; } 90.escape_sequence { color: #94BFF3; }
91
92.keyword { color: #F0DFAF; font-weight: bold; } 91.keyword { color: #F0DFAF; font-weight: bold; }
93.keyword.unsafe { color: #BC8383; font-weight: bold; } 92.keyword.unsafe { color: #BC8383; font-weight: bold; }
94.control { font-style: italic; } 93.control { font-style: italic; }
94
95.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
95</style> 96</style>
96"; 97";
diff --git a/crates/ra_ide/src/syntax_highlighting/injection.rs b/crates/ra_ide/src/syntax_highlighting/injection.rs
index 181c21256..8665b480f 100644
--- a/crates/ra_ide/src/syntax_highlighting/injection.rs
+++ b/crates/ra_ide/src/syntax_highlighting/injection.rs
@@ -25,7 +25,7 @@ pub(super) fn highlight_injection(
25 return None; 25 return None;
26 } 26 }
27 let value = literal.value()?; 27 let value = literal.value()?;
28 let (analysis, tmp_file_id) = Analysis::from_single_file(value); 28 let (analysis, tmp_file_id) = Analysis::from_single_file(value.into_owned());
29 29
30 if let Some(range) = literal.open_quote_text_range() { 30 if let Some(range) = literal.open_quote_text_range() {
31 acc.add(HighlightedRange { 31 acc.add(HighlightedRange {
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index 13d9dd195..49ec94bdc 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -32,6 +32,7 @@ pub enum HighlightTag {
32 Macro, 32 Macro,
33 Module, 33 Module,
34 NumericLiteral, 34 NumericLiteral,
35 Punctuation,
35 SelfKeyword, 36 SelfKeyword,
36 SelfType, 37 SelfType,
37 Static, 38 Static,
@@ -41,6 +42,7 @@ pub enum HighlightTag {
41 TypeAlias, 42 TypeAlias,
42 TypeParam, 43 TypeParam,
43 Union, 44 Union,
45 ValueParam,
44 Local, 46 Local,
45 UnresolvedReference, 47 UnresolvedReference,
46 FormatSpecifier, 48 FormatSpecifier,
@@ -82,6 +84,7 @@ impl HighlightTag {
82 HighlightTag::Generic => "generic", 84 HighlightTag::Generic => "generic",
83 HighlightTag::Keyword => "keyword", 85 HighlightTag::Keyword => "keyword",
84 HighlightTag::Lifetime => "lifetime", 86 HighlightTag::Lifetime => "lifetime",
87 HighlightTag::Punctuation => "punctuation",
85 HighlightTag::Macro => "macro", 88 HighlightTag::Macro => "macro",
86 HighlightTag::Module => "module", 89 HighlightTag::Module => "module",
87 HighlightTag::NumericLiteral => "numeric_literal", 90 HighlightTag::NumericLiteral => "numeric_literal",
@@ -95,6 +98,7 @@ impl HighlightTag {
95 HighlightTag::TypeAlias => "type_alias", 98 HighlightTag::TypeAlias => "type_alias",
96 HighlightTag::TypeParam => "type_param", 99 HighlightTag::TypeParam => "type_param",
97 HighlightTag::Union => "union", 100 HighlightTag::Union => "union",
101 HighlightTag::ValueParam => "value_param",
98 HighlightTag::Local => "variable", 102 HighlightTag::Local => "variable",
99 HighlightTag::UnresolvedReference => "unresolved_reference", 103 HighlightTag::UnresolvedReference => "unresolved_reference",
100 } 104 }
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index b7fad9719..87a6e2523 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -1,6 +1,7 @@
1use std::fs; 1use std::fs;
2 2
3use test_utils::{assert_eq_text, project_dir, read_text}; 3use expect::{expect_file, ExpectFile};
4use test_utils::project_dir;
4 5
5use crate::{mock_analysis::single_file, FileRange, TextRange}; 6use crate::{mock_analysis::single_file, FileRange, TextRange};
6 7
@@ -24,6 +25,16 @@ impl Bar for Foo {
24 } 25 }
25} 26}
26 27
28impl Foo {
29 fn baz(mut self) -> i32 {
30 self.x
31 }
32
33 fn qux(&mut self) {
34 self.x = 0;
35 }
36}
37
27static mut STATIC_MUT: i32 = 0; 38static mut STATIC_MUT: i32 = 0;
28 39
29fn foo<'a, T>() -> T { 40fn foo<'a, T>() -> T {
@@ -91,7 +102,7 @@ impl<T> Option<T> {
91} 102}
92"# 103"#
93 .trim(), 104 .trim(),
94 "crates/ra_ide/src/snapshots/highlighting.html", 105 expect_file!["crates/ra_ide/test_data/highlighting.html"],
95 false, 106 false,
96 ); 107 );
97} 108}
@@ -114,7 +125,7 @@ fn bar() {
114} 125}
115"# 126"#
116 .trim(), 127 .trim(),
117 "crates/ra_ide/src/snapshots/rainbow_highlighting.html", 128 expect_file!["crates/ra_ide/test_data/rainbow_highlighting.html"],
118 true, 129 true,
119 ); 130 );
120} 131}
@@ -167,7 +178,7 @@ fn main() {
167 ); 178 );
168}"## 179}"##
169 .trim(), 180 .trim(),
170 "crates/ra_ide/src/snapshots/highlight_injection.html", 181 expect_file!["crates/ra_ide/test_data/highlight_injection.html"],
171 false, 182 false,
172 ); 183 );
173} 184}
@@ -250,7 +261,7 @@ fn main() {
250 println!("{ничоси}", ничоси = 92); 261 println!("{ничоси}", ничоси = 92);
251}"# 262}"#
252 .trim(), 263 .trim(),
253 "crates/ra_ide/src/snapshots/highlight_strings.html", 264 expect_file!["crates/ra_ide/test_data/highlight_strings.html"],
254 false, 265 false,
255 ); 266 );
256} 267}
@@ -278,7 +289,7 @@ fn main() {
278} 289}
279"# 290"#
280 .trim(), 291 .trim(),
281 "crates/ra_ide/src/snapshots/highlight_unsafe.html", 292 expect_file!["crates/ra_ide/test_data/highlight_unsafe.html"],
282 false, 293 false,
283 ); 294 );
284} 295}
@@ -354,7 +365,7 @@ macro_rules! noop {
354} 365}
355"# 366"#
356 .trim(), 367 .trim(),
357 "crates/ra_ide/src/snapshots/highlight_doctest.html", 368 expect_file!["crates/ra_ide/test_data/highlight_doctest.html"],
358 false, 369 false,
359 ); 370 );
360} 371}
@@ -362,11 +373,8 @@ macro_rules! noop {
362/// Highlights the code given by the `ra_fixture` argument, renders the 373/// Highlights the code given by the `ra_fixture` argument, renders the
363/// result as HTML, and compares it with the HTML file given as `snapshot`. 374/// result as HTML, and compares it with the HTML file given as `snapshot`.
364/// Note that the `snapshot` file is overwritten by the rendered HTML. 375/// Note that the `snapshot` file is overwritten by the rendered HTML.
365fn check_highlighting(ra_fixture: &str, snapshot: &str, rainbow: bool) { 376fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) {
366 let (analysis, file_id) = single_file(ra_fixture); 377 let (analysis, file_id) = single_file(ra_fixture);
367 let dst_file = project_dir().join(snapshot);
368 let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap(); 378 let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
369 let expected_html = &read_text(&dst_file); 379 expect.assert_eq(actual_html)
370 fs::write(dst_file, &actual_html).unwrap();
371 assert_eq_text!(expected_html, actual_html);
372} 380}
diff --git a/crates/ra_ide/src/syntax_tree.rs b/crates/ra_ide/src/syntax_tree.rs
index f716a3861..07217e808 100644
--- a/crates/ra_ide/src/syntax_tree.rs
+++ b/crates/ra_ide/src/syntax_tree.rs
@@ -116,7 +116,7 @@ mod tests {
116 syn.trim(), 116 syn.trim(),
117 r#" 117 r#"
118[email protected] 118[email protected]
119 FN_DEF@0..11 119 [email protected]
120 [email protected] "fn" 120 [email protected] "fn"
121 [email protected] " " 121 [email protected] " "
122 [email protected] 122 [email protected]
@@ -148,7 +148,7 @@ fn test() {
148 syn.trim(), 148 syn.trim(),
149 r#" 149 r#"
150[email protected] 150[email protected]
151 FN_DEF@0..60 151 [email protected]
152 [email protected] "fn" 152 [email protected] "fn"
153 [email protected] " " 153 [email protected] " "
154 [email protected] 154 [email protected]
@@ -190,7 +190,7 @@ [email protected]
190 assert_eq_text!( 190 assert_eq_text!(
191 syn.trim(), 191 syn.trim(),
192 r#" 192 r#"
193FN_DEF@0..11 193[email protected]
194 [email protected] "fn" 194 [email protected] "fn"
195 [email protected] " " 195 [email protected] " "
196 [email protected] 196 [email protected]
@@ -258,7 +258,7 @@ fn bar() {
258 syn.trim(), 258 syn.trim(),
259 r#" 259 r#"
260[email protected] 260[email protected]
261 FN_DEF@0..12 261 [email protected]
262 [email protected] "fn" 262 [email protected] "fn"
263 [email protected] " " 263 [email protected] " "
264 [email protected] 264 [email protected]
@@ -292,7 +292,7 @@ fn bar() {
292 syn.trim(), 292 syn.trim(),
293 r#" 293 r#"
294[email protected] 294[email protected]
295 FN_DEF@0..12 295 [email protected]
296 [email protected] "fn" 296 [email protected] "fn"
297 [email protected] " " 297 [email protected] " "
298 [email protected] 298 [email protected]
@@ -325,7 +325,7 @@ fn bar() {
325 syn.trim(), 325 syn.trim(),
326 r#" 326 r#"
327[email protected] 327[email protected]
328 FN_DEF@0..12 328 [email protected]
329 [email protected] "fn" 329 [email protected] "fn"
330 [email protected] " " 330 [email protected] " "
331 [email protected] 331 [email protected]
@@ -339,7 +339,7 @@ [email protected]
339 [email protected] "\n" 339 [email protected] "\n"
340 [email protected] "}" 340 [email protected] "}"
341 [email protected] "\n" 341 [email protected] "\n"
342 FN_DEF@13..25 342 [email protected]
343 [email protected] "fn" 343 [email protected] "fn"
344 [email protected] " " 344 [email protected] " "
345 [email protected] 345 [email protected]
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs
index 83776d2b6..d3ce744b4 100644
--- a/crates/ra_ide/src/typing.rs
+++ b/crates/ra_ide/src/typing.rs
@@ -39,7 +39,6 @@ pub(crate) const TRIGGER_CHARS: &str = ".=>";
39// Some features trigger on typing certain characters: 39// Some features trigger on typing certain characters:
40// 40//
41// - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression 41// - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression
42// - Enter inside comments automatically inserts `///`
43// - typing `.` in a chain method call auto-indents 42// - typing `.` in a chain method call auto-indents
44pub(crate) fn on_char_typed( 43pub(crate) fn on_char_typed(
45 db: &RootDatabase, 44 db: &RootDatabase,
diff --git a/crates/ra_ide/src/typing/on_enter.rs b/crates/ra_ide/src/typing/on_enter.rs
index 2faaa8ff0..143b1ae41 100644
--- a/crates/ra_ide/src/typing/on_enter.rs
+++ b/crates/ra_ide/src/typing/on_enter.rs
@@ -7,10 +7,31 @@ use ra_syntax::{
7 ast::{self, AstToken}, 7 ast::{self, AstToken},
8 AstNode, SmolStr, SourceFile, 8 AstNode, SmolStr, SourceFile,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxToken, TextSize, TokenAtOffset, 10 SyntaxToken, TextRange, TextSize, TokenAtOffset,
11}; 11};
12use ra_text_edit::TextEdit; 12use ra_text_edit::TextEdit;
13use test_utils::mark;
13 14
15// Feature: On Enter
16//
17// rust-analyzer can override kbd:[Enter] key to make it smarter:
18//
19// - kbd:[Enter] inside triple-slash comments automatically inserts `///`
20// - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//`
21//
22// This action needs to be assigned to shortcut explicitly.
23//
24// VS Code::
25//
26// Add the following to `keybindings.json`:
27// [source,json]
28// ----
29// {
30// "key": "Enter",
31// "command": "rust-analyzer.onEnter",
32// "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust"
33// }
34// ----
14pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> { 35pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> {
15 let parse = db.parse(position.file_id); 36 let parse = db.parse(position.file_id);
16 let file = parse.tree(); 37 let file = parse.tree();
@@ -30,15 +51,25 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text
30 return None; 51 return None;
31 } 52 }
32 53
54 let mut remove_last_space = false;
33 // Continuing single-line non-doc comments (like this one :) ) is annoying 55 // Continuing single-line non-doc comments (like this one :) ) is annoying
34 if prefix == "//" && comment_range.end() == position.offset && !followed_by_comment(&comment) { 56 if prefix == "//" && comment_range.end() == position.offset {
35 return None; 57 if comment.text().ends_with(' ') {
58 mark::hit!(continues_end_of_line_comment_with_space);
59 remove_last_space = true;
60 } else if !followed_by_comment(&comment) {
61 return None;
62 }
36 } 63 }
37 64
38 let indent = node_indent(&file, comment.syntax())?; 65 let indent = node_indent(&file, comment.syntax())?;
39 let inserted = format!("\n{}{} $0", indent, prefix); 66 let inserted = format!("\n{}{} $0", indent, prefix);
40 let edit = TextEdit::insert(position.offset, inserted); 67 let delete = if remove_last_space {
41 68 TextRange::new(position.offset - TextSize::of(' '), position.offset)
69 } else {
70 TextRange::empty(position.offset)
71 };
72 let edit = TextEdit::replace(delete, inserted);
42 Some(edit) 73 Some(edit)
43} 74}
44 75
@@ -75,10 +106,10 @@ fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
75 106
76#[cfg(test)] 107#[cfg(test)]
77mod tests { 108mod tests {
78 use test_utils::assert_eq_text; 109 use stdx::trim_indent;
110 use test_utils::{assert_eq_text, mark};
79 111
80 use crate::mock_analysis::analysis_and_position; 112 use crate::mock_analysis::analysis_and_position;
81 use stdx::trim_indent;
82 113
83 fn apply_on_enter(before: &str) -> Option<String> { 114 fn apply_on_enter(before: &str) -> Option<String> {
84 let (analysis, position) = analysis_and_position(&before); 115 let (analysis, position) = analysis_and_position(&before);
@@ -192,7 +223,7 @@ fn main() {
192 } 223 }
193 224
194 #[test] 225 #[test]
195 fn does_not_continue_end_of_code_comment() { 226 fn does_not_continue_end_of_line_comment() {
196 do_check_noop( 227 do_check_noop(
197 r" 228 r"
198fn main() { 229fn main() {
@@ -202,4 +233,24 @@ fn main() {
202", 233",
203 ); 234 );
204 } 235 }
236
237 #[test]
238 fn continues_end_of_line_comment_with_space() {
239 mark::check!(continues_end_of_line_comment_with_space);
240 do_check(
241 r#"
242fn main() {
243 // Fix me <|>
244 let x = 1 + 1;
245}
246"#,
247 r#"
248fn main() {
249 // Fix me
250 // $0
251 let x = 1 + 1;
252}
253"#,
254 );
255 }
205} 256}