aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/Cargo.toml42
-rw-r--r--crates/ra_ide/src/call_hierarchy.rs393
-rw-r--r--crates/ra_ide/src/call_info.rs742
-rw-r--r--crates/ra_ide/src/completion.rs206
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs644
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs416
-rw-r--r--crates/ra_ide/src/completion/complete_fn_param.rs135
-rw-r--r--crates/ra_ide/src/completion/complete_keyword.rs536
-rw-r--r--crates/ra_ide/src/completion/complete_macro_in_item_position.rs41
-rw-r--r--crates/ra_ide/src/completion/complete_pattern.rs88
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs378
-rw-r--r--crates/ra_ide/src/completion/complete_qualified_path.rs733
-rw-r--r--crates/ra_ide/src/completion/complete_record.rs226
-rw-r--r--crates/ra_ide/src/completion/complete_snippet.rs116
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs488
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs658
-rw-r--r--crates/ra_ide/src/completion/completion_config.rs35
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs465
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs384
-rw-r--r--crates/ra_ide/src/completion/patterns.rs194
-rw-r--r--crates/ra_ide/src/completion/presentation.rs1230
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs114
-rw-r--r--crates/ra_ide/src/diagnostics.rs797
-rw-r--r--crates/ra_ide/src/display.rs83
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs491
-rw-r--r--crates/ra_ide/src/display/short_label.rs101
-rw-r--r--crates/ra_ide/src/expand_macro.rs283
-rw-r--r--crates/ra_ide/src/extend_selection.rs654
-rw-r--r--crates/ra_ide/src/file_structure.rs431
-rw-r--r--crates/ra_ide/src/folding_ranges.rs401
-rw-r--r--crates/ra_ide/src/goto_definition.rs967
-rw-r--r--crates/ra_ide/src/goto_implementation.rs229
-rw-r--r--crates/ra_ide/src/goto_type_definition.rs151
-rw-r--r--crates/ra_ide/src/hover.rs2866
-rw-r--r--crates/ra_ide/src/inlay_hints.rs918
-rw-r--r--crates/ra_ide/src/join_lines.rs750
-rw-r--r--crates/ra_ide/src/lib.rs535
-rw-r--r--crates/ra_ide/src/link_rewrite.rs79
-rw-r--r--crates/ra_ide/src/markup.rs38
-rw-r--r--crates/ra_ide/src/matching_brace.rs73
-rw-r--r--crates/ra_ide/src/mock_analysis.rs176
-rw-r--r--crates/ra_ide/src/parent_module.rs155
-rw-r--r--crates/ra_ide/src/prime_caches.rs12
-rw-r--r--crates/ra_ide/src/references.rs695
-rw-r--r--crates/ra_ide/src/references/rename.rs1010
-rw-r--r--crates/ra_ide/src/runnables.rs883
-rw-r--r--crates/ra_ide/src/ssr.rs72
-rw-r--r--crates/ra_ide/src/status.rs145
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs775
-rw-r--r--crates/ra_ide/src/syntax_highlighting/html.rs97
-rw-r--r--crates/ra_ide/src/syntax_highlighting/injection.rs188
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs203
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs380
-rw-r--r--crates/ra_ide/src/syntax_tree.rs359
-rw-r--r--crates/ra_ide/src/typing.rs365
-rw-r--r--crates/ra_ide/src/typing/on_enter.rs256
-rw-r--r--crates/ra_ide/test_data/highlight_doctest.html102
-rw-r--r--crates/ra_ide/test_data/highlight_injection.html48
-rw-r--r--crates/ra_ide/test_data/highlight_strings.html96
-rw-r--r--crates/ra_ide/test_data/highlight_unsafe.html54
-rw-r--r--crates/ra_ide/test_data/highlighting.html128
-rw-r--r--crates/ra_ide/test_data/rainbow_highlighting.html49
62 files changed, 0 insertions, 24359 deletions
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
deleted file mode 100644
index de606f57e..000000000
--- a/crates/ra_ide/Cargo.toml
+++ /dev/null
@@ -1,42 +0,0 @@
1[package]
2edition = "2018"
3name = "ra_ide"
4version = "0.1.0"
5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
7
8[lib]
9doctest = false
10
11[features]
12wasm = []
13
14[dependencies]
15either = "1.5.3"
16indexmap = "1.3.2"
17itertools = "0.9.0"
18log = "0.4.8"
19rustc-hash = "1.1.0"
20oorandom = "11.1.2"
21pulldown-cmark-to-cmark = "5.0.0"
22pulldown-cmark = {version = "0.7.2", default-features = false}
23
24stdx = { path = "../stdx" }
25
26ra_syntax = { path = "../ra_syntax" }
27ra_text_edit = { path = "../ra_text_edit" }
28ra_db = { path = "../ra_db" }
29ra_ide_db = { path = "../ra_ide_db" }
30ra_cfg = { path = "../ra_cfg" }
31ra_fmt = { path = "../ra_fmt" }
32ra_prof = { path = "../ra_prof" }
33test_utils = { path = "../test_utils" }
34ra_assists = { path = "../ra_assists" }
35ra_ssr = { path = "../ra_ssr" }
36
37# ra_ide should depend only on the top-level `hir` package. if you need
38# something from some `hir_xxx` subpackage, reexport the API via `hir`.
39hir = { path = "../ra_hir", package = "ra_hir" }
40
41[dev-dependencies]
42expect = { path = "../expect" }
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs
deleted file mode 100644
index 1fcaf4a32..000000000
--- a/crates/ra_ide/src/call_hierarchy.rs
+++ /dev/null
@@ -1,393 +0,0 @@
1//! Entry point for call-hierarchy
2
3use indexmap::IndexMap;
4
5use hir::Semantics;
6use ra_ide_db::RootDatabase;
7use ra_syntax::{ast, match_ast, AstNode, TextRange};
8
9use crate::{
10 call_info::FnCallNode, display::ToNav, goto_definition, references, FilePosition,
11 NavigationTarget, RangeInfo,
12};
13
14#[derive(Debug, Clone)]
15pub struct CallItem {
16 pub target: NavigationTarget,
17 pub ranges: Vec<TextRange>,
18}
19
20impl CallItem {
21 #[cfg(test)]
22 pub(crate) fn assert_match(&self, expected: &str) {
23 let actual = self.debug_render();
24 test_utils::assert_eq_text!(expected.trim(), actual.trim(),);
25 }
26
27 #[cfg(test)]
28 pub(crate) fn debug_render(&self) -> String {
29 format!("{} : {:?}", self.target.debug_render(), self.ranges)
30 }
31}
32
33pub(crate) fn call_hierarchy(
34 db: &RootDatabase,
35 position: FilePosition,
36) -> Option<RangeInfo<Vec<NavigationTarget>>> {
37 goto_definition::goto_definition(db, position)
38}
39
40pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
41 let sema = Semantics::new(db);
42
43 // 1. Find all refs
44 // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply.
45 // 3. Add ranges relative to the start of the fndef.
46 let refs = references::find_all_refs(&sema, position, None)?;
47
48 let mut calls = CallLocations::default();
49
50 for reference in refs.info.references() {
51 let file_id = reference.file_range.file_id;
52 let file = sema.parse(file_id);
53 let file = file.syntax();
54 let token = file.token_at_offset(reference.file_range.range.start()).next()?;
55 let token = sema.descend_into_macros(token);
56 let syntax = token.parent();
57
58 // This target is the containing function
59 if let Some(nav) = syntax.ancestors().find_map(|node| {
60 match_ast! {
61 match node {
62 ast::Fn(it) => {
63 let def = sema.to_def(&it)?;
64 Some(def.to_nav(sema.db))
65 },
66 _ => None,
67 }
68 }
69 }) {
70 let relative_range = reference.file_range.range;
71 calls.add(&nav, relative_range);
72 }
73 }
74
75 Some(calls.into_items())
76}
77
78pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
79 let sema = Semantics::new(db);
80 let file_id = position.file_id;
81 let file = sema.parse(file_id);
82 let file = file.syntax();
83 let token = file.token_at_offset(position.offset).next()?;
84 let token = sema.descend_into_macros(token);
85 let syntax = token.parent();
86
87 let mut calls = CallLocations::default();
88
89 syntax
90 .descendants()
91 .filter_map(|node| FnCallNode::with_node_exact(&node))
92 .filter_map(|call_node| {
93 let name_ref = call_node.name_ref()?;
94
95 if let Some(func_target) = match &call_node {
96 FnCallNode::CallExpr(expr) => {
97 //FIXME: Type::as_callable is broken
98 let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(db)?;
99 match callable.kind() {
100 hir::CallableKind::Function(it) => {
101 let fn_def: hir::Function = it.into();
102 let nav = fn_def.to_nav(db);
103 Some(nav)
104 }
105 _ => None,
106 }
107 }
108 FnCallNode::MethodCallExpr(expr) => {
109 let function = sema.resolve_method_call(&expr)?;
110 Some(function.to_nav(db))
111 }
112 } {
113 Some((func_target, name_ref.syntax().text_range()))
114 } else {
115 None
116 }
117 })
118 .for_each(|(nav, range)| calls.add(&nav, range));
119
120 Some(calls.into_items())
121}
122
123#[derive(Default)]
124struct CallLocations {
125 funcs: IndexMap<NavigationTarget, Vec<TextRange>>,
126}
127
128impl CallLocations {
129 fn add(&mut self, target: &NavigationTarget, range: TextRange) {
130 self.funcs.entry(target.clone()).or_default().push(range);
131 }
132
133 fn into_items(self) -> Vec<CallItem> {
134 self.funcs.into_iter().map(|(target, ranges)| CallItem { target, ranges }).collect()
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use ra_db::FilePosition;
141
142 use crate::mock_analysis::analysis_and_position;
143
144 fn check_hierarchy(
145 ra_fixture: &str,
146 expected: &str,
147 expected_incoming: &[&str],
148 expected_outgoing: &[&str],
149 ) {
150 let (analysis, pos) = analysis_and_position(ra_fixture);
151
152 let mut navs = analysis.call_hierarchy(pos).unwrap().unwrap().info;
153 assert_eq!(navs.len(), 1);
154 let nav = navs.pop().unwrap();
155 nav.assert_match(expected);
156
157 let item_pos =
158 FilePosition { file_id: nav.file_id, offset: nav.focus_or_full_range().start() };
159 let incoming_calls = analysis.incoming_calls(item_pos).unwrap().unwrap();
160 assert_eq!(incoming_calls.len(), expected_incoming.len());
161
162 for call in 0..incoming_calls.len() {
163 incoming_calls[call].assert_match(expected_incoming[call]);
164 }
165
166 let outgoing_calls = analysis.outgoing_calls(item_pos).unwrap().unwrap();
167 assert_eq!(outgoing_calls.len(), expected_outgoing.len());
168
169 for call in 0..outgoing_calls.len() {
170 outgoing_calls[call].assert_match(expected_outgoing[call]);
171 }
172 }
173
174 #[test]
175 fn test_call_hierarchy_on_ref() {
176 check_hierarchy(
177 r#"
178//- /lib.rs
179fn callee() {}
180fn caller() {
181 call<|>ee();
182}
183"#,
184 "callee FN FileId(1) 0..14 3..9",
185 &["caller FN FileId(1) 15..44 18..24 : [33..39]"],
186 &[],
187 );
188 }
189
190 #[test]
191 fn test_call_hierarchy_on_def() {
192 check_hierarchy(
193 r#"
194//- /lib.rs
195fn call<|>ee() {}
196fn caller() {
197 callee();
198}
199"#,
200 "callee FN FileId(1) 0..14 3..9",
201 &["caller FN FileId(1) 15..44 18..24 : [33..39]"],
202 &[],
203 );
204 }
205
206 #[test]
207 fn test_call_hierarchy_in_same_fn() {
208 check_hierarchy(
209 r#"
210//- /lib.rs
211fn callee() {}
212fn caller() {
213 call<|>ee();
214 callee();
215}
216"#,
217 "callee FN FileId(1) 0..14 3..9",
218 &["caller FN FileId(1) 15..58 18..24 : [33..39, 47..53]"],
219 &[],
220 );
221 }
222
223 #[test]
224 fn test_call_hierarchy_in_different_fn() {
225 check_hierarchy(
226 r#"
227//- /lib.rs
228fn callee() {}
229fn caller1() {
230 call<|>ee();
231}
232
233fn caller2() {
234 callee();
235}
236"#,
237 "callee FN FileId(1) 0..14 3..9",
238 &[
239 "caller1 FN FileId(1) 15..45 18..25 : [34..40]",
240 "caller2 FN FileId(1) 47..77 50..57 : [66..72]",
241 ],
242 &[],
243 );
244 }
245
246 #[test]
247 fn test_call_hierarchy_in_tests_mod() {
248 check_hierarchy(
249 r#"
250//- /lib.rs cfg:test
251fn callee() {}
252fn caller1() {
253 call<|>ee();
254}
255
256#[cfg(test)]
257mod tests {
258 use super::*;
259
260 #[test]
261 fn test_caller() {
262 callee();
263 }
264}
265"#,
266 "callee FN FileId(1) 0..14 3..9",
267 &[
268 "caller1 FN FileId(1) 15..45 18..25 : [34..40]",
269 "test_caller FN FileId(1) 95..149 110..121 : [134..140]",
270 ],
271 &[],
272 );
273 }
274
275 #[test]
276 fn test_call_hierarchy_in_different_files() {
277 check_hierarchy(
278 r#"
279//- /lib.rs
280mod foo;
281use foo::callee;
282
283fn caller() {
284 call<|>ee();
285}
286
287//- /foo/mod.rs
288pub fn callee() {}
289"#,
290 "callee FN FileId(2) 0..18 7..13",
291 &["caller FN FileId(1) 27..56 30..36 : [45..51]"],
292 &[],
293 );
294 }
295
296 #[test]
297 fn test_call_hierarchy_outgoing() {
298 check_hierarchy(
299 r#"
300//- /lib.rs
301fn callee() {}
302fn call<|>er() {
303 callee();
304 callee();
305}
306"#,
307 "caller FN FileId(1) 15..58 18..24",
308 &[],
309 &["callee FN FileId(1) 0..14 3..9 : [33..39, 47..53]"],
310 );
311 }
312
313 #[test]
314 fn test_call_hierarchy_outgoing_in_different_files() {
315 check_hierarchy(
316 r#"
317//- /lib.rs
318mod foo;
319use foo::callee;
320
321fn call<|>er() {
322 callee();
323}
324
325//- /foo/mod.rs
326pub fn callee() {}
327"#,
328 "caller FN FileId(1) 27..56 30..36",
329 &[],
330 &["callee FN FileId(2) 0..18 7..13 : [45..51]"],
331 );
332 }
333
334 #[test]
335 fn test_call_hierarchy_incoming_outgoing() {
336 check_hierarchy(
337 r#"
338//- /lib.rs
339fn caller1() {
340 call<|>er2();
341}
342
343fn caller2() {
344 caller3();
345}
346
347fn caller3() {
348
349}
350"#,
351 "caller2 FN FileId(1) 33..64 36..43",
352 &["caller1 FN FileId(1) 0..31 3..10 : [19..26]"],
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 &[],
391 );
392 }
393}
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
deleted file mode 100644
index ff602202f..000000000
--- a/crates/ra_ide/src/call_info.rs
+++ /dev/null
@@ -1,742 +0,0 @@
1//! FIXME: write short doc here
2use either::Either;
3use hir::{Docs, HirDisplay, Semantics, Type};
4use ra_ide_db::RootDatabase;
5use ra_syntax::{
6 ast::{self, ArgListOwner},
7 match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize,
8};
9use stdx::format_to;
10use test_utils::mark;
11
12use crate::FilePosition;
13
14/// Contains information about a call site. Specifically the
15/// `FunctionSignature`and current parameter.
16#[derive(Debug)]
17pub struct CallInfo {
18 pub doc: Option<String>,
19 pub signature: String,
20 pub active_parameter: Option<usize>,
21 parameters: Vec<TextRange>,
22}
23
24impl CallInfo {
25 pub fn parameter_labels(&self) -> impl Iterator<Item = &str> + '_ {
26 self.parameters.iter().map(move |&it| &self.signature[it])
27 }
28 pub fn parameter_ranges(&self) -> &[TextRange] {
29 &self.parameters
30 }
31 fn push_param(&mut self, param: &str) {
32 if !self.signature.ends_with('(') {
33 self.signature.push_str(", ");
34 }
35 let start = TextSize::of(&self.signature);
36 self.signature.push_str(param);
37 let end = TextSize::of(&self.signature);
38 self.parameters.push(TextRange::new(start, end))
39 }
40}
41
42/// Computes parameter information for the given call expression.
43pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
44 let sema = Semantics::new(db);
45 let file = sema.parse(position.file_id);
46 let file = file.syntax();
47 let token = file.token_at_offset(position.offset).next()?;
48 let token = sema.descend_into_macros(token);
49
50 let (callable, active_parameter) = call_info_impl(&sema, token)?;
51
52 let mut res =
53 CallInfo { doc: None, signature: String::new(), parameters: vec![], active_parameter };
54
55 match callable.kind() {
56 hir::CallableKind::Function(func) => {
57 res.doc = func.docs(db).map(|it| it.as_str().to_string());
58 format_to!(res.signature, "fn {}", func.name(db));
59 }
60 hir::CallableKind::TupleStruct(strukt) => {
61 res.doc = strukt.docs(db).map(|it| it.as_str().to_string());
62 format_to!(res.signature, "struct {}", strukt.name(db));
63 }
64 hir::CallableKind::TupleEnumVariant(variant) => {
65 res.doc = variant.docs(db).map(|it| it.as_str().to_string());
66 format_to!(
67 res.signature,
68 "enum {}::{}",
69 variant.parent_enum(db).name(db),
70 variant.name(db)
71 );
72 }
73 hir::CallableKind::Closure => (),
74 }
75
76 res.signature.push('(');
77 {
78 if let Some(self_param) = callable.receiver_param(db) {
79 format_to!(res.signature, "{}", self_param)
80 }
81 let mut buf = String::new();
82 for (pat, ty) in callable.params(db) {
83 buf.clear();
84 if let Some(pat) = pat {
85 match pat {
86 Either::Left(_self) => format_to!(buf, "self: "),
87 Either::Right(pat) => format_to!(buf, "{}: ", pat),
88 }
89 }
90 format_to!(buf, "{}", ty.display(db));
91 res.push_param(&buf);
92 }
93 }
94 res.signature.push(')');
95
96 match callable.kind() {
97 hir::CallableKind::Function(_) | hir::CallableKind::Closure => {
98 let ret_type = callable.return_type();
99 if !ret_type.is_unit() {
100 format_to!(res.signature, " -> {}", ret_type.display(db));
101 }
102 }
103 hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
104 }
105 Some(res)
106}
107
108fn call_info_impl(
109 sema: &Semantics<RootDatabase>,
110 token: SyntaxToken,
111) -> Option<(hir::Callable, Option<usize>)> {
112 // Find the calling expression and it's NameRef
113 let calling_node = FnCallNode::with_node(&token.parent())?;
114
115 let callable = match &calling_node {
116 FnCallNode::CallExpr(call) => sema.type_of_expr(&call.expr()?)?.as_callable(sema.db)?,
117 FnCallNode::MethodCallExpr(call) => sema.resolve_method_call_as_callable(call)?,
118 };
119 let active_param = if let Some(arg_list) = calling_node.arg_list() {
120 // Number of arguments specified at the call site
121 let num_args_at_callsite = arg_list.args().count();
122
123 let arg_list_range = arg_list.syntax().text_range();
124 if !arg_list_range.contains_inclusive(token.text_range().start()) {
125 mark::hit!(call_info_bad_offset);
126 return None;
127 }
128 let param = std::cmp::min(
129 num_args_at_callsite,
130 arg_list
131 .args()
132 .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
133 .count(),
134 );
135
136 Some(param)
137 } else {
138 None
139 };
140 Some((callable, active_param))
141}
142
143#[derive(Debug)]
144pub(crate) struct ActiveParameter {
145 pub(crate) ty: Type,
146 pub(crate) name: String,
147}
148
149impl ActiveParameter {
150 pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option<Self> {
151 let sema = Semantics::new(db);
152 let file = sema.parse(position.file_id);
153 let file = file.syntax();
154 let token = file.token_at_offset(position.offset).next()?;
155 let token = sema.descend_into_macros(token);
156 Self::at_token(&sema, token)
157 }
158
159 pub(crate) fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> {
160 let (signature, active_parameter) = call_info_impl(&sema, token)?;
161
162 let idx = active_parameter?;
163 let mut params = signature.params(sema.db);
164 if !(idx < params.len()) {
165 mark::hit!(too_many_arguments);
166 return None;
167 }
168 let (pat, ty) = params.swap_remove(idx);
169 let name = pat?.to_string();
170 Some(ActiveParameter { ty, name })
171 }
172}
173
174#[derive(Debug)]
175pub(crate) enum FnCallNode {
176 CallExpr(ast::CallExpr),
177 MethodCallExpr(ast::MethodCallExpr),
178}
179
180impl FnCallNode {
181 fn with_node(syntax: &SyntaxNode) -> Option<FnCallNode> {
182 syntax.ancestors().find_map(|node| {
183 match_ast! {
184 match node {
185 ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
186 ast::MethodCallExpr(it) => {
187 let arg_list = it.arg_list()?;
188 if !arg_list.syntax().text_range().contains_range(syntax.text_range()) {
189 return None;
190 }
191 Some(FnCallNode::MethodCallExpr(it))
192 },
193 _ => None,
194 }
195 }
196 })
197 }
198
199 pub(crate) fn with_node_exact(node: &SyntaxNode) -> Option<FnCallNode> {
200 match_ast! {
201 match node {
202 ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
203 ast::MethodCallExpr(it) => Some(FnCallNode::MethodCallExpr(it)),
204 _ => None,
205 }
206 }
207 }
208
209 pub(crate) fn name_ref(&self) -> Option<ast::NameRef> {
210 match self {
211 FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()? {
212 ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
213 _ => return None,
214 }),
215
216 FnCallNode::MethodCallExpr(call_expr) => {
217 call_expr.syntax().children().filter_map(ast::NameRef::cast).next()
218 }
219 }
220 }
221
222 fn arg_list(&self) -> Option<ast::ArgList> {
223 match self {
224 FnCallNode::CallExpr(expr) => expr.arg_list(),
225 FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
226 }
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use expect::{expect, Expect};
233 use test_utils::mark;
234
235 use crate::mock_analysis::analysis_and_position;
236
237 fn check(ra_fixture: &str, expect: Expect) {
238 let (analysis, position) = analysis_and_position(ra_fixture);
239 let call_info = analysis.call_info(position).unwrap();
240 let actual = match call_info {
241 Some(call_info) => {
242 let docs = match &call_info.doc {
243 None => "".to_string(),
244 Some(docs) => format!("{}\n------\n", docs.as_str()),
245 };
246 let params = call_info
247 .parameter_labels()
248 .enumerate()
249 .map(|(i, param)| {
250 if Some(i) == call_info.active_parameter {
251 format!("<{}>", param)
252 } else {
253 param.to_string()
254 }
255 })
256 .collect::<Vec<_>>()
257 .join(", ");
258 format!("{}{}\n({})\n", docs, call_info.signature, params)
259 }
260 None => String::new(),
261 };
262 expect.assert_eq(&actual);
263 }
264
265 #[test]
266 fn test_fn_signature_two_args() {
267 check(
268 r#"
269fn foo(x: u32, y: u32) -> u32 {x + y}
270fn bar() { foo(<|>3, ); }
271"#,
272 expect![[r#"
273 fn foo(x: u32, y: u32) -> u32
274 (<x: u32>, y: u32)
275 "#]],
276 );
277 check(
278 r#"
279fn foo(x: u32, y: u32) -> u32 {x + y}
280fn bar() { foo(3<|>, ); }
281"#,
282 expect![[r#"
283 fn foo(x: u32, y: u32) -> u32
284 (<x: u32>, y: u32)
285 "#]],
286 );
287 check(
288 r#"
289fn foo(x: u32, y: u32) -> u32 {x + y}
290fn bar() { foo(3,<|> ); }
291"#,
292 expect![[r#"
293 fn foo(x: u32, y: u32) -> u32
294 (x: u32, <y: u32>)
295 "#]],
296 );
297 check(
298 r#"
299fn foo(x: u32, y: u32) -> u32 {x + y}
300fn bar() { foo(3, <|>); }
301"#,
302 expect![[r#"
303 fn foo(x: u32, y: u32) -> u32
304 (x: u32, <y: u32>)
305 "#]],
306 );
307 }
308
309 #[test]
310 fn test_fn_signature_two_args_empty() {
311 check(
312 r#"
313fn foo(x: u32, y: u32) -> u32 {x + y}
314fn bar() { foo(<|>); }
315"#,
316 expect![[r#"
317 fn foo(x: u32, y: u32) -> u32
318 (<x: u32>, y: u32)
319 "#]],
320 );
321 }
322
323 #[test]
324 fn test_fn_signature_two_args_first_generics() {
325 check(
326 r#"
327fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
328 where T: Copy + Display, U: Debug
329{ x + y }
330
331fn bar() { foo(<|>3, ); }
332"#,
333 expect![[r#"
334 fn foo(x: i32, y: {unknown}) -> u32
335 (<x: i32>, y: {unknown})
336 "#]],
337 );
338 }
339
340 #[test]
341 fn test_fn_signature_no_params() {
342 check(
343 r#"
344fn foo<T>() -> T where T: Copy + Display {}
345fn bar() { foo(<|>); }
346"#,
347 expect![[r#"
348 fn foo() -> {unknown}
349 ()
350 "#]],
351 );
352 }
353
354 #[test]
355 fn test_fn_signature_for_impl() {
356 check(
357 r#"
358struct F;
359impl F { pub fn new() { } }
360fn bar() {
361 let _ : F = F::new(<|>);
362}
363"#,
364 expect![[r#"
365 fn new()
366 ()
367 "#]],
368 );
369 }
370
371 #[test]
372 fn test_fn_signature_for_method_self() {
373 check(
374 r#"
375struct S;
376impl S { pub fn do_it(&self) {} }
377
378fn bar() {
379 let s: S = S;
380 s.do_it(<|>);
381}
382"#,
383 expect![[r#"
384 fn do_it(&self)
385 ()
386 "#]],
387 );
388 }
389
390 #[test]
391 fn test_fn_signature_for_method_with_arg() {
392 check(
393 r#"
394struct S;
395impl S {
396 fn foo(&self, x: i32) {}
397}
398
399fn main() { S.foo(<|>); }
400"#,
401 expect![[r#"
402 fn foo(&self, x: i32)
403 (<x: i32>)
404 "#]],
405 );
406 }
407
408 #[test]
409 fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
410 check(
411 r#"
412struct S;
413impl S {
414 fn foo(&self, x: i32) {}
415}
416
417fn main() { S::foo(<|>); }
418"#,
419 expect![[r#"
420 fn foo(self: &S, x: i32)
421 (<self: &S>, x: i32)
422 "#]],
423 );
424 }
425
426 #[test]
427 fn test_fn_signature_with_docs_simple() {
428 check(
429 r#"
430/// test
431// non-doc-comment
432fn foo(j: u32) -> u32 {
433 j
434}
435
436fn bar() {
437 let _ = foo(<|>);
438}
439"#,
440 expect![[r#"
441 test
442 ------
443 fn foo(j: u32) -> u32
444 (<j: u32>)
445 "#]],
446 );
447 }
448
449 #[test]
450 fn test_fn_signature_with_docs() {
451 check(
452 r#"
453/// Adds one to the number given.
454///
455/// # Examples
456///
457/// ```
458/// let five = 5;
459///
460/// assert_eq!(6, my_crate::add_one(5));
461/// ```
462pub fn add_one(x: i32) -> i32 {
463 x + 1
464}
465
466pub fn do() {
467 add_one(<|>
468}"#,
469 expect![[r##"
470 Adds one to the number given.
471
472 # Examples
473
474 ```
475 let five = 5;
476
477 assert_eq!(6, my_crate::add_one(5));
478 ```
479 ------
480 fn add_one(x: i32) -> i32
481 (<x: i32>)
482 "##]],
483 );
484 }
485
486 #[test]
487 fn test_fn_signature_with_docs_impl() {
488 check(
489 r#"
490struct addr;
491impl addr {
492 /// Adds one to the number given.
493 ///
494 /// # Examples
495 ///
496 /// ```
497 /// let five = 5;
498 ///
499 /// assert_eq!(6, my_crate::add_one(5));
500 /// ```
501 pub fn add_one(x: i32) -> i32 {
502 x + 1
503 }
504}
505
506pub fn do_it() {
507 addr {};
508 addr::add_one(<|>);
509}
510"#,
511 expect![[r##"
512 Adds one to the number given.
513
514 # Examples
515
516 ```
517 let five = 5;
518
519 assert_eq!(6, my_crate::add_one(5));
520 ```
521 ------
522 fn add_one(x: i32) -> i32
523 (<x: i32>)
524 "##]],
525 );
526 }
527
528 #[test]
529 fn test_fn_signature_with_docs_from_actix() {
530 check(
531 r#"
532struct WriteHandler<E>;
533
534impl<E> WriteHandler<E> {
535 /// Method is called when writer emits error.
536 ///
537 /// If this method returns `ErrorAction::Continue` writer processing
538 /// continues otherwise stream processing stops.
539 fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
540 Running::Stop
541 }
542
543 /// Method is called when writer finishes.
544 ///
545 /// By default this method stops actor's `Context`.
546 fn finished(&mut self, ctx: &mut Self::Context) {
547 ctx.stop()
548 }
549}
550
551pub fn foo(mut r: WriteHandler<()>) {
552 r.finished(<|>);
553}
554"#,
555 expect![[r#"
556 Method is called when writer finishes.
557
558 By default this method stops actor's `Context`.
559 ------
560 fn finished(&mut self, ctx: &mut {unknown})
561 (<ctx: &mut {unknown}>)
562 "#]],
563 );
564 }
565
566 #[test]
567 fn call_info_bad_offset() {
568 mark::check!(call_info_bad_offset);
569 check(
570 r#"
571fn foo(x: u32, y: u32) -> u32 {x + y}
572fn bar() { foo <|> (3, ); }
573"#,
574 expect![[""]],
575 );
576 }
577
578 #[test]
579 fn test_nested_method_in_lambda() {
580 check(
581 r#"
582struct Foo;
583impl Foo { fn bar(&self, _: u32) { } }
584
585fn bar(_: u32) { }
586
587fn main() {
588 let foo = Foo;
589 std::thread::spawn(move || foo.bar(<|>));
590}
591"#,
592 expect![[r#"
593 fn bar(&self, _: u32)
594 (<_: u32>)
595 "#]],
596 );
597 }
598
599 #[test]
600 fn works_for_tuple_structs() {
601 check(
602 r#"
603/// A cool tuple struct
604struct S(u32, i32);
605fn main() {
606 let s = S(0, <|>);
607}
608"#,
609 expect![[r#"
610 A cool tuple struct
611 ------
612 struct S(u32, i32)
613 (u32, <i32>)
614 "#]],
615 );
616 }
617
618 #[test]
619 fn generic_struct() {
620 check(
621 r#"
622struct S<T>(T);
623fn main() {
624 let s = S(<|>);
625}
626"#,
627 expect![[r#"
628 struct S({unknown})
629 (<{unknown}>)
630 "#]],
631 );
632 }
633
634 #[test]
635 fn works_for_enum_variants() {
636 check(
637 r#"
638enum E {
639 /// A Variant
640 A(i32),
641 /// Another
642 B,
643 /// And C
644 C { a: i32, b: i32 }
645}
646
647fn main() {
648 let a = E::A(<|>);
649}
650"#,
651 expect![[r#"
652 A Variant
653 ------
654 enum E::A(i32)
655 (<i32>)
656 "#]],
657 );
658 }
659
660 #[test]
661 fn cant_call_struct_record() {
662 check(
663 r#"
664struct S { x: u32, y: i32 }
665fn main() {
666 let s = S(<|>);
667}
668"#,
669 expect![[""]],
670 );
671 }
672
673 #[test]
674 fn cant_call_enum_record() {
675 check(
676 r#"
677enum E {
678 /// A Variant
679 A(i32),
680 /// Another
681 B,
682 /// And C
683 C { a: i32, b: i32 }
684}
685
686fn main() {
687 let a = E::C(<|>);
688}
689"#,
690 expect![[""]],
691 );
692 }
693
694 #[test]
695 fn fn_signature_for_call_in_macro() {
696 check(
697 r#"
698macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
699fn foo() { }
700id! {
701 fn bar() { foo(<|>); }
702}
703"#,
704 expect![[r#"
705 fn foo()
706 ()
707 "#]],
708 );
709 }
710
711 #[test]
712 fn call_info_for_lambdas() {
713 check(
714 r#"
715struct S;
716fn foo(s: S) -> i32 { 92 }
717fn main() {
718 (|s| foo(s))(<|>)
719}
720 "#,
721 expect![[r#"
722 (S) -> i32
723 (<S>)
724 "#]],
725 )
726 }
727
728 #[test]
729 fn call_info_for_fn_ptr() {
730 check(
731 r#"
732fn main(f: fn(i32, f64) -> char) {
733 f(0, <|>)
734}
735 "#,
736 expect![[r#"
737 (i32, f64) -> char
738 (i32, <f64>)
739 "#]],
740 )
741 }
742}
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
deleted file mode 100644
index 68ac05e4c..000000000
--- a/crates/ra_ide/src/completion.rs
+++ /dev/null
@@ -1,206 +0,0 @@
1mod completion_config;
2mod completion_item;
3mod completion_context;
4mod presentation;
5mod patterns;
6#[cfg(test)]
7mod test_utils;
8
9mod complete_attribute;
10mod complete_dot;
11mod complete_record;
12mod complete_pattern;
13mod complete_fn_param;
14mod complete_keyword;
15mod complete_snippet;
16mod complete_qualified_path;
17mod complete_unqualified_path;
18mod complete_postfix;
19mod complete_macro_in_item_position;
20mod complete_trait_impl;
21
22use ra_ide_db::RootDatabase;
23
24use crate::{
25 completion::{
26 completion_context::CompletionContext,
27 completion_item::{CompletionKind, Completions},
28 },
29 FilePosition,
30};
31
32pub use crate::completion::{
33 completion_config::CompletionConfig,
34 completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
35};
36
37//FIXME: split the following feature into fine-grained features.
38
39// Feature: Magic Completions
40//
41// In addition to usual reference completion, rust-analyzer provides some ✨magic✨
42// completions as well:
43//
44// Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
45// is placed at the appropriate position. Even though `if` is easy to type, you
46// still want to complete it, to get ` { }` for free! `return` is inserted with a
47// space or `;` depending on the return type of the function.
48//
49// When completing a function call, `()` are automatically inserted. If a function
50// takes arguments, the cursor is positioned inside the parenthesis.
51//
52// There are postfix completions, which can be triggered by typing something like
53// `foo().if`. The word after `.` determines postfix completion. Possible variants are:
54//
55// - `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
56// - `expr.match` -> `match expr {}`
57// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
58// - `expr.ref` -> `&expr`
59// - `expr.refm` -> `&mut expr`
60// - `expr.not` -> `!expr`
61// - `expr.dbg` -> `dbg!(expr)`
62//
63// There also snippet completions:
64//
65// .Expressions
66// - `pd` -> `eprintln!(" = {:?}", );`
67// - `ppd` -> `eprintln!(" = {:#?}", );`
68//
69// .Items
70// - `tfn` -> `#[test] fn feature(){}`
71// - `tmod` ->
72// ```rust
73// #[cfg(test)]
74// mod tests {
75// use super::*;
76//
77// #[test]
78// fn test_name() {}
79// }
80// ```
81
82/// Main entry point for completion. We run completion as a two-phase process.
83///
84/// First, we look at the position and collect a so-called `CompletionContext.
85/// This is a somewhat messy process, because, during completion, syntax tree is
86/// incomplete and can look really weird.
87///
88/// Once the context is collected, we run a series of completion routines which
89/// look at the context and produce completion items. One subtlety about this
90/// phase is that completion engine should not filter by the substring which is
91/// already present, it should give all possible variants for the identifier at
92/// the caret. In other words, for
93///
94/// ```no-run
95/// fn f() {
96/// let foo = 92;
97/// let _ = bar<|>
98/// }
99/// ```
100///
101/// `foo` *should* be present among the completion variants. Filtering by
102/// identifier prefix/fuzzy match should be done higher in the stack, together
103/// with ordering of completions (currently this is done by the client).
104pub(crate) fn completions(
105 db: &RootDatabase,
106 config: &CompletionConfig,
107 position: FilePosition,
108) -> Option<Completions> {
109 let ctx = CompletionContext::new(db, position, config)?;
110
111 let mut acc = Completions::default();
112 complete_attribute::complete_attribute(&mut acc, &ctx);
113 complete_fn_param::complete_fn_param(&mut acc, &ctx);
114 complete_keyword::complete_expr_keyword(&mut acc, &ctx);
115 complete_keyword::complete_use_tree_keyword(&mut acc, &ctx);
116 complete_snippet::complete_expr_snippet(&mut acc, &ctx);
117 complete_snippet::complete_item_snippet(&mut acc, &ctx);
118 complete_qualified_path::complete_qualified_path(&mut acc, &ctx);
119 complete_unqualified_path::complete_unqualified_path(&mut acc, &ctx);
120 complete_dot::complete_dot(&mut acc, &ctx);
121 complete_record::complete_record(&mut acc, &ctx);
122 complete_pattern::complete_pattern(&mut acc, &ctx);
123 complete_postfix::complete_postfix(&mut acc, &ctx);
124 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
125 complete_trait_impl::complete_trait_impl(&mut acc, &ctx);
126
127 Some(acc)
128}
129
130#[cfg(test)]
131mod tests {
132 use crate::completion::completion_config::CompletionConfig;
133 use crate::mock_analysis::analysis_and_position;
134
135 struct DetailAndDocumentation<'a> {
136 detail: &'a str,
137 documentation: &'a str,
138 }
139
140 fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
141 let (analysis, position) = analysis_and_position(ra_fixture);
142 let config = CompletionConfig::default();
143 let completions = analysis.completions(&config, position).unwrap().unwrap();
144 for item in completions {
145 if item.detail() == Some(expected.detail) {
146 let opt = item.documentation();
147 let doc = opt.as_ref().map(|it| it.as_str());
148 assert_eq!(doc, Some(expected.documentation));
149 return;
150 }
151 }
152 panic!("completion detail not found: {}", expected.detail)
153 }
154
155 #[test]
156 fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
157 check_detail_and_documentation(
158 r#"
159 //- /lib.rs
160 macro_rules! bar {
161 () => {
162 struct Bar;
163 impl Bar {
164 #[doc = "Do the foo"]
165 fn foo(&self) {}
166 }
167 }
168 }
169
170 bar!();
171
172 fn foo() {
173 let bar = Bar;
174 bar.fo<|>;
175 }
176 "#,
177 DetailAndDocumentation { detail: "fn foo(&self)", documentation: "Do the foo" },
178 );
179 }
180
181 #[test]
182 fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
183 check_detail_and_documentation(
184 r#"
185 //- /lib.rs
186 macro_rules! bar {
187 () => {
188 struct Bar;
189 impl Bar {
190 /// Do the foo
191 fn foo(&self) {}
192 }
193 }
194 }
195
196 bar!();
197
198 fn foo() {
199 let bar = Bar;
200 bar.fo<|>;
201 }
202 "#,
203 DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" },
204 );
205 }
206}
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs
deleted file mode 100644
index 2faaae974..000000000
--- a/crates/ra_ide/src/completion/complete_attribute.rs
+++ /dev/null
@@ -1,644 +0,0 @@
1//! Completion for attributes
2//!
3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes.
5
6use ra_syntax::{ast, AstNode, SyntaxKind};
7use rustc_hash::FxHashSet;
8
9use crate::completion::{
10 completion_context::CompletionContext,
11 completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions},
12};
13
14pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
15 let attribute = ctx.attribute_under_caret.as_ref()?;
16 match (attribute.path(), attribute.token_tree()) {
17 (Some(path), Some(token_tree)) if path.to_string() == "derive" => {
18 complete_derive(acc, ctx, token_tree)
19 }
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)) => {}
28 _ => complete_attribute_start(acc, ctx, attribute),
29 }
30 Some(())
31}
32
33fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
34 for attr_completion in ATTRIBUTES {
35 let mut item = CompletionItem::new(
36 CompletionKind::Attribute,
37 ctx.source_range(),
38 attr_completion.label,
39 )
40 .kind(CompletionItemKind::Attribute);
41
42 if let Some(lookup) = attr_completion.lookup {
43 item = item.lookup_by(lookup);
44 }
45
46 match (attr_completion.snippet, ctx.config.snippet_cap) {
47 (Some(snippet), Some(cap)) => {
48 item = item.insert_snippet(cap, snippet);
49 }
50 _ => {}
51 }
52
53 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner {
54 acc.add(item);
55 }
56 }
57}
58
59struct AttrCompletion {
60 label: &'static str,
61 lookup: Option<&'static str>,
62 snippet: Option<&'static str>,
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 }
78}
79
80const ATTRIBUTES: &[AttrCompletion] = &[
81 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
82 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
83 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
84 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
85 attr(r#"deprecated = "…""#, Some("deprecated"), Some(r#"deprecated = "${0:reason}""#)),
86 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
87 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
88 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
89 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
90 // FIXME: resolve through macro resolution?
91 attr("global_allocator", None, None).prefer_inner(),
92 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
93 attr("inline(…)", Some("inline"), Some("inline(${0:lint})")),
94 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
95 attr("link", None, None),
96 attr("macro_export", None, None),
97 attr("macro_use", None, None),
98 attr(r#"must_use = "…""#, Some("must_use"), Some(r#"must_use = "${0:reason}""#)),
99 attr("no_mangle", None, None),
100 attr("no_std", None, None).prefer_inner(),
101 attr("non_exhaustive", None, None),
102 attr("panic_handler", None, None).prefer_inner(),
103 attr("path = \"…\"", Some("path"), Some("path =\"${0:path}\"")),
104 attr("proc_macro", None, None),
105 attr("proc_macro_attribute", None, None),
106 attr("proc_macro_derive(…)", Some("proc_macro_derive"), Some("proc_macro_derive(${0:Trait})")),
107 attr("recursion_limit = …", Some("recursion_limit"), Some("recursion_limit = ${0:128}"))
108 .prefer_inner(),
109 attr("repr(…)", Some("repr"), Some("repr(${0:C})")),
110 attr(
111 "should_panic(…)",
112 Some("should_panic"),
113 Some(r#"should_panic(expected = "${0:reason}")"#),
114 ),
115 attr(
116 r#"target_feature = "…""#,
117 Some("target_feature"),
118 Some("target_feature = \"${0:feature}\""),
119 ),
120 attr("test", None, None),
121 attr("used", None, None),
122 attr("warn(…)", Some("warn"), Some("warn(${0:lint})")),
123 attr(
124 r#"windows_subsystem = "…""#,
125 Some("windows_subsystem"),
126 Some(r#"windows_subsystem = "${0:subsystem}""#),
127 )
128 .prefer_inner(),
129];
130
131fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
132 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) {
133 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
134 .into_iter()
135 .filter(|completion| !existing_derives.contains(completion.label))
136 {
137 let mut label = derive_completion.label.to_owned();
138 for dependency in derive_completion
139 .dependencies
140 .into_iter()
141 .filter(|&&dependency| !existing_derives.contains(dependency))
142 {
143 label.push_str(", ");
144 label.push_str(dependency);
145 }
146 acc.add(
147 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label)
148 .kind(CompletionItemKind::Attribute),
149 );
150 }
151
152 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
153 acc.add(
154 CompletionItem::new(
155 CompletionKind::Attribute,
156 ctx.source_range(),
157 custom_derive_name,
158 )
159 .kind(CompletionItemKind::Attribute),
160 );
161 }
162 }
163}
164
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>, ()> {
185 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
186 (Some(left_paren), Some(right_paren))
187 if left_paren.kind() == SyntaxKind::L_PAREN
188 && right_paren.kind() == SyntaxKind::R_PAREN =>
189 {
190 let mut input_derives = FxHashSet::default();
191 let mut current_derive = String::new();
192 for token in derive_input
193 .syntax()
194 .children_with_tokens()
195 .filter_map(|token| token.into_token())
196 .skip_while(|token| token != &left_paren)
197 .skip(1)
198 .take_while(|token| token != &right_paren)
199 {
200 if SyntaxKind::COMMA == token.kind() {
201 if !current_derive.is_empty() {
202 input_derives.insert(current_derive);
203 current_derive = String::new();
204 }
205 } else {
206 current_derive.push_str(token.to_string().trim());
207 }
208 }
209
210 if !current_derive.is_empty() {
211 input_derives.insert(current_derive);
212 }
213 Ok(input_derives)
214 }
215 _ => Err(()),
216 }
217}
218
219fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
220 let mut result = FxHashSet::default();
221 ctx.scope.process_all_names(&mut |name, scope_def| {
222 if let hir::ScopeDef::MacroDef(mac) = scope_def {
223 if mac.is_derive_macro() {
224 result.insert(name.to_string());
225 }
226 }
227 });
228 result
229}
230
231struct DeriveCompletion {
232 label: &'static str,
233 dependencies: &'static [&'static str],
234}
235
236/// Standard Rust derives and the information about their dependencies
237/// (the dependencies are needed so that the main derive don't break the compilation when added)
238#[rustfmt::skip]
239const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
240 DeriveCompletion { label: "Clone", dependencies: &[] },
241 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
242 DeriveCompletion { label: "Debug", dependencies: &[] },
243 DeriveCompletion { label: "Default", dependencies: &[] },
244 DeriveCompletion { label: "Hash", dependencies: &[] },
245 DeriveCompletion { label: "PartialEq", dependencies: &[] },
246 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
247 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
248 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
249];
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
375#[cfg(test)]
376mod tests {
377 use expect::{expect, Expect};
378
379 use crate::completion::{test_utils::completion_list, CompletionKind};
380
381 fn check(ra_fixture: &str, expect: Expect) {
382 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
383 expect.assert_eq(&actual);
384 }
385
386 #[test]
387 fn empty_derive_completion() {
388 check(
389 r#"
390#[derive(<|>)]
391struct Test {}
392 "#,
393 expect![[r#"
394 at Clone
395 at Copy, Clone
396 at Debug
397 at Default
398 at Eq, PartialEq
399 at Hash
400 at Ord, PartialOrd, Eq, PartialEq
401 at PartialEq
402 at PartialOrd, PartialEq
403 "#]],
404 );
405 }
406
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]
532 fn no_completion_for_incorrect_derive() {
533 check(
534 r#"
535#[derive{<|>)]
536struct Test {}
537"#,
538 expect![[r#""#]],
539 )
540 }
541
542 #[test]
543 fn derive_with_input_completion() {
544 check(
545 r#"
546#[derive(serde::Serialize, PartialEq, <|>)]
547struct Test {}
548"#,
549 expect![[r#"
550 at Clone
551 at Copy, Clone
552 at Debug
553 at Default
554 at Eq
555 at Hash
556 at Ord, PartialOrd, Eq
557 at PartialOrd
558 "#]],
559 )
560 }
561
562 #[test]
563 fn test_attribute_completion() {
564 check(
565 r#"#[<|>]"#,
566 expect![[r#"
567 at allow(…)
568 at cfg(…)
569 at cfg_attr(…)
570 at deny(…)
571 at deprecated = "…"
572 at derive(…)
573 at doc = "…"
574 at forbid(…)
575 at ignore = "…"
576 at inline(…)
577 at link
578 at link_name = "…"
579 at macro_export
580 at macro_use
581 at must_use = "…"
582 at no_mangle
583 at non_exhaustive
584 at path = "…"
585 at proc_macro
586 at proc_macro_attribute
587 at proc_macro_derive(…)
588 at repr(…)
589 at should_panic(…)
590 at target_feature = "…"
591 at test
592 at used
593 at warn(…)
594 "#]],
595 )
596 }
597
598 #[test]
599 fn test_attribute_completion_inside_nested_attr() {
600 check(r#"#[cfg(<|>)]"#, expect![[]])
601 }
602
603 #[test]
604 fn test_inner_attribute_completion() {
605 check(
606 r"#![<|>]",
607 expect![[r#"
608 at allow(…)
609 at cfg(…)
610 at cfg_attr(…)
611 at deny(…)
612 at deprecated = "…"
613 at derive(…)
614 at doc = "…"
615 at feature(…)
616 at forbid(…)
617 at global_allocator
618 at ignore = "…"
619 at inline(…)
620 at link
621 at link_name = "…"
622 at macro_export
623 at macro_use
624 at must_use = "…"
625 at no_mangle
626 at no_std
627 at non_exhaustive
628 at panic_handler
629 at path = "…"
630 at proc_macro
631 at proc_macro_attribute
632 at proc_macro_derive(…)
633 at recursion_limit = …
634 at repr(…)
635 at should_panic(…)
636 at target_feature = "…"
637 at test
638 at used
639 at warn(…)
640 at windows_subsystem = "…"
641 "#]],
642 );
643 }
644}
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
deleted file mode 100644
index 532665285..000000000
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ /dev/null
@@ -1,416 +0,0 @@
1//! Completes references after dot (fields and method calls).
2
3use hir::{HasVisibility, Type};
4use rustc_hash::FxHashSet;
5use test_utils::mark;
6
7use crate::completion::{completion_context::CompletionContext, completion_item::Completions};
8
9/// Complete dot accesses, i.e. fields or methods.
10pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
11 let dot_receiver = match &ctx.dot_receiver {
12 Some(expr) => expr,
13 _ => return,
14 };
15
16 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
17 Some(ty) => ty,
18 _ => return,
19 };
20
21 if ctx.is_call {
22 mark::hit!(test_no_struct_field_completion_for_method_call);
23 } else {
24 complete_fields(acc, ctx, &receiver_ty);
25 }
26 complete_methods(acc, ctx, &receiver_ty);
27}
28
29fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
30 for receiver in receiver.autoderef(ctx.db) {
31 for (field, ty) in receiver.fields(ctx.db) {
32 if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
33 // Skip private field. FIXME: If the definition location of the
34 // field is editable, we should show the completion
35 continue;
36 }
37 acc.add_field(ctx, field, &ty);
38 }
39 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
40 // FIXME: Handle visibility
41 acc.add_tuple_field(ctx, i, &ty);
42 }
43 }
44}
45
46fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
47 if let Some(krate) = ctx.krate {
48 let mut seen_methods = FxHashSet::default();
49 let traits_in_scope = ctx.scope.traits_in_scope();
50 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
51 if func.has_self_param(ctx.db)
52 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
53 && seen_methods.insert(func.name(ctx.db))
54 {
55 acc.add_function(ctx, func, None);
56 }
57 None::<()>
58 });
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use expect::{expect, Expect};
65 use test_utils::mark;
66
67 use crate::completion::{test_utils::completion_list, CompletionKind};
68
69 fn check(ra_fixture: &str, expect: Expect) {
70 let actual = completion_list(ra_fixture, CompletionKind::Reference);
71 expect.assert_eq(&actual);
72 }
73
74 #[test]
75 fn test_struct_field_and_method_completion() {
76 check(
77 r#"
78struct S { foo: u32 }
79impl S {
80 fn bar(&self) {}
81}
82fn foo(s: S) { s.<|> }
83"#,
84 expect![[r#"
85 me bar() fn bar(&self)
86 fd foo u32
87 "#]],
88 );
89 }
90
91 #[test]
92 fn test_struct_field_completion_self() {
93 check(
94 r#"
95struct S { the_field: (u32,) }
96impl S {
97 fn foo(self) { self.<|> }
98}
99"#,
100 expect![[r#"
101 me foo() fn foo(self)
102 fd the_field (u32,)
103 "#]],
104 )
105 }
106
107 #[test]
108 fn test_struct_field_completion_autoderef() {
109 check(
110 r#"
111struct A { the_field: (u32, i32) }
112impl A {
113 fn foo(&self) { self.<|> }
114}
115"#,
116 expect![[r#"
117 me foo() fn foo(&self)
118 fd the_field (u32, i32)
119 "#]],
120 )
121 }
122
123 #[test]
124 fn test_no_struct_field_completion_for_method_call() {
125 mark::check!(test_no_struct_field_completion_for_method_call);
126 check(
127 r#"
128struct A { the_field: u32 }
129fn foo(a: A) { a.<|>() }
130"#,
131 expect![[""]],
132 );
133 }
134
135 #[test]
136 fn test_visibility_filtering() {
137 check(
138 r#"
139mod inner {
140 pub struct A {
141 private_field: u32,
142 pub pub_field: u32,
143 pub(crate) crate_field: u32,
144 pub(super) super_field: u32,
145 }
146}
147fn foo(a: inner::A) { a.<|> }
148"#,
149 expect![[r#"
150 fd crate_field u32
151 fd pub_field u32
152 fd super_field u32
153 "#]],
154 );
155
156 check(
157 r#"
158struct A {}
159mod m {
160 impl super::A {
161 fn private_method(&self) {}
162 pub(super) fn the_method(&self) {}
163 }
164}
165fn foo(a: A) { a.<|> }
166"#,
167 expect![[r#"
168 me the_method() pub(super) fn the_method(&self)
169 "#]],
170 );
171 }
172
173 #[test]
174 fn test_union_field_completion() {
175 check(
176 r#"
177union U { field: u8, other: u16 }
178fn foo(u: U) { u.<|> }
179"#,
180 expect![[r#"
181 fd field u8
182 fd other u16
183 "#]],
184 );
185 }
186
187 #[test]
188 fn test_method_completion_only_fitting_impls() {
189 check(
190 r#"
191struct A<T> {}
192impl A<u32> {
193 fn the_method(&self) {}
194}
195impl A<i32> {
196 fn the_other_method(&self) {}
197}
198fn foo(a: A<u32>) { a.<|> }
199"#,
200 expect![[r#"
201 me the_method() fn the_method(&self)
202 "#]],
203 )
204 }
205
206 #[test]
207 fn test_trait_method_completion() {
208 check(
209 r#"
210struct A {}
211trait Trait { fn the_method(&self); }
212impl Trait for A {}
213fn foo(a: A) { a.<|> }
214"#,
215 expect![[r#"
216 me the_method() fn the_method(&self)
217 "#]],
218 );
219 }
220
221 #[test]
222 fn test_trait_method_completion_deduplicated() {
223 check(
224 r"
225struct A {}
226trait Trait { fn the_method(&self); }
227impl<T> Trait for T {}
228fn foo(a: &A) { a.<|> }
229",
230 expect![[r#"
231 me the_method() fn the_method(&self)
232 "#]],
233 );
234 }
235
236 #[test]
237 fn completes_trait_method_from_other_module() {
238 check(
239 r"
240struct A {}
241mod m {
242 pub trait Trait { fn the_method(&self); }
243}
244use m::Trait;
245impl Trait for A {}
246fn foo(a: A) { a.<|> }
247",
248 expect![[r#"
249 me the_method() fn the_method(&self)
250 "#]],
251 );
252 }
253
254 #[test]
255 fn test_no_non_self_method() {
256 check(
257 r#"
258struct A {}
259impl A {
260 fn the_method() {}
261}
262fn foo(a: A) {
263 a.<|>
264}
265"#,
266 expect![[""]],
267 );
268 }
269
270 #[test]
271 fn test_tuple_field_completion() {
272 check(
273 r#"
274fn foo() {
275 let b = (0, 3.14);
276 b.<|>
277}
278"#,
279 expect![[r#"
280 fd 0 i32
281 fd 1 f64
282 "#]],
283 )
284 }
285
286 #[test]
287 fn test_tuple_field_inference() {
288 check(
289 r#"
290pub struct S;
291impl S { pub fn blah(&self) {} }
292
293struct T(S);
294
295impl T {
296 fn foo(&self) {
297 // FIXME: This doesn't work without the trailing `a` as `0.` is a float
298 self.0.a<|>
299 }
300}
301"#,
302 expect![[r#"
303 me blah() pub fn blah(&self)
304 "#]],
305 );
306 }
307
308 #[test]
309 fn test_completion_works_in_consts() {
310 check(
311 r#"
312struct A { the_field: u32 }
313const X: u32 = {
314 A { the_field: 92 }.<|>
315};
316"#,
317 expect![[r#"
318 fd the_field u32
319 "#]],
320 );
321 }
322
323 #[test]
324 fn works_in_simple_macro_1() {
325 check(
326 r#"
327macro_rules! m { ($e:expr) => { $e } }
328struct A { the_field: u32 }
329fn foo(a: A) {
330 m!(a.x<|>)
331}
332"#,
333 expect![[r#"
334 fd the_field u32
335 "#]],
336 );
337 }
338
339 #[test]
340 fn works_in_simple_macro_2() {
341 // this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery
342 check(
343 r#"
344macro_rules! m { ($e:expr) => { $e } }
345struct A { the_field: u32 }
346fn foo(a: A) {
347 m!(a.<|>)
348}
349"#,
350 expect![[r#"
351 fd the_field u32
352 "#]],
353 );
354 }
355
356 #[test]
357 fn works_in_simple_macro_recursive_1() {
358 check(
359 r#"
360macro_rules! m { ($e:expr) => { $e } }
361struct A { the_field: u32 }
362fn foo(a: A) {
363 m!(m!(m!(a.x<|>)))
364}
365"#,
366 expect![[r#"
367 fd the_field u32
368 "#]],
369 );
370 }
371
372 #[test]
373 fn macro_expansion_resilient() {
374 check(
375 r#"
376macro_rules! dbg {
377 () => {};
378 ($val:expr) => {
379 match $val { tmp => { tmp } }
380 };
381 // Trailing comma with single argument is ignored
382 ($val:expr,) => { $crate::dbg!($val) };
383 ($($val:expr),+ $(,)?) => {
384 ($($crate::dbg!($val)),+,)
385 };
386}
387struct A { the_field: u32 }
388fn foo(a: A) {
389 dbg!(a.<|>)
390}
391"#,
392 expect![[r#"
393 fd the_field u32
394 "#]],
395 );
396 }
397
398 #[test]
399 fn test_method_completion_issue_3547() {
400 check(
401 r#"
402struct HashSet<T> {}
403impl<T> HashSet<T> {
404 pub fn the_method(&self) {}
405}
406fn foo() {
407 let s: HashSet<_>;
408 s.<|>
409}
410"#,
411 expect![[r#"
412 me the_method() pub fn the_method(&self)
413 "#]],
414 );
415 }
416}
diff --git a/crates/ra_ide/src/completion/complete_fn_param.rs b/crates/ra_ide/src/completion/complete_fn_param.rs
deleted file mode 100644
index 406334257..000000000
--- a/crates/ra_ide/src/completion/complete_fn_param.rs
+++ /dev/null
@@ -1,135 +0,0 @@
1//! See `complete_fn_param`.
2
3use ra_syntax::{
4 ast::{self, ModuleItemOwner},
5 match_ast, AstNode,
6};
7use rustc_hash::FxHashMap;
8
9use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions};
10
11/// Complete repeated parameters, both name and type. For example, if all
12/// functions in a file have a `spam: &mut Spam` parameter, a completion with
13/// `spam: &mut Spam` insert text/label and `spam` lookup string will be
14/// suggested.
15pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) {
16 if !ctx.is_param {
17 return;
18 }
19
20 let mut params = FxHashMap::default();
21
22 let me = ctx.token.ancestors().find_map(ast::Fn::cast);
23 let mut process_fn = |func: ast::Fn| {
24 if Some(&func) == me.as_ref() {
25 return;
26 }
27 func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
28 let text = param.syntax().text().to_string();
29 params.entry(text).or_insert(param);
30 })
31 };
32
33 for node in ctx.token.parent().ancestors() {
34 match_ast! {
35 match node {
36 ast::SourceFile(it) => it.items().filter_map(|item| match item {
37 ast::Item::Fn(it) => Some(it),
38 _ => None,
39 }).for_each(&mut process_fn),
40 ast::ItemList(it) => it.items().filter_map(|item| match item {
41 ast::Item::Fn(it) => Some(it),
42 _ => None,
43 }).for_each(&mut process_fn),
44 ast::AssocItemList(it) => it.assoc_items().filter_map(|item| match item {
45 ast::AssocItem::Fn(it) => Some(it),
46 _ => None,
47 }).for_each(&mut process_fn),
48 _ => continue,
49 }
50 };
51 }
52
53 params
54 .into_iter()
55 .filter_map(|(label, param)| {
56 let lookup = param.pat()?.syntax().text().to_string();
57 Some((label, lookup))
58 })
59 .for_each(|(label, lookup)| {
60 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
61 .kind(crate::CompletionItemKind::Binding)
62 .lookup_by(lookup)
63 .add_to(acc)
64 });
65}
66
67#[cfg(test)]
68mod tests {
69 use expect::{expect, Expect};
70
71 use crate::completion::{test_utils::completion_list, CompletionKind};
72
73 fn check(ra_fixture: &str, expect: Expect) {
74 let actual = completion_list(ra_fixture, CompletionKind::Magic);
75 expect.assert_eq(&actual);
76 }
77
78 #[test]
79 fn test_param_completion_last_param() {
80 check(
81 r#"
82fn foo(file_id: FileId) {}
83fn bar(file_id: FileId) {}
84fn baz(file<|>) {}
85"#,
86 expect![[r#"
87 bn file_id: FileId
88 "#]],
89 );
90 }
91
92 #[test]
93 fn test_param_completion_nth_param() {
94 check(
95 r#"
96fn foo(file_id: FileId) {}
97fn baz(file<|>, x: i32) {}
98"#,
99 expect![[r#"
100 bn file_id: FileId
101 "#]],
102 );
103 }
104
105 #[test]
106 fn test_param_completion_trait_param() {
107 check(
108 r#"
109pub(crate) trait SourceRoot {
110 pub fn contains(&self, file_id: FileId) -> bool;
111 pub fn module_map(&self) -> &ModuleMap;
112 pub fn lines(&self, file_id: FileId) -> &LineIndex;
113 pub fn syntax(&self, file<|>)
114}
115"#,
116 expect![[r#"
117 bn file_id: FileId
118 "#]],
119 );
120 }
121
122 #[test]
123 fn completes_param_in_inner_function() {
124 check(
125 r#"
126fn outer(text: String) {
127 fn inner(<|>)
128}
129"#,
130 expect![[r#"
131 bn text: String
132 "#]],
133 )
134 }
135}
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs
deleted file mode 100644
index b62064797..000000000
--- a/crates/ra_ide/src/completion/complete_keyword.rs
+++ /dev/null
@@ -1,536 +0,0 @@
1//! FIXME: write short doc here
2
3use ra_syntax::{ast, SyntaxKind};
4use test_utils::mark;
5
6use crate::completion::{
7 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
8};
9
10pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
11 // complete keyword "crate" in use stmt
12 let source_range = ctx.source_range();
13 match (ctx.use_item_syntax.as_ref(), ctx.path_prefix.as_ref()) {
14 (Some(_), None) => {
15 CompletionItem::new(CompletionKind::Keyword, source_range, "crate::")
16 .kind(CompletionItemKind::Keyword)
17 .insert_text("crate::")
18 .add_to(acc);
19 CompletionItem::new(CompletionKind::Keyword, source_range, "self")
20 .kind(CompletionItemKind::Keyword)
21 .add_to(acc);
22 CompletionItem::new(CompletionKind::Keyword, source_range, "super::")
23 .kind(CompletionItemKind::Keyword)
24 .insert_text("super::")
25 .add_to(acc);
26 }
27 (Some(_), Some(_)) => {
28 CompletionItem::new(CompletionKind::Keyword, source_range, "self")
29 .kind(CompletionItemKind::Keyword)
30 .add_to(acc);
31 CompletionItem::new(CompletionKind::Keyword, source_range, "super::")
32 .kind(CompletionItemKind::Keyword)
33 .insert_text("super::")
34 .add_to(acc);
35 }
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 }
51}
52
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
59 let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
60 if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
61 add_keyword(ctx, acc, "where", "where ");
62 return;
63 }
64 if ctx.unsafe_is_prev {
65 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
66 add_keyword(ctx, acc, "fn", "fn $0() {}")
67 }
68
69 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
70 add_keyword(ctx, acc, "trait", "trait $0 {}");
71 add_keyword(ctx, acc, "impl", "impl $0 {}");
72 }
73
74 return;
75 }
76 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
77 {
78 add_keyword(ctx, acc, "fn", "fn $0() {}");
79 }
80 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
81 add_keyword(ctx, acc, "use", "use ");
82 add_keyword(ctx, acc, "impl", "impl $0 {}");
83 add_keyword(ctx, acc, "trait", "trait $0 {}");
84 }
85
86 if ctx.has_item_list_or_source_file_parent {
87 add_keyword(ctx, acc, "enum", "enum $0 {}");
88 add_keyword(ctx, acc, "struct", "struct $0");
89 add_keyword(ctx, acc, "union", "union $0 {}");
90 }
91
92 if ctx.is_expr {
93 add_keyword(ctx, acc, "match", "match $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 ");
98 }
99
100 if ctx.if_is_prev || ctx.block_expr_parent {
101 add_keyword(ctx, acc, "let", "let ");
102 }
103
104 if ctx.after_if {
105 add_keyword(ctx, acc, "else", "else {$0}");
106 add_keyword(ctx, acc, "else if", "else if $0 {}");
107 }
108 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
109 add_keyword(ctx, acc, "mod", "mod $0 {}");
110 }
111 if ctx.bind_pat_parent || ctx.ref_pat_parent {
112 add_keyword(ctx, acc, "mut", "mut ");
113 }
114 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
115 {
116 add_keyword(ctx, acc, "const", "const ");
117 add_keyword(ctx, acc, "type", "type ");
118 }
119 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
120 add_keyword(ctx, acc, "static", "static ");
121 };
122 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
123 add_keyword(ctx, acc, "extern", "extern ");
124 }
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 {
130 add_keyword(ctx, acc, "unsafe", "unsafe ");
131 }
132 if ctx.in_loop_body {
133 if ctx.can_be_stmt {
134 add_keyword(ctx, acc, "continue", "continue;");
135 add_keyword(ctx, acc, "break", "break;");
136 } else {
137 add_keyword(ctx, acc, "continue", "continue");
138 add_keyword(ctx, acc, "break", "break");
139 }
140 }
141 if ctx.has_item_list_or_source_file_parent || ctx.has_impl_parent {
142 add_keyword(ctx, acc, "pub", "pub ")
143 }
144
145 if !ctx.is_trivial_path {
146 return;
147 }
148 let fn_def = match &ctx.function_syntax {
149 Some(it) => it,
150 None => return,
151 };
152 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
153}
154
155fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
156 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
157 .kind(CompletionItemKind::Keyword);
158
159 match ctx.config.snippet_cap {
160 Some(cap) => res.insert_snippet(cap, snippet),
161 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
162 }
163 .build()
164}
165
166fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
167 acc.add(keyword(ctx, kw, snippet));
168}
169
170fn complete_return(
171 ctx: &CompletionContext,
172 fn_def: &ast::Fn,
173 can_be_stmt: bool,
174) -> Option<CompletionItem> {
175 let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
176 (true, true) => "return $0;",
177 (true, false) => "return;",
178 (false, true) => "return $0",
179 (false, false) => "return",
180 };
181 Some(keyword(ctx, "return", snip))
182}
183
184#[cfg(test)]
185mod tests {
186 use expect::{expect, Expect};
187
188 use crate::completion::{
189 test_utils::{check_edit, completion_list},
190 CompletionKind,
191 };
192 use test_utils::mark;
193
194 fn check(ra_fixture: &str, expect: Expect) {
195 let actual = completion_list(ra_fixture, CompletionKind::Keyword);
196 expect.assert_eq(&actual)
197 }
198
199 #[test]
200 fn test_keywords_in_use_stmt() {
201 check(
202 r"use <|>",
203 expect![[r#"
204 kw crate::
205 kw self
206 kw super::
207 "#]],
208 );
209
210 check(
211 r"use a::<|>",
212 expect![[r#"
213 kw self
214 kw super::
215 "#]],
216 );
217
218 check(
219 r"use a::{b, <|>}",
220 expect![[r#"
221 kw self
222 kw super::
223 "#]],
224 );
225 }
226
227 #[test]
228 fn test_keywords_at_source_file_level() {
229 check(
230 r"m<|>",
231 expect![[r#"
232 kw const
233 kw enum
234 kw extern
235 kw fn
236 kw impl
237 kw mod
238 kw pub
239 kw static
240 kw struct
241 kw trait
242 kw type
243 kw union
244 kw unsafe
245 kw use
246 "#]],
247 );
248 }
249
250 #[test]
251 fn test_keywords_in_function() {
252 check(
253 r"fn quux() { <|> }",
254 expect![[r#"
255 kw const
256 kw extern
257 kw fn
258 kw if
259 kw if let
260 kw impl
261 kw let
262 kw loop
263 kw match
264 kw mod
265 kw return
266 kw static
267 kw trait
268 kw type
269 kw unsafe
270 kw use
271 kw while
272 "#]],
273 );
274 }
275
276 #[test]
277 fn test_keywords_inside_block() {
278 check(
279 r"fn quux() { if true { <|> } }",
280 expect![[r#"
281 kw const
282 kw extern
283 kw fn
284 kw if
285 kw if let
286 kw impl
287 kw let
288 kw loop
289 kw match
290 kw mod
291 kw return
292 kw static
293 kw trait
294 kw type
295 kw unsafe
296 kw use
297 kw while
298 "#]],
299 );
300 }
301
302 #[test]
303 fn test_keywords_after_if() {
304 check(
305 r#"fn quux() { if true { () } <|> }"#,
306 expect![[r#"
307 kw const
308 kw else
309 kw else if
310 kw extern
311 kw fn
312 kw if
313 kw if let
314 kw impl
315 kw let
316 kw loop
317 kw match
318 kw mod
319 kw return
320 kw static
321 kw trait
322 kw type
323 kw unsafe
324 kw use
325 kw while
326 "#]],
327 );
328 check_edit(
329 "else",
330 r#"fn quux() { if true { () } <|> }"#,
331 r#"fn quux() { if true { () } else {$0} }"#,
332 );
333 }
334
335 #[test]
336 fn test_keywords_in_match_arm() {
337 check(
338 r#"
339fn quux() -> i32 {
340 match () { () => <|> }
341}
342"#,
343 expect![[r#"
344 kw if
345 kw if let
346 kw loop
347 kw match
348 kw return
349 kw unsafe
350 kw while
351 "#]],
352 );
353 }
354
355 #[test]
356 fn test_keywords_in_trait_def() {
357 check(
358 r"trait My { <|> }",
359 expect![[r#"
360 kw const
361 kw fn
362 kw type
363 kw unsafe
364 "#]],
365 );
366 }
367
368 #[test]
369 fn test_keywords_in_impl_def() {
370 check(
371 r"impl My { <|> }",
372 expect![[r#"
373 kw const
374 kw fn
375 kw pub
376 kw type
377 kw unsafe
378 "#]],
379 );
380 }
381
382 #[test]
383 fn test_keywords_in_loop() {
384 check(
385 r"fn my() { loop { <|> } }",
386 expect![[r#"
387 kw break
388 kw const
389 kw continue
390 kw extern
391 kw fn
392 kw if
393 kw if let
394 kw impl
395 kw let
396 kw loop
397 kw match
398 kw mod
399 kw return
400 kw static
401 kw trait
402 kw type
403 kw unsafe
404 kw use
405 kw while
406 "#]],
407 );
408 }
409
410 #[test]
411 fn test_keywords_after_unsafe_in_item_list() {
412 check(
413 r"unsafe <|>",
414 expect![[r#"
415 kw fn
416 kw impl
417 kw trait
418 "#]],
419 );
420 }
421
422 #[test]
423 fn test_keywords_after_unsafe_in_block_expr() {
424 check(
425 r"fn my_fn() { unsafe <|> }",
426 expect![[r#"
427 kw fn
428 kw impl
429 kw trait
430 "#]],
431 );
432 }
433
434 #[test]
435 fn test_mut_in_ref_and_in_fn_parameters_list() {
436 check(
437 r"fn my_fn(&<|>) {}",
438 expect![[r#"
439 kw mut
440 "#]],
441 );
442 check(
443 r"fn my_fn(<|>) {}",
444 expect![[r#"
445 kw mut
446 "#]],
447 );
448 check(
449 r"fn my_fn() { let &<|> }",
450 expect![[r#"
451 kw mut
452 "#]],
453 );
454 }
455
456 #[test]
457 fn test_where_keyword() {
458 check(
459 r"trait A <|>",
460 expect![[r#"
461 kw where
462 "#]],
463 );
464 check(
465 r"impl A <|>",
466 expect![[r#"
467 kw where
468 "#]],
469 );
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 }
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
deleted file mode 100644
index 0447f0511..000000000
--- a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
+++ /dev/null
@@ -1,41 +0,0 @@
1//! FIXME: write short doc here
2
3use crate::completion::{CompletionContext, Completions};
4
5pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
6 // Show only macros in top level.
7 if ctx.is_new_item {
8 ctx.scope.process_all_names(&mut |name, res| {
9 if let hir::ScopeDef::MacroDef(mac) = res {
10 acc.add_macro(ctx, Some(name.to_string()), mac);
11 }
12 })
13 }
14}
15
16#[cfg(test)]
17mod tests {
18 use expect::{expect, Expect};
19
20 use crate::completion::{test_utils::completion_list, CompletionKind};
21
22 fn check(ra_fixture: &str, expect: Expect) {
23 let actual = completion_list(ra_fixture, CompletionKind::Reference);
24 expect.assert_eq(&actual)
25 }
26
27 #[test]
28 fn completes_macros_as_item() {
29 check(
30 r#"
31macro_rules! foo { () => {} }
32fn foo() {}
33
34<|>
35"#,
36 expect![[r#"
37 ma foo!(…) macro_rules! foo
38 "#]],
39 )
40 }
41}
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs
deleted file mode 100644
index aceb77cb5..000000000
--- a/crates/ra_ide/src/completion/complete_pattern.rs
+++ /dev/null
@@ -1,88 +0,0 @@
1//! FIXME: write short doc here
2
3use crate::completion::{CompletionContext, Completions};
4
5/// Completes constats and paths in patterns.
6pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
7 if !ctx.is_pat_binding_or_const {
8 return;
9 }
10 if ctx.record_pat_syntax.is_some() {
11 return;
12 }
13
14 // FIXME: ideally, we should look at the type we are matching against and
15 // suggest variants + auto-imports
16 ctx.scope.process_all_names(&mut |name, res| {
17 match &res {
18 hir::ScopeDef::ModuleDef(def) => match def {
19 hir::ModuleDef::Adt(hir::Adt::Enum(..))
20 | hir::ModuleDef::Adt(hir::Adt::Struct(..))
21 | hir::ModuleDef::EnumVariant(..)
22 | hir::ModuleDef::Const(..)
23 | hir::ModuleDef::Module(..) => (),
24 _ => return,
25 },
26 hir::ScopeDef::MacroDef(_) => (),
27 _ => return,
28 };
29
30 acc.add_resolution(ctx, name.to_string(), &res)
31 });
32}
33
34#[cfg(test)]
35mod tests {
36 use expect::{expect, Expect};
37
38 use crate::completion::{test_utils::completion_list, CompletionKind};
39
40 fn check(ra_fixture: &str, expect: Expect) {
41 let actual = completion_list(ra_fixture, CompletionKind::Reference);
42 expect.assert_eq(&actual)
43 }
44
45 #[test]
46 fn completes_enum_variants_and_modules() {
47 check(
48 r#"
49enum E { X }
50use self::E::X;
51const Z: E = E::X;
52mod m {}
53
54static FOO: E = E::X;
55struct Bar { f: u32 }
56
57fn foo() {
58 match E::X { <|> }
59}
60"#,
61 expect![[r#"
62 st Bar
63 en E
64 ev X ()
65 ct Z
66 md m
67 "#]],
68 );
69 }
70
71 #[test]
72 fn completes_in_simple_macro_call() {
73 check(
74 r#"
75macro_rules! m { ($e:expr) => { $e } }
76enum E { X }
77
78fn foo() {
79 m!(match E::X { <|> })
80}
81"#,
82 expect![[r#"
83 en E
84 ma m!(…) macro_rules! m
85 "#]],
86 );
87 }
88}
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
deleted file mode 100644
index 8735b9010..000000000
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ /dev/null
@@ -1,378 +0,0 @@
1//! FIXME: write short doc here
2use ra_assists::utils::TryEnum;
3use ra_syntax::{
4 ast::{self, AstNode},
5 TextRange, TextSize,
6};
7use ra_text_edit::TextEdit;
8
9use crate::{
10 completion::{
11 completion_config::SnippetCap,
12 completion_context::CompletionContext,
13 completion_item::{Builder, CompletionKind, Completions},
14 },
15 CompletionItem, CompletionItemKind,
16};
17
18pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
19 if !ctx.config.enable_postfix_completions {
20 return;
21 }
22
23 let dot_receiver = match &ctx.dot_receiver {
24 Some(it) => it,
25 None => return,
26 };
27
28 let receiver_text =
29 get_receiver_text(dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
30
31 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
32 Some(it) => it,
33 None => return,
34 };
35
36 let cap = match ctx.config.snippet_cap {
37 Some(it) => it,
38 None => return,
39 };
40 let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty);
41 if let Some(try_enum) = &try_enum {
42 match try_enum {
43 TryEnum::Result => {
44 postfix_snippet(
45 ctx,
46 cap,
47 &dot_receiver,
48 "ifl",
49 "if let Ok {}",
50 &format!("if let Ok($1) = {} {{\n $0\n}}", receiver_text),
51 )
52 .add_to(acc);
53
54 postfix_snippet(
55 ctx,
56 cap,
57 &dot_receiver,
58 "while",
59 "while let Ok {}",
60 &format!("while let Ok($1) = {} {{\n $0\n}}", receiver_text),
61 )
62 .add_to(acc);
63 }
64 TryEnum::Option => {
65 postfix_snippet(
66 ctx,
67 cap,
68 &dot_receiver,
69 "ifl",
70 "if let Some {}",
71 &format!("if let Some($1) = {} {{\n $0\n}}", receiver_text),
72 )
73 .add_to(acc);
74
75 postfix_snippet(
76 ctx,
77 cap,
78 &dot_receiver,
79 "while",
80 "while let Some {}",
81 &format!("while let Some($1) = {} {{\n $0\n}}", receiver_text),
82 )
83 .add_to(acc);
84 }
85 }
86 } else if receiver_ty.is_bool() || receiver_ty.is_unknown() {
87 postfix_snippet(
88 ctx,
89 cap,
90 &dot_receiver,
91 "if",
92 "if expr {}",
93 &format!("if {} {{\n $0\n}}", receiver_text),
94 )
95 .add_to(acc);
96 postfix_snippet(
97 ctx,
98 cap,
99 &dot_receiver,
100 "while",
101 "while expr {}",
102 &format!("while {} {{\n $0\n}}", receiver_text),
103 )
104 .add_to(acc);
105 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
106 .add_to(acc);
107 }
108
109 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
110 .add_to(acc);
111 postfix_snippet(
112 ctx,
113 cap,
114 &dot_receiver,
115 "refm",
116 "&mut expr",
117 &format!("&mut {}", receiver_text),
118 )
119 .add_to(acc);
120
121 // The rest of the postfix completions create an expression that moves an argument,
122 // so it's better to consider references now to avoid breaking the compilation
123 let dot_receiver = include_references(dot_receiver);
124 let receiver_text =
125 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
126
127 match try_enum {
128 Some(try_enum) => match try_enum {
129 TryEnum::Result => {
130 postfix_snippet(
131 ctx,
132 cap,
133 &dot_receiver,
134 "match",
135 "match expr {}",
136 &format!("match {} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}", receiver_text),
137 )
138 .add_to(acc);
139 }
140 TryEnum::Option => {
141 postfix_snippet(
142 ctx,
143 cap,
144 &dot_receiver,
145 "match",
146 "match expr {}",
147 &format!(
148 "match {} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}",
149 receiver_text
150 ),
151 )
152 .add_to(acc);
153 }
154 },
155 None => {
156 postfix_snippet(
157 ctx,
158 cap,
159 &dot_receiver,
160 "match",
161 "match expr {}",
162 &format!("match {} {{\n ${{1:_}} => {{$0}},\n}}", receiver_text),
163 )
164 .add_to(acc);
165 }
166 }
167
168 postfix_snippet(
169 ctx,
170 cap,
171 &dot_receiver,
172 "box",
173 "Box::new(expr)",
174 &format!("Box::new({})", receiver_text),
175 )
176 .add_to(acc);
177
178 postfix_snippet(
179 ctx,
180 cap,
181 &dot_receiver,
182 "dbg",
183 "dbg!(expr)",
184 &format!("dbg!({})", receiver_text),
185 )
186 .add_to(acc);
187
188 postfix_snippet(
189 ctx,
190 cap,
191 &dot_receiver,
192 "call",
193 "function(expr)",
194 &format!("${{1}}({})", receiver_text),
195 )
196 .add_to(acc);
197}
198
199fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
200 if receiver_is_ambiguous_float_literal {
201 let text = receiver.syntax().text();
202 let without_dot = ..text.len() - TextSize::of('.');
203 text.slice(without_dot).to_string()
204 } else {
205 receiver.to_string()
206 }
207}
208
209fn include_references(initial_element: &ast::Expr) -> ast::Expr {
210 let mut resulting_element = initial_element.clone();
211 while let Some(parent_ref_element) =
212 resulting_element.syntax().parent().and_then(ast::RefExpr::cast)
213 {
214 resulting_element = ast::Expr::from(parent_ref_element);
215 }
216 resulting_element
217}
218
219fn postfix_snippet(
220 ctx: &CompletionContext,
221 cap: SnippetCap,
222 receiver: &ast::Expr,
223 label: &str,
224 detail: &str,
225 snippet: &str,
226) -> Builder {
227 let edit = {
228 let receiver_syntax = receiver.syntax();
229 let receiver_range = ctx.sema.original_range(receiver_syntax).range;
230 let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
231 TextEdit::replace(delete_range, snippet.to_string())
232 };
233 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
234 .detail(detail)
235 .kind(CompletionItemKind::Snippet)
236 .snippet_edit(cap, edit)
237}
238
239#[cfg(test)]
240mod tests {
241 use expect::{expect, Expect};
242
243 use crate::completion::{
244 test_utils::{check_edit, completion_list},
245 CompletionKind,
246 };
247
248 fn check(ra_fixture: &str, expect: Expect) {
249 let actual = completion_list(ra_fixture, CompletionKind::Postfix);
250 expect.assert_eq(&actual)
251 }
252
253 #[test]
254 fn postfix_completion_works_for_trivial_path_expression() {
255 check(
256 r#"
257fn main() {
258 let bar = true;
259 bar.<|>
260}
261"#,
262 expect![[r#"
263 sn box Box::new(expr)
264 sn call function(expr)
265 sn dbg dbg!(expr)
266 sn if if expr {}
267 sn match match expr {}
268 sn not !expr
269 sn ref &expr
270 sn refm &mut expr
271 sn while while expr {}
272 "#]],
273 );
274 }
275
276 #[test]
277 fn postfix_type_filtering() {
278 check(
279 r#"
280fn main() {
281 let bar: u8 = 12;
282 bar.<|>
283}
284"#,
285 expect![[r#"
286 sn box Box::new(expr)
287 sn call function(expr)
288 sn dbg dbg!(expr)
289 sn match match expr {}
290 sn ref &expr
291 sn refm &mut expr
292 "#]],
293 )
294 }
295
296 #[test]
297 fn option_iflet() {
298 check_edit(
299 "ifl",
300 r#"
301enum Option<T> { Some(T), None }
302
303fn main() {
304 let bar = Option::Some(true);
305 bar.<|>
306}
307"#,
308 r#"
309enum Option<T> { Some(T), None }
310
311fn main() {
312 let bar = Option::Some(true);
313 if let Some($1) = bar {
314 $0
315}
316}
317"#,
318 );
319 }
320
321 #[test]
322 fn result_match() {
323 check_edit(
324 "match",
325 r#"
326enum Result<T, E> { Ok(T), Err(E) }
327
328fn main() {
329 let bar = Result::Ok(true);
330 bar.<|>
331}
332"#,
333 r#"
334enum Result<T, E> { Ok(T), Err(E) }
335
336fn main() {
337 let bar = Result::Ok(true);
338 match bar {
339 Ok(${1:_}) => {$2},
340 Err(${3:_}) => {$0},
341}
342}
343"#,
344 );
345 }
346
347 #[test]
348 fn postfix_completion_works_for_ambiguous_float_literal() {
349 check_edit("refm", r#"fn main() { 42.<|> }"#, r#"fn main() { &mut 42 }"#)
350 }
351
352 #[test]
353 fn works_in_simple_macro() {
354 check_edit(
355 "dbg",
356 r#"
357macro_rules! m { ($e:expr) => { $e } }
358fn main() {
359 let bar: u8 = 12;
360 m!(bar.d<|>)
361}
362"#,
363 r#"
364macro_rules! m { ($e:expr) => { $e } }
365fn main() {
366 let bar: u8 = 12;
367 m!(dbg!(bar))
368}
369"#,
370 );
371 }
372
373 #[test]
374 fn postfix_completion_for_references() {
375 check_edit("dbg", r#"fn main() { &&42.<|> }"#, r#"fn main() { dbg!(&&42) }"#);
376 check_edit("refm", r#"fn main() { &&42.<|> }"#, r#"fn main() { &&&mut 42 }"#);
377 }
378}
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs
deleted file mode 100644
index b08f5b9b4..000000000
--- a/crates/ra_ide/src/completion/complete_qualified_path.rs
+++ /dev/null
@@ -1,733 +0,0 @@
1//! Completion of paths, i.e. `some::prefix::<|>`.
2
3use hir::{Adt, HasVisibility, PathResolution, ScopeDef};
4use ra_syntax::AstNode;
5use rustc_hash::FxHashSet;
6use test_utils::mark;
7
8use crate::completion::{CompletionContext, Completions};
9
10pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
11 let path = match &ctx.path_prefix {
12 Some(path) => path.clone(),
13 None => return,
14 };
15
16 if ctx.attribute_under_caret.is_some() {
17 return;
18 }
19
20 let context_module = ctx.scope.module();
21
22 let resolution = match ctx.scope.resolve_hir_path_qualifier(&path) {
23 Some(res) => res,
24 None => return,
25 };
26
27 // Add associated types on type parameters and `Self`.
28 resolution.assoc_type_shorthand_candidates(ctx.db, |alias| {
29 acc.add_type_alias(ctx, alias);
30 None::<()>
31 });
32
33 match resolution {
34 PathResolution::Def(hir::ModuleDef::Module(module)) => {
35 let module_scope = module.scope(ctx.db, context_module);
36 for (name, def) in module_scope {
37 if ctx.use_item_syntax.is_some() {
38 if let ScopeDef::Unknown = def {
39 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() {
40 if name_ref.syntax().text() == name.to_string().as_str() {
41 // for `use self::foo<|>`, don't suggest `foo` as a completion
42 mark::hit!(dont_complete_current_use);
43 continue;
44 }
45 }
46 }
47 }
48
49 acc.add_resolution(ctx, name.to_string(), &def);
50 }
51 }
52 PathResolution::Def(def @ hir::ModuleDef::Adt(_))
53 | PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) => {
54 if let hir::ModuleDef::Adt(Adt::Enum(e)) = def {
55 for variant in e.variants(ctx.db) {
56 acc.add_enum_variant(ctx, variant, None);
57 }
58 }
59 let ty = match def {
60 hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
61 hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
62 _ => unreachable!(),
63 };
64
65 // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType.
66 // (where AssocType is defined on a trait, not an inherent impl)
67
68 let krate = ctx.krate;
69 if let Some(krate) = krate {
70 let traits_in_scope = ctx.scope.traits_in_scope();
71 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
72 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
73 return None;
74 }
75 match item {
76 hir::AssocItem::Function(func) => {
77 acc.add_function(ctx, func, None);
78 }
79 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
80 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
81 }
82 None::<()>
83 });
84
85 // Iterate assoc types separately
86 ty.iterate_assoc_items(ctx.db, krate, |item| {
87 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
88 return None;
89 }
90 match item {
91 hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {}
92 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
93 }
94 None::<()>
95 });
96 }
97 }
98 PathResolution::Def(hir::ModuleDef::Trait(t)) => {
99 // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
100 for item in t.items(ctx.db) {
101 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
102 continue;
103 }
104 match item {
105 hir::AssocItem::Function(func) => {
106 acc.add_function(ctx, func, None);
107 }
108 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
109 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
110 }
111 }
112 }
113 PathResolution::TypeParam(_) | PathResolution::SelfType(_) => {
114 if let Some(krate) = ctx.krate {
115 let ty = match resolution {
116 PathResolution::TypeParam(param) => param.ty(ctx.db),
117 PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db),
118 _ => return,
119 };
120
121 let traits_in_scope = ctx.scope.traits_in_scope();
122 let mut seen = FxHashSet::default();
123 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
124 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
125 return None;
126 }
127
128 // We might iterate candidates of a trait multiple times here, so deduplicate
129 // them.
130 if seen.insert(item) {
131 match item {
132 hir::AssocItem::Function(func) => {
133 acc.add_function(ctx, func, None);
134 }
135 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
136 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
137 }
138 }
139 None::<()>
140 });
141 }
142 }
143 _ => {}
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use expect::{expect, Expect};
150 use test_utils::mark;
151
152 use crate::completion::{
153 test_utils::{check_edit, completion_list},
154 CompletionKind,
155 };
156
157 fn check(ra_fixture: &str, expect: Expect) {
158 let actual = completion_list(ra_fixture, CompletionKind::Reference);
159 expect.assert_eq(&actual);
160 }
161
162 fn check_builtin(ra_fixture: &str, expect: Expect) {
163 let actual = completion_list(ra_fixture, CompletionKind::BuiltinType);
164 expect.assert_eq(&actual);
165 }
166
167 #[test]
168 fn dont_complete_current_use() {
169 mark::check!(dont_complete_current_use);
170 check(r#"use self::foo<|>;"#, expect![[""]]);
171 }
172
173 #[test]
174 fn dont_complete_current_use_in_braces_with_glob() {
175 check(
176 r#"
177mod foo { pub struct S; }
178use self::{foo::*, bar<|>};
179"#,
180 expect![[r#"
181 st S
182 md foo
183 "#]],
184 );
185 }
186
187 #[test]
188 fn dont_complete_primitive_in_use() {
189 check_builtin(r#"use self::<|>;"#, expect![[""]]);
190 }
191
192 #[test]
193 fn dont_complete_primitive_in_module_scope() {
194 check_builtin(r#"fn foo() { self::<|> }"#, expect![[""]]);
195 }
196
197 #[test]
198 fn completes_primitives() {
199 check_builtin(
200 r#"fn main() { let _: <|> = 92; }"#,
201 expect![[r#"
202 bt bool
203 bt char
204 bt f32
205 bt f64
206 bt i128
207 bt i16
208 bt i32
209 bt i64
210 bt i8
211 bt isize
212 bt str
213 bt u128
214 bt u16
215 bt u32
216 bt u64
217 bt u8
218 bt usize
219 "#]],
220 );
221 }
222
223 #[test]
224 fn completes_mod_with_same_name_as_function() {
225 check(
226 r#"
227use self::my::<|>;
228
229mod my { pub struct Bar; }
230fn my() {}
231"#,
232 expect![[r#"
233 st Bar
234 "#]],
235 );
236 }
237
238 #[test]
239 fn filters_visibility() {
240 check(
241 r#"
242use self::my::<|>;
243
244mod my {
245 struct Bar;
246 pub struct Foo;
247 pub use Bar as PublicBar;
248}
249"#,
250 expect![[r#"
251 st Foo
252 st PublicBar
253 "#]],
254 );
255 }
256
257 #[test]
258 fn completes_use_item_starting_with_self() {
259 check(
260 r#"
261use self::m::<|>;
262
263mod m { pub struct Bar; }
264"#,
265 expect![[r#"
266 st Bar
267 "#]],
268 );
269 }
270
271 #[test]
272 fn completes_use_item_starting_with_crate() {
273 check(
274 r#"
275//- /lib.rs
276mod foo;
277struct Spam;
278//- /foo.rs
279use crate::Sp<|>
280"#,
281 expect![[r#"
282 st Spam
283 md foo
284 "#]],
285 );
286 }
287
288 #[test]
289 fn completes_nested_use_tree() {
290 check(
291 r#"
292//- /lib.rs
293mod foo;
294struct Spam;
295//- /foo.rs
296use crate::{Sp<|>};
297"#,
298 expect![[r#"
299 st Spam
300 md foo
301 "#]],
302 );
303 }
304
305 #[test]
306 fn completes_deeply_nested_use_tree() {
307 check(
308 r#"
309//- /lib.rs
310mod foo;
311pub mod bar {
312 pub mod baz {
313 pub struct Spam;
314 }
315}
316//- /foo.rs
317use crate::{bar::{baz::Sp<|>}};
318"#,
319 expect![[r#"
320 st Spam
321 "#]],
322 );
323 }
324
325 #[test]
326 fn completes_enum_variant() {
327 check(
328 r#"
329enum E { Foo, Bar(i32) }
330fn foo() { let _ = E::<|> }
331"#,
332 expect![[r#"
333 ev Bar(…) (i32)
334 ev Foo ()
335 "#]],
336 );
337 }
338
339 #[test]
340 fn completes_struct_associated_items() {
341 check(
342 r#"
343//- /lib.rs
344struct S;
345
346impl S {
347 fn a() {}
348 fn b(&self) {}
349 const C: i32 = 42;
350 type T = i32;
351}
352
353fn foo() { let _ = S::<|> }
354"#,
355 expect![[r#"
356 ct C const C: i32 = 42;
357 ta T type T = i32;
358 fn a() fn a()
359 me b() fn b(&self)
360 "#]],
361 );
362 }
363
364 #[test]
365 fn associated_item_visibility() {
366 check(
367 r#"
368struct S;
369
370mod m {
371 impl super::S {
372 pub(super) fn public_method() { }
373 fn private_method() { }
374 pub(super) type PublicType = u32;
375 type PrivateType = u32;
376 pub(super) const PUBLIC_CONST: u32 = 1;
377 const PRIVATE_CONST: u32 = 1;
378 }
379}
380
381fn foo() { let _ = S::<|> }
382"#,
383 expect![[r#"
384 ct PUBLIC_CONST pub(super) const PUBLIC_CONST: u32 = 1;
385 ta PublicType pub(super) type PublicType = u32;
386 fn public_method() pub(super) fn public_method()
387 "#]],
388 );
389 }
390
391 #[test]
392 fn completes_enum_associated_method() {
393 check(
394 r#"
395enum E {};
396impl E { fn m() { } }
397
398fn foo() { let _ = E::<|> }
399 "#,
400 expect![[r#"
401 fn m() fn m()
402 "#]],
403 );
404 }
405
406 #[test]
407 fn completes_union_associated_method() {
408 check(
409 r#"
410union U {};
411impl U { fn m() { } }
412
413fn foo() { let _ = U::<|> }
414"#,
415 expect![[r#"
416 fn m() fn m()
417 "#]],
418 );
419 }
420
421 #[test]
422 fn completes_use_paths_across_crates() {
423 check(
424 r#"
425//- /main.rs
426use foo::<|>;
427
428//- /foo/lib.rs
429pub mod bar { pub struct S; }
430"#,
431 expect![[r#"
432 md bar
433 "#]],
434 );
435 }
436
437 #[test]
438 fn completes_trait_associated_method_1() {
439 check(
440 r#"
441trait Trait { fn m(); }
442
443fn foo() { let _ = Trait::<|> }
444"#,
445 expect![[r#"
446 fn m() fn m()
447 "#]],
448 );
449 }
450
451 #[test]
452 fn completes_trait_associated_method_2() {
453 check(
454 r#"
455trait Trait { fn m(); }
456
457struct S;
458impl Trait for S {}
459
460fn foo() { let _ = S::<|> }
461"#,
462 expect![[r#"
463 fn m() fn m()
464 "#]],
465 );
466 }
467
468 #[test]
469 fn completes_trait_associated_method_3() {
470 check(
471 r#"
472trait Trait { fn m(); }
473
474struct S;
475impl Trait for S {}
476
477fn foo() { let _ = <S as Trait>::<|> }
478"#,
479 expect![[r#"
480 fn m() fn m()
481 "#]],
482 );
483 }
484
485 #[test]
486 fn completes_ty_param_assoc_ty() {
487 check(
488 r#"
489trait Super {
490 type Ty;
491 const CONST: u8;
492 fn func() {}
493 fn method(&self) {}
494}
495
496trait Sub: Super {
497 type SubTy;
498 const C2: ();
499 fn subfunc() {}
500 fn submethod(&self) {}
501}
502
503fn foo<T: Sub>() { T::<|> }
504"#,
505 expect![[r#"
506 ct C2 const C2: ();
507 ct CONST const CONST: u8;
508 ta SubTy type SubTy;
509 ta Ty type Ty;
510 fn func() fn func()
511 me method() fn method(&self)
512 fn subfunc() fn subfunc()
513 me submethod() fn submethod(&self)
514 "#]],
515 );
516 }
517
518 #[test]
519 fn completes_self_param_assoc_ty() {
520 check(
521 r#"
522trait Super {
523 type Ty;
524 const CONST: u8 = 0;
525 fn func() {}
526 fn method(&self) {}
527}
528
529trait Sub: Super {
530 type SubTy;
531 const C2: () = ();
532 fn subfunc() {}
533 fn submethod(&self) {}
534}
535
536struct Wrap<T>(T);
537impl<T> Super for Wrap<T> {}
538impl<T> Sub for Wrap<T> {
539 fn subfunc() {
540 // Should be able to assume `Self: Sub + Super`
541 Self::<|>
542 }
543}
544"#,
545 expect![[r#"
546 ct C2 const C2: () = ();
547 ct CONST const CONST: u8 = 0;
548 ta SubTy type SubTy;
549 ta Ty type Ty;
550 fn func() fn func()
551 me method() fn method(&self)
552 fn subfunc() fn subfunc()
553 me submethod() fn submethod(&self)
554 "#]],
555 );
556 }
557
558 #[test]
559 fn completes_type_alias() {
560 check(
561 r#"
562struct S;
563impl S { fn foo() {} }
564type T = S;
565impl T { fn bar() {} }
566
567fn main() { T::<|>; }
568"#,
569 expect![[r#"
570 fn bar() fn bar()
571 fn foo() fn foo()
572 "#]],
573 );
574 }
575
576 #[test]
577 fn completes_qualified_macros() {
578 check(
579 r#"
580#[macro_export]
581macro_rules! foo { () => {} }
582
583fn main() { let _ = crate::<|> }
584 "#,
585 expect![[r##"
586 ma foo!(…) #[macro_export]
587 macro_rules! foo
588 fn main() fn main()
589 "##]],
590 );
591 }
592
593 #[test]
594 fn test_super_super_completion() {
595 check(
596 r#"
597mod a {
598 const A: usize = 0;
599 mod b {
600 const B: usize = 0;
601 mod c { use super::super::<|> }
602 }
603}
604"#,
605 expect![[r#"
606 ct A
607 md b
608 "#]],
609 );
610 }
611
612 #[test]
613 fn completes_reexported_items_under_correct_name() {
614 check(
615 r#"
616fn foo() { self::m::<|> }
617
618mod m {
619 pub use super::p::wrong_fn as right_fn;
620 pub use super::p::WRONG_CONST as RIGHT_CONST;
621 pub use super::p::WrongType as RightType;
622}
623mod p {
624 fn wrong_fn() {}
625 const WRONG_CONST: u32 = 1;
626 struct WrongType {};
627}
628"#,
629 expect![[r#"
630 ct RIGHT_CONST
631 st RightType
632 fn right_fn() fn wrong_fn()
633 "#]],
634 );
635
636 check_edit(
637 "RightType",
638 r#"
639fn foo() { self::m::<|> }
640
641mod m {
642 pub use super::p::wrong_fn as right_fn;
643 pub use super::p::WRONG_CONST as RIGHT_CONST;
644 pub use super::p::WrongType as RightType;
645}
646mod p {
647 fn wrong_fn() {}
648 const WRONG_CONST: u32 = 1;
649 struct WrongType {};
650}
651"#,
652 r#"
653fn foo() { self::m::RightType }
654
655mod m {
656 pub use super::p::wrong_fn as right_fn;
657 pub use super::p::WRONG_CONST as RIGHT_CONST;
658 pub use super::p::WrongType as RightType;
659}
660mod p {
661 fn wrong_fn() {}
662 const WRONG_CONST: u32 = 1;
663 struct WrongType {};
664}
665"#,
666 );
667 }
668
669 #[test]
670 fn completes_in_simple_macro_call() {
671 check(
672 r#"
673macro_rules! m { ($e:expr) => { $e } }
674fn main() { m!(self::f<|>); }
675fn foo() {}
676"#,
677 expect![[r#"
678 fn foo() fn foo()
679 fn main() fn main()
680 "#]],
681 );
682 }
683
684 #[test]
685 fn function_mod_share_name() {
686 check(
687 r#"
688fn foo() { self::m::<|> }
689
690mod m {
691 pub mod z {}
692 pub fn z() {}
693}
694"#,
695 expect![[r#"
696 md z
697 fn z() pub fn z()
698 "#]],
699 );
700 }
701
702 #[test]
703 fn completes_hashmap_new() {
704 check(
705 r#"
706struct RandomState;
707struct HashMap<K, V, S = RandomState> {}
708
709impl<K, V> HashMap<K, V, RandomState> {
710 pub fn new() -> HashMap<K, V, RandomState> { }
711}
712fn foo() {
713 HashMap::<|>
714}
715"#,
716 expect![[r#"
717 fn new() pub fn new() -> HashMap<K, V, RandomState>
718 "#]],
719 );
720 }
721
722 #[test]
723 fn dont_complete_attr() {
724 check(
725 r#"
726mod foo { pub struct Foo; }
727#[foo::<|>]
728fn f() {}
729"#,
730 expect![[""]],
731 );
732 }
733}
diff --git a/crates/ra_ide/src/completion/complete_record.rs b/crates/ra_ide/src/completion/complete_record.rs
deleted file mode 100644
index 74b94594d..000000000
--- a/crates/ra_ide/src/completion/complete_record.rs
+++ /dev/null
@@ -1,226 +0,0 @@
1//! Complete fields in record literals and patterns.
2use crate::completion::{CompletionContext, Completions};
3
4pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
5 let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) {
6 (None, None) => return None,
7 (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"),
8 (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat),
9 (_, Some(record_lit)) => ctx.sema.record_literal_missing_fields(record_lit),
10 };
11
12 for (field, ty) in missing_fields {
13 acc.add_field(ctx, field, &ty)
14 }
15
16 Some(())
17}
18
19#[cfg(test)]
20mod tests {
21 use expect::{expect, Expect};
22
23 use crate::completion::{test_utils::completion_list, CompletionKind};
24
25 fn check(ra_fixture: &str, expect: Expect) {
26 let actual = completion_list(ra_fixture, CompletionKind::Reference);
27 expect.assert_eq(&actual);
28 }
29
30 #[test]
31 fn test_record_pattern_field() {
32 check(
33 r#"
34struct S { foo: u32 }
35
36fn process(f: S) {
37 match f {
38 S { f<|>: 92 } => (),
39 }
40}
41"#,
42 expect![[r#"
43 fd foo u32
44 "#]],
45 );
46 }
47
48 #[test]
49 fn test_record_pattern_enum_variant() {
50 check(
51 r#"
52enum E { S { foo: u32, bar: () } }
53
54fn process(e: E) {
55 match e {
56 E::S { <|> } => (),
57 }
58}
59"#,
60 expect![[r#"
61 fd bar ()
62 fd foo u32
63 "#]],
64 );
65 }
66
67 #[test]
68 fn test_record_pattern_field_in_simple_macro() {
69 check(
70 r"
71macro_rules! m { ($e:expr) => { $e } }
72struct S { foo: u32 }
73
74fn process(f: S) {
75 m!(match f {
76 S { f<|>: 92 } => (),
77 })
78}
79",
80 expect![[r#"
81 fd foo u32
82 "#]],
83 );
84 }
85
86 #[test]
87 fn only_missing_fields_are_completed_in_destruct_pats() {
88 check(
89 r#"
90struct S {
91 foo1: u32, foo2: u32,
92 bar: u32, baz: u32,
93}
94
95fn main() {
96 let s = S {
97 foo1: 1, foo2: 2,
98 bar: 3, baz: 4,
99 };
100 if let S { foo1, foo2: a, <|> } = s {}
101}
102"#,
103 expect![[r#"
104 fd bar u32
105 fd baz u32
106 "#]],
107 );
108 }
109
110 #[test]
111 fn test_record_literal_field() {
112 check(
113 r#"
114struct A { the_field: u32 }
115fn foo() {
116 A { the<|> }
117}
118"#,
119 expect![[r#"
120 fd the_field u32
121 "#]],
122 );
123 }
124
125 #[test]
126 fn test_record_literal_enum_variant() {
127 check(
128 r#"
129enum E { A { a: u32 } }
130fn foo() {
131 let _ = E::A { <|> }
132}
133"#,
134 expect![[r#"
135 fd a u32
136 "#]],
137 );
138 }
139
140 #[test]
141 fn test_record_literal_two_structs() {
142 check(
143 r#"
144struct A { a: u32 }
145struct B { b: u32 }
146
147fn foo() {
148 let _: A = B { <|> }
149}
150"#,
151 expect![[r#"
152 fd b u32
153 "#]],
154 );
155 }
156
157 #[test]
158 fn test_record_literal_generic_struct() {
159 check(
160 r#"
161struct A<T> { a: T }
162
163fn foo() {
164 let _: A<u32> = A { <|> }
165}
166"#,
167 expect![[r#"
168 fd a u32
169 "#]],
170 );
171 }
172
173 #[test]
174 fn test_record_literal_field_in_simple_macro() {
175 check(
176 r#"
177macro_rules! m { ($e:expr) => { $e } }
178struct A { the_field: u32 }
179fn foo() {
180 m!(A { the<|> })
181}
182"#,
183 expect![[r#"
184 fd the_field u32
185 "#]],
186 );
187 }
188
189 #[test]
190 fn only_missing_fields_are_completed() {
191 check(
192 r#"
193struct S {
194 foo1: u32, foo2: u32,
195 bar: u32, baz: u32,
196}
197
198fn main() {
199 let foo1 = 1;
200 let s = S { foo1, foo2: 5, <|> }
201}
202"#,
203 expect![[r#"
204 fd bar u32
205 fd baz u32
206 "#]],
207 );
208 }
209
210 #[test]
211 fn completes_functional_update() {
212 check(
213 r#"
214struct S { foo1: u32, foo2: u32 }
215
216fn main() {
217 let foo1 = 1;
218 let s = S { foo1, <|> .. loop {} }
219}
220"#,
221 expect![[r#"
222 fd foo2 u32
223 "#]],
224 );
225 }
226}
diff --git a/crates/ra_ide/src/completion/complete_snippet.rs b/crates/ra_ide/src/completion/complete_snippet.rs
deleted file mode 100644
index 28d8f7876..000000000
--- a/crates/ra_ide/src/completion/complete_snippet.rs
+++ /dev/null
@@ -1,116 +0,0 @@
1//! FIXME: write short doc here
2
3use crate::completion::{
4 completion_config::SnippetCap, completion_item::Builder, CompletionContext, CompletionItem,
5 CompletionItemKind, CompletionKind, Completions,
6};
7
8fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
9 CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label)
10 .insert_snippet(cap, snippet)
11 .kind(CompletionItemKind::Snippet)
12}
13
14pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
15 if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) {
16 return;
17 }
18 let cap = match ctx.config.snippet_cap {
19 Some(it) => it,
20 None => return,
21 };
22
23 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
24 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
25}
26
27pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
28 if !ctx.is_new_item {
29 return;
30 }
31 let cap = match ctx.config.snippet_cap {
32 Some(it) => it,
33 None => return,
34 };
35
36 snippet(
37 ctx,
38 cap,
39 "Test module",
40 "\
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn ${1:test_name}() {
47 $0
48 }
49}",
50 )
51 .lookup_by("tmod")
52 .add_to(acc);
53
54 snippet(
55 ctx,
56 cap,
57 "Test function",
58 "\
59#[test]
60fn ${1:feature}() {
61 $0
62}",
63 )
64 .lookup_by("tfn")
65 .add_to(acc);
66
67 snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc);
68 snippet(ctx, cap, "pub(crate)", "pub(crate) $0").add_to(acc);
69}
70
71#[cfg(test)]
72mod tests {
73 use expect::{expect, Expect};
74
75 use crate::completion::{test_utils::completion_list, CompletionKind};
76
77 fn check(ra_fixture: &str, expect: Expect) {
78 let actual = completion_list(ra_fixture, CompletionKind::Snippet);
79 expect.assert_eq(&actual)
80 }
81
82 #[test]
83 fn completes_snippets_in_expressions() {
84 check(
85 r#"fn foo(x: i32) { <|> }"#,
86 expect![[r#"
87 sn pd
88 sn ppd
89 "#]],
90 );
91 }
92
93 #[test]
94 fn should_not_complete_snippets_in_path() {
95 check(r#"fn foo(x: i32) { ::foo<|> }"#, expect![[""]]);
96 check(r#"fn foo(x: i32) { ::<|> }"#, expect![[""]]);
97 }
98
99 #[test]
100 fn completes_snippets_in_items() {
101 check(
102 r#"
103#[cfg(test)]
104mod tests {
105 <|>
106}
107"#,
108 expect![[r#"
109 sn Test function
110 sn Test module
111 sn macro_rules
112 sn pub(crate)
113 "#]],
114 )
115 }
116}
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
deleted file mode 100644
index d9a0ef167..000000000
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ /dev/null
@@ -1,488 +0,0 @@
1//! Completion for associated items in a trait implementation.
2//!
3//! This module adds the completion items related to implementing associated
4//! items within a `impl Trait for Struct` block. The current context node
5//! must be within either a `FN`, `TYPE_ALIAS`, or `CONST` node
6//! and an direct child of an `IMPL`.
7//!
8//! # Examples
9//!
10//! Considering the following trait `impl`:
11//!
12//! ```ignore
13//! trait SomeTrait {
14//! fn foo();
15//! }
16//!
17//! impl SomeTrait for () {
18//! fn f<|>
19//! }
20//! ```
21//!
22//! may result in the completion of the following method:
23//!
24//! ```ignore
25//! # trait SomeTrait {
26//! # fn foo();
27//! # }
28//!
29//! impl SomeTrait for () {
30//! fn foo() {}<|>
31//! }
32//! ```
33
34use hir::{self, Docs, HasSource};
35use ra_assists::utils::get_missing_assoc_items;
36use ra_syntax::{
37 ast::{self, edit, Impl},
38 AstNode, SyntaxKind, SyntaxNode, TextRange, T,
39};
40use ra_text_edit::TextEdit;
41
42use crate::{
43 completion::{
44 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
45 },
46 display::function_declaration,
47};
48
49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
50 if let Some((trigger, impl_def)) = completion_match(ctx) {
51 match trigger.kind() {
52 SyntaxKind::NAME_REF => get_missing_assoc_items(&ctx.sema, &impl_def)
53 .into_iter()
54 .for_each(|item| match item {
55 hir::AssocItem::Function(fn_item) => {
56 add_function_impl(&trigger, acc, ctx, fn_item)
57 }
58 hir::AssocItem::TypeAlias(type_item) => {
59 add_type_alias_impl(&trigger, acc, ctx, type_item)
60 }
61 hir::AssocItem::Const(const_item) => {
62 add_const_impl(&trigger, acc, ctx, const_item)
63 }
64 }),
65
66 SyntaxKind::FN => {
67 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
68 .into_iter()
69 .filter_map(|item| match item {
70 hir::AssocItem::Function(fn_item) => Some(fn_item),
71 _ => None,
72 })
73 {
74 add_function_impl(&trigger, acc, ctx, missing_fn);
75 }
76 }
77
78 SyntaxKind::TYPE_ALIAS => {
79 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
80 .into_iter()
81 .filter_map(|item| match item {
82 hir::AssocItem::TypeAlias(type_item) => Some(type_item),
83 _ => None,
84 })
85 {
86 add_type_alias_impl(&trigger, acc, ctx, missing_fn);
87 }
88 }
89
90 SyntaxKind::CONST => {
91 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
92 .into_iter()
93 .filter_map(|item| match item {
94 hir::AssocItem::Const(const_item) => Some(const_item),
95 _ => None,
96 })
97 {
98 add_const_impl(&trigger, acc, ctx, missing_fn);
99 }
100 }
101
102 _ => {}
103 }
104 }
105}
106
107fn completion_match(ctx: &CompletionContext) -> Option<(SyntaxNode, Impl)> {
108 let (trigger, impl_def_offset) = ctx.token.ancestors().find_map(|p| match p.kind() {
109 SyntaxKind::FN | SyntaxKind::TYPE_ALIAS | SyntaxKind::CONST | SyntaxKind::BLOCK_EXPR => {
110 Some((p, 2))
111 }
112 SyntaxKind::NAME_REF => Some((p, 5)),
113 _ => None,
114 })?;
115 let impl_def = (0..impl_def_offset - 1)
116 .try_fold(trigger.parent()?, |t, _| t.parent())
117 .and_then(ast::Impl::cast)?;
118 Some((trigger, impl_def))
119}
120
121fn add_function_impl(
122 fn_def_node: &SyntaxNode,
123 acc: &mut Completions,
124 ctx: &CompletionContext,
125 func: hir::Function,
126) {
127 let fn_name = func.name(ctx.db).to_string();
128
129 let label = if !func.params(ctx.db).is_empty() {
130 format!("fn {}(..)", fn_name)
131 } else {
132 format!("fn {}()", fn_name)
133 };
134
135 let builder = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
136 .lookup_by(fn_name)
137 .set_documentation(func.docs(ctx.db));
138
139 let completion_kind = if func.has_self_param(ctx.db) {
140 CompletionItemKind::Method
141 } else {
142 CompletionItemKind::Function
143 };
144 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end());
145
146 let function_decl = function_declaration(&func.source(ctx.db).value);
147 match ctx.config.snippet_cap {
148 Some(cap) => {
149 let snippet = format!("{} {{\n $0\n}}", function_decl);
150 builder.snippet_edit(cap, TextEdit::replace(range, snippet))
151 }
152 None => {
153 let header = format!("{} {{", function_decl);
154 builder.text_edit(TextEdit::replace(range, header))
155 }
156 }
157 .kind(completion_kind)
158 .add_to(acc);
159}
160
161fn add_type_alias_impl(
162 type_def_node: &SyntaxNode,
163 acc: &mut Completions,
164 ctx: &CompletionContext,
165 type_alias: hir::TypeAlias,
166) {
167 let alias_name = type_alias.name(ctx.db).to_string();
168
169 let snippet = format!("type {} = ", alias_name);
170
171 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end());
172
173 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
174 .text_edit(TextEdit::replace(range, snippet))
175 .lookup_by(alias_name)
176 .kind(CompletionItemKind::TypeAlias)
177 .set_documentation(type_alias.docs(ctx.db))
178 .add_to(acc);
179}
180
181fn add_const_impl(
182 const_def_node: &SyntaxNode,
183 acc: &mut Completions,
184 ctx: &CompletionContext,
185 const_: hir::Const,
186) {
187 let const_name = const_.name(ctx.db).map(|n| n.to_string());
188
189 if let Some(const_name) = const_name {
190 let snippet = make_const_compl_syntax(&const_.source(ctx.db).value);
191
192 let range = TextRange::new(const_def_node.text_range().start(), ctx.source_range().end());
193
194 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
195 .text_edit(TextEdit::replace(range, snippet))
196 .lookup_by(const_name)
197 .kind(CompletionItemKind::Const)
198 .set_documentation(const_.docs(ctx.db))
199 .add_to(acc);
200 }
201}
202
203fn make_const_compl_syntax(const_: &ast::Const) -> String {
204 let const_ = edit::remove_attrs_and_docs(const_);
205
206 let const_start = const_.syntax().text_range().start();
207 let const_end = const_.syntax().text_range().end();
208
209 let start =
210 const_.syntax().first_child_or_token().map_or(const_start, |f| f.text_range().start());
211
212 let end = const_
213 .syntax()
214 .children_with_tokens()
215 .find(|s| s.kind() == T![;] || s.kind() == T![=])
216 .map_or(const_end, |f| f.text_range().start());
217
218 let len = end - start;
219 let range = TextRange::new(0.into(), len);
220
221 let syntax = const_.syntax().text().slice(range).to_string();
222
223 format!("{} = ", syntax.trim_end())
224}
225
226#[cfg(test)]
227mod tests {
228 use expect::{expect, Expect};
229
230 use crate::completion::{
231 test_utils::{check_edit, completion_list},
232 CompletionKind,
233 };
234
235 fn check(ra_fixture: &str, expect: Expect) {
236 let actual = completion_list(ra_fixture, CompletionKind::Magic);
237 expect.assert_eq(&actual)
238 }
239
240 #[test]
241 fn name_ref_function_type_const() {
242 check(
243 r#"
244trait Test {
245 type TestType;
246 const TEST_CONST: u16;
247 fn test();
248}
249struct T;
250
251impl Test for T {
252 t<|>
253}
254"#,
255 expect![["
256ct const TEST_CONST: u16 = \n\
257fn fn test()
258ta type TestType = \n\
259 "]],
260 );
261 }
262
263 #[test]
264 fn no_nested_fn_completions() {
265 check(
266 r"
267trait Test {
268 fn test();
269 fn test2();
270}
271struct T;
272
273impl Test for T {
274 fn test() {
275 t<|>
276 }
277}
278",
279 expect![[""]],
280 );
281 }
282
283 #[test]
284 fn name_ref_single_function() {
285 check_edit(
286 "test",
287 r#"
288trait Test {
289 fn test();
290}
291struct T;
292
293impl Test for T {
294 t<|>
295}
296"#,
297 r#"
298trait Test {
299 fn test();
300}
301struct T;
302
303impl Test for T {
304 fn test() {
305 $0
306}
307}
308"#,
309 );
310 }
311
312 #[test]
313 fn single_function() {
314 check_edit(
315 "test",
316 r#"
317trait Test {
318 fn test();
319}
320struct T;
321
322impl Test for T {
323 fn t<|>
324}
325"#,
326 r#"
327trait Test {
328 fn test();
329}
330struct T;
331
332impl Test for T {
333 fn test() {
334 $0
335}
336}
337"#,
338 );
339 }
340
341 #[test]
342 fn hide_implemented_fn() {
343 check(
344 r#"
345trait Test {
346 fn foo();
347 fn foo_bar();
348}
349struct T;
350
351impl Test for T {
352 fn foo() {}
353 fn f<|>
354}
355"#,
356 expect![[r#"
357 fn fn foo_bar()
358 "#]],
359 );
360 }
361
362 #[test]
363 fn generic_fn() {
364 check_edit(
365 "foo",
366 r#"
367trait Test {
368 fn foo<T>();
369}
370struct T;
371
372impl Test for T {
373 fn f<|>
374}
375"#,
376 r#"
377trait Test {
378 fn foo<T>();
379}
380struct T;
381
382impl Test for T {
383 fn foo<T>() {
384 $0
385}
386}
387"#,
388 );
389 check_edit(
390 "foo",
391 r#"
392trait Test {
393 fn foo<T>() where T: Into<String>;
394}
395struct T;
396
397impl Test for T {
398 fn f<|>
399}
400"#,
401 r#"
402trait Test {
403 fn foo<T>() where T: Into<String>;
404}
405struct T;
406
407impl Test for T {
408 fn foo<T>()
409where T: Into<String> {
410 $0
411}
412}
413"#,
414 );
415 }
416
417 #[test]
418 fn associated_type() {
419 check_edit(
420 "SomeType",
421 r#"
422trait Test {
423 type SomeType;
424}
425
426impl Test for () {
427 type S<|>
428}
429"#,
430 "
431trait Test {
432 type SomeType;
433}
434
435impl Test for () {
436 type SomeType = \n\
437}
438",
439 );
440 }
441
442 #[test]
443 fn associated_const() {
444 check_edit(
445 "SOME_CONST",
446 r#"
447trait Test {
448 const SOME_CONST: u16;
449}
450
451impl Test for () {
452 const S<|>
453}
454"#,
455 "
456trait Test {
457 const SOME_CONST: u16;
458}
459
460impl Test for () {
461 const SOME_CONST: u16 = \n\
462}
463",
464 );
465
466 check_edit(
467 "SOME_CONST",
468 r#"
469trait Test {
470 const SOME_CONST: u16 = 92;
471}
472
473impl Test for () {
474 const S<|>
475}
476"#,
477 "
478trait Test {
479 const SOME_CONST: u16 = 92;
480}
481
482impl Test for () {
483 const SOME_CONST: u16 = \n\
484}
485",
486 );
487 }
488}
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
deleted file mode 100644
index bd9551f35..000000000
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ /dev/null
@@ -1,658 +0,0 @@
1//! Completion of names from the current scope, e.g. locals and imported items.
2
3use hir::{Adt, ModuleDef, ScopeDef, Type};
4use ra_syntax::AstNode;
5use test_utils::mark;
6
7use crate::completion::{CompletionContext, Completions};
8
9pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
10 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
11 return;
12 }
13 if ctx.record_lit_syntax.is_some()
14 || ctx.record_pat_syntax.is_some()
15 || ctx.attribute_under_caret.is_some()
16 {
17 return;
18 }
19
20 if let Some(ty) = &ctx.expected_type {
21 complete_enum_variants(acc, ctx, ty);
22 }
23
24 if ctx.is_pat_binding_or_const {
25 return;
26 }
27
28 ctx.scope.process_all_names(&mut |name, res| {
29 if ctx.use_item_syntax.is_some() {
30 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
31 if name_ref.syntax().text() == name.to_string().as_str() {
32 mark::hit!(self_fulfilling_completion);
33 return;
34 }
35 }
36 }
37 acc.add_resolution(ctx, name.to_string(), &res)
38 });
39}
40
41fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
42 if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
43 let variants = enum_data.variants(ctx.db);
44
45 let module = if let Some(module) = ctx.scope.module() {
46 // Compute path from the completion site if available.
47 module
48 } else {
49 // Otherwise fall back to the enum's definition site.
50 enum_data.module(ctx.db)
51 };
52
53 for variant in variants {
54 if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) {
55 // Variants with trivial paths are already added by the existing completion logic,
56 // so we should avoid adding these twice
57 if path.segments.len() > 1 {
58 acc.add_qualified_enum_variant(ctx, variant, path);
59 }
60 }
61 }
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use expect::{expect, Expect};
68 use test_utils::mark;
69
70 use crate::completion::{
71 test_utils::{check_edit, completion_list},
72 CompletionKind,
73 };
74
75 fn check(ra_fixture: &str, expect: Expect) {
76 let actual = completion_list(ra_fixture, CompletionKind::Reference);
77 expect.assert_eq(&actual)
78 }
79
80 #[test]
81 fn self_fulfilling_completion() {
82 mark::check!(self_fulfilling_completion);
83 check(
84 r#"
85use foo<|>
86use std::collections;
87"#,
88 expect![[r#"
89 ?? collections
90 "#]],
91 );
92 }
93
94 #[test]
95 fn bind_pat_and_path_ignore_at() {
96 check(
97 r#"
98enum Enum { A, B }
99fn quux(x: Option<Enum>) {
100 match x {
101 None => (),
102 Some(en<|> @ Enum::A) => (),
103 }
104}
105"#,
106 expect![[""]],
107 );
108 }
109
110 #[test]
111 fn bind_pat_and_path_ignore_ref() {
112 check(
113 r#"
114enum Enum { A, B }
115fn quux(x: Option<Enum>) {
116 match x {
117 None => (),
118 Some(ref en<|>) => (),
119 }
120}
121"#,
122 expect![[""]],
123 );
124 }
125
126 #[test]
127 fn bind_pat_and_path() {
128 check(
129 r#"
130enum Enum { A, B }
131fn quux(x: Option<Enum>) {
132 match x {
133 None => (),
134 Some(En<|>) => (),
135 }
136}
137"#,
138 expect![[r#"
139 en Enum
140 "#]],
141 );
142 }
143
144 #[test]
145 fn completes_bindings_from_let() {
146 check(
147 r#"
148fn quux(x: i32) {
149 let y = 92;
150 1 + <|>;
151 let z = ();
152}
153"#,
154 expect![[r#"
155 fn quux(…) fn quux(x: i32)
156 bn x i32
157 bn y i32
158 "#]],
159 );
160 }
161
162 #[test]
163 fn completes_bindings_from_if_let() {
164 check(
165 r#"
166fn quux() {
167 if let Some(x) = foo() {
168 let y = 92;
169 };
170 if let Some(a) = bar() {
171 let b = 62;
172 1 + <|>
173 }
174}
175"#,
176 expect![[r#"
177 bn a
178 bn b i32
179 fn quux() fn quux()
180 "#]],
181 );
182 }
183
184 #[test]
185 fn completes_bindings_from_for() {
186 check(
187 r#"
188fn quux() {
189 for x in &[1, 2, 3] { <|> }
190}
191"#,
192 expect![[r#"
193 fn quux() fn quux()
194 bn x
195 "#]],
196 );
197 }
198
199 #[test]
200 fn completes_if_prefix_is_keyword() {
201 mark::check!(completes_if_prefix_is_keyword);
202 check_edit(
203 "wherewolf",
204 r#"
205fn main() {
206 let wherewolf = 92;
207 drop(where<|>)
208}
209"#,
210 r#"
211fn main() {
212 let wherewolf = 92;
213 drop(wherewolf)
214}
215"#,
216 )
217 }
218
219 #[test]
220 fn completes_generic_params() {
221 check(
222 r#"fn quux<T>() { <|> }"#,
223 expect![[r#"
224 tp T
225 fn quux() fn quux<T>()
226 "#]],
227 );
228 }
229
230 #[test]
231 fn completes_generic_params_in_struct() {
232 check(
233 r#"struct S<T> { x: <|>}"#,
234 expect![[r#"
235 st S<…>
236 tp Self
237 tp T
238 "#]],
239 );
240 }
241
242 #[test]
243 fn completes_self_in_enum() {
244 check(
245 r#"enum X { Y(<|>) }"#,
246 expect![[r#"
247 tp Self
248 en X
249 "#]],
250 );
251 }
252
253 #[test]
254 fn completes_module_items() {
255 check(
256 r#"
257struct S;
258enum E {}
259fn quux() { <|> }
260"#,
261 expect![[r#"
262 en E
263 st S
264 fn quux() fn quux()
265 "#]],
266 );
267 }
268
269 #[test]
270 fn completes_extern_prelude() {
271 check(
272 r#"
273//- /lib.rs
274use <|>;
275
276//- /other_crate/lib.rs
277// nothing here
278"#,
279 expect![[r#"
280 md other_crate
281 "#]],
282 );
283 }
284
285 #[test]
286 fn completes_module_items_in_nested_modules() {
287 check(
288 r#"
289struct Foo;
290mod m {
291 struct Bar;
292 fn quux() { <|> }
293}
294"#,
295 expect![[r#"
296 st Bar
297 fn quux() fn quux()
298 "#]],
299 );
300 }
301
302 #[test]
303 fn completes_return_type() {
304 check(
305 r#"
306struct Foo;
307fn x() -> <|>
308"#,
309 expect![[r#"
310 st Foo
311 fn x() fn x()
312 "#]],
313 );
314 }
315
316 #[test]
317 fn dont_show_both_completions_for_shadowing() {
318 check(
319 r#"
320fn foo() {
321 let bar = 92;
322 {
323 let bar = 62;
324 drop(<|>)
325 }
326}
327"#,
328 // FIXME: should be only one bar here
329 expect![[r#"
330 bn bar i32
331 bn bar i32
332 fn foo() fn foo()
333 "#]],
334 );
335 }
336
337 #[test]
338 fn completes_self_in_methods() {
339 check(
340 r#"impl S { fn foo(&self) { <|> } }"#,
341 expect![[r#"
342 tp Self
343 bn self &{unknown}
344 "#]],
345 );
346 }
347
348 #[test]
349 fn completes_prelude() {
350 check(
351 r#"
352//- /main.rs
353fn foo() { let x: <|> }
354
355//- /std/lib.rs
356#[prelude_import]
357use prelude::*;
358
359mod prelude { struct Option; }
360"#,
361 expect![[r#"
362 st Option
363 fn foo() fn foo()
364 md std
365 "#]],
366 );
367 }
368
369 #[test]
370 fn completes_std_prelude_if_core_is_defined() {
371 check(
372 r#"
373//- /main.rs
374fn foo() { let x: <|> }
375
376//- /core/lib.rs
377#[prelude_import]
378use prelude::*;
379
380mod prelude { struct Option; }
381
382//- /std/lib.rs
383#[prelude_import]
384use prelude::*;
385
386mod prelude { struct String; }
387"#,
388 expect![[r#"
389 st String
390 md core
391 fn foo() fn foo()
392 md std
393 "#]],
394 );
395 }
396
397 #[test]
398 fn completes_macros_as_value() {
399 check(
400 r#"
401macro_rules! foo { () => {} }
402
403#[macro_use]
404mod m1 {
405 macro_rules! bar { () => {} }
406}
407
408mod m2 {
409 macro_rules! nope { () => {} }
410
411 #[macro_export]
412 macro_rules! baz { () => {} }
413}
414
415fn main() { let v = <|> }
416"#,
417 expect![[r##"
418 ma bar!(…) macro_rules! bar
419 ma baz!(…) #[macro_export]
420 macro_rules! baz
421 ma foo!(…) macro_rules! foo
422 md m1
423 md m2
424 fn main() fn main()
425 "##]],
426 );
427 }
428
429 #[test]
430 fn completes_both_macro_and_value() {
431 check(
432 r#"
433macro_rules! foo { () => {} }
434fn foo() { <|> }
435"#,
436 expect![[r#"
437 ma foo!(…) macro_rules! foo
438 fn foo() fn foo()
439 "#]],
440 );
441 }
442
443 #[test]
444 fn completes_macros_as_type() {
445 check(
446 r#"
447macro_rules! foo { () => {} }
448fn main() { let x: <|> }
449"#,
450 expect![[r#"
451 ma foo!(…) macro_rules! foo
452 fn main() fn main()
453 "#]],
454 );
455 }
456
457 #[test]
458 fn completes_macros_as_stmt() {
459 check(
460 r#"
461macro_rules! foo { () => {} }
462fn main() { <|> }
463"#,
464 expect![[r#"
465 ma foo!(…) macro_rules! foo
466 fn main() fn main()
467 "#]],
468 );
469 }
470
471 #[test]
472 fn completes_local_item() {
473 check(
474 r#"
475fn main() {
476 return f<|>;
477 fn frobnicate() {}
478}
479"#,
480 expect![[r#"
481 fn frobnicate() fn frobnicate()
482 fn main() fn main()
483 "#]],
484 );
485 }
486
487 #[test]
488 fn completes_in_simple_macro_1() {
489 check(
490 r#"
491macro_rules! m { ($e:expr) => { $e } }
492fn quux(x: i32) {
493 let y = 92;
494 m!(<|>);
495}
496"#,
497 expect![[r#"
498 ma m!(…) macro_rules! m
499 fn quux(…) fn quux(x: i32)
500 bn x i32
501 bn y i32
502 "#]],
503 );
504 }
505
506 #[test]
507 fn completes_in_simple_macro_2() {
508 check(
509 r"
510macro_rules! m { ($e:expr) => { $e } }
511fn quux(x: i32) {
512 let y = 92;
513 m!(x<|>);
514}
515",
516 expect![[r#"
517 ma m!(…) macro_rules! m
518 fn quux(…) fn quux(x: i32)
519 bn x i32
520 bn y i32
521 "#]],
522 );
523 }
524
525 #[test]
526 fn completes_in_simple_macro_without_closing_parens() {
527 check(
528 r#"
529macro_rules! m { ($e:expr) => { $e } }
530fn quux(x: i32) {
531 let y = 92;
532 m!(x<|>
533}
534"#,
535 expect![[r#"
536 ma m!(…) macro_rules! m
537 fn quux(…) fn quux(x: i32)
538 bn x i32
539 bn y i32
540 "#]],
541 );
542 }
543
544 #[test]
545 fn completes_unresolved_uses() {
546 check(
547 r#"
548use spam::Quux;
549
550fn main() { <|> }
551"#,
552 expect![[r#"
553 ?? Quux
554 fn main() fn main()
555 "#]],
556 );
557 }
558 #[test]
559 fn completes_enum_variant_matcharm() {
560 check(
561 r#"
562enum Foo { Bar, Baz, Quux }
563
564fn main() {
565 let foo = Foo::Quux;
566 match foo { Qu<|> }
567}
568"#,
569 expect![[r#"
570 en Foo
571 ev Foo::Bar ()
572 ev Foo::Baz ()
573 ev Foo::Quux ()
574 "#]],
575 )
576 }
577
578 #[test]
579 fn completes_enum_variant_iflet() {
580 check(
581 r#"
582enum Foo { Bar, Baz, Quux }
583
584fn main() {
585 let foo = Foo::Quux;
586 if let Qu<|> = foo { }
587}
588"#,
589 expect![[r#"
590 en Foo
591 ev Foo::Bar ()
592 ev Foo::Baz ()
593 ev Foo::Quux ()
594 "#]],
595 )
596 }
597
598 #[test]
599 fn completes_enum_variant_basic_expr() {
600 check(
601 r#"
602enum Foo { Bar, Baz, Quux }
603fn main() { let foo: Foo = Q<|> }
604"#,
605 expect![[r#"
606 en Foo
607 ev Foo::Bar ()
608 ev Foo::Baz ()
609 ev Foo::Quux ()
610 fn main() fn main()
611 "#]],
612 )
613 }
614
615 #[test]
616 fn completes_enum_variant_from_module() {
617 check(
618 r#"
619mod m { pub enum E { V } }
620fn f() -> m::E { V<|> }
621"#,
622 expect![[r#"
623 fn f() fn f() -> m::E
624 md m
625 ev m::E::V ()
626 "#]],
627 )
628 }
629
630 #[test]
631 fn dont_complete_attr() {
632 check(
633 r#"
634struct Foo;
635#[<|>]
636fn f() {}
637"#,
638 expect![[""]],
639 )
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 "#]],
656 )
657 }
658}
diff --git a/crates/ra_ide/src/completion/completion_config.rs b/crates/ra_ide/src/completion/completion_config.rs
deleted file mode 100644
index 71b49ace8..000000000
--- a/crates/ra_ide/src/completion/completion_config.rs
+++ /dev/null
@@ -1,35 +0,0 @@
1//! Settings for tweaking completion.
2//!
3//! The fun thing here is `SnippetCap` -- this type can only be created in this
4//! module, and we use to statically check that we only produce snippet
5//! completions if we are allowed to.
6
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct CompletionConfig {
9 pub enable_postfix_completions: bool,
10 pub add_call_parenthesis: bool,
11 pub add_call_argument_snippets: bool,
12 pub snippet_cap: Option<SnippetCap>,
13}
14
15impl CompletionConfig {
16 pub fn allow_snippets(&mut self, yes: bool) {
17 self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None }
18 }
19}
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub struct SnippetCap {
23 _private: (),
24}
25
26impl Default for CompletionConfig {
27 fn default() -> Self {
28 CompletionConfig {
29 enable_postfix_completions: true,
30 add_call_parenthesis: true,
31 add_call_argument_snippets: true,
32 snippet_cap: Some(SnippetCap { _private: () }),
33 }
34 }
35}
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
deleted file mode 100644
index 2113abbb2..000000000
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ /dev/null
@@ -1,465 +0,0 @@
1//! FIXME: write short doc here
2
3use hir::{Semantics, SemanticsScope, Type};
4use ra_db::SourceDatabase;
5use ra_ide_db::RootDatabase;
6use ra_syntax::{
7 algo::{find_covering_element, find_node_at_offset},
8 ast, match_ast, AstNode, NodeOrToken,
9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextSize,
11};
12use ra_text_edit::Indel;
13
14use super::patterns::{
15 has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, has_impl_parent,
16 has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling,
17 has_trait_parent, if_is_prev, is_in_loop_body, is_match_arm, unsafe_is_prev,
18};
19use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition};
20use test_utils::mark;
21
22/// `CompletionContext` is created early during completion to figure out, where
23/// exactly is the cursor, syntax-wise.
24#[derive(Debug)]
25pub(crate) struct CompletionContext<'a> {
26 pub(super) sema: Semantics<'a, RootDatabase>,
27 pub(super) scope: SemanticsScope<'a>,
28 pub(super) db: &'a RootDatabase,
29 pub(super) config: &'a CompletionConfig,
30 pub(super) offset: TextSize,
31 /// The token before the cursor, in the original file.
32 pub(super) original_token: SyntaxToken,
33 /// The token before the cursor, in the macro-expanded file.
34 pub(super) token: SyntaxToken,
35 pub(super) krate: Option<hir::Crate>,
36 pub(super) expected_type: Option<Type>,
37 pub(super) name_ref_syntax: Option<ast::NameRef>,
38 pub(super) function_syntax: Option<ast::Fn>,
39 pub(super) use_item_syntax: Option<ast::Use>,
40 pub(super) record_lit_syntax: Option<ast::RecordExpr>,
41 pub(super) record_pat_syntax: Option<ast::RecordPat>,
42 pub(super) record_field_syntax: Option<ast::RecordExprField>,
43 pub(super) impl_def: Option<ast::Impl>,
44 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
45 pub(super) active_parameter: Option<ActiveParameter>,
46 pub(super) is_param: bool,
47 /// If a name-binding or reference to a const in a pattern.
48 /// Irrefutable patterns (like let) are excluded.
49 pub(super) is_pat_binding_or_const: bool,
50 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
51 pub(super) is_trivial_path: bool,
52 /// If not a trivial path, the prefix (qualifier).
53 pub(super) path_prefix: Option<hir::Path>,
54 pub(super) after_if: bool,
55 /// `true` if we are a statement or a last expr in the block.
56 pub(super) can_be_stmt: bool,
57 /// `true` if we expect an expression at the cursor position.
58 pub(super) is_expr: bool,
59 /// Something is typed at the "top" level, in module or impl/trait.
60 pub(super) is_new_item: bool,
61 /// The receiver if this is a field or method access, i.e. writing something.<|>
62 pub(super) dot_receiver: Option<ast::Expr>,
63 pub(super) dot_receiver_is_ambiguous_float_literal: bool,
64 /// If this is a call (method or function) in particular, i.e. the () are already there.
65 pub(super) is_call: bool,
66 /// Like `is_call`, but for tuple patterns.
67 pub(super) is_pattern_call: bool,
68 /// If this is a macro call, i.e. the () are already there.
69 pub(super) is_macro_call: bool,
70 pub(super) is_path_type: bool,
71 pub(super) has_type_args: bool,
72 pub(super) attribute_under_caret: Option<ast::Attr>,
73 pub(super) unsafe_is_prev: bool,
74 pub(super) if_is_prev: bool,
75 pub(super) block_expr_parent: bool,
76 pub(super) bind_pat_parent: bool,
77 pub(super) ref_pat_parent: bool,
78 pub(super) in_loop_body: bool,
79 pub(super) has_trait_parent: bool,
80 pub(super) has_impl_parent: bool,
81 pub(super) trait_as_prev_sibling: bool,
82 pub(super) impl_as_prev_sibling: bool,
83 pub(super) is_match_arm: bool,
84 pub(super) has_item_list_or_source_file_parent: bool,
85}
86
87impl<'a> CompletionContext<'a> {
88 pub(super) fn new(
89 db: &'a RootDatabase,
90 position: FilePosition,
91 config: &'a CompletionConfig,
92 ) -> Option<CompletionContext<'a>> {
93 let sema = Semantics::new(db);
94
95 let original_file = sema.parse(position.file_id);
96
97 // Insert a fake ident to get a valid parse tree. We will use this file
98 // to determine context, though the original_file will be used for
99 // actual completion.
100 let file_with_fake_ident = {
101 let parse = db.parse(position.file_id);
102 let edit = Indel::insert(position.offset, "intellijRulezz".to_string());
103 parse.reparse(&edit).tree()
104 };
105 let fake_ident_token =
106 file_with_fake_ident.syntax().token_at_offset(position.offset).right_biased().unwrap();
107
108 let krate = sema.to_module_def(position.file_id).map(|m| m.krate());
109 let original_token =
110 original_file.syntax().token_at_offset(position.offset).left_biased()?;
111 let token = sema.descend_into_macros(original_token.clone());
112 let scope = sema.scope_at_offset(&token.parent(), position.offset);
113 let mut ctx = CompletionContext {
114 sema,
115 scope,
116 db,
117 config,
118 original_token,
119 token,
120 offset: position.offset,
121 krate,
122 expected_type: None,
123 name_ref_syntax: None,
124 function_syntax: None,
125 use_item_syntax: None,
126 record_lit_syntax: None,
127 record_pat_syntax: None,
128 record_field_syntax: None,
129 impl_def: None,
130 active_parameter: ActiveParameter::at(db, position),
131 is_param: false,
132 is_pat_binding_or_const: false,
133 is_trivial_path: false,
134 path_prefix: None,
135 after_if: false,
136 can_be_stmt: false,
137 is_expr: false,
138 is_new_item: false,
139 dot_receiver: None,
140 is_call: false,
141 is_pattern_call: false,
142 is_macro_call: false,
143 is_path_type: false,
144 has_type_args: false,
145 dot_receiver_is_ambiguous_float_literal: false,
146 attribute_under_caret: None,
147 unsafe_is_prev: false,
148 in_loop_body: false,
149 ref_pat_parent: false,
150 bind_pat_parent: false,
151 block_expr_parent: false,
152 has_trait_parent: false,
153 has_impl_parent: false,
154 trait_as_prev_sibling: false,
155 impl_as_prev_sibling: false,
156 if_is_prev: false,
157 is_match_arm: false,
158 has_item_list_or_source_file_parent: false,
159 };
160
161 let mut original_file = original_file.syntax().clone();
162 let mut hypothetical_file = file_with_fake_ident.syntax().clone();
163 let mut offset = position.offset;
164 let mut fake_ident_token = fake_ident_token;
165
166 // Are we inside a macro call?
167 while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
168 find_node_at_offset::<ast::MacroCall>(&original_file, offset),
169 find_node_at_offset::<ast::MacroCall>(&hypothetical_file, offset),
170 ) {
171 if actual_macro_call.path().as_ref().map(|s| s.syntax().text())
172 != macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text())
173 {
174 break;
175 }
176 let hypothetical_args = match macro_call_with_fake_ident.token_tree() {
177 Some(tt) => tt,
178 None => break,
179 };
180 if let (Some(actual_expansion), Some(hypothetical_expansion)) = (
181 ctx.sema.expand(&actual_macro_call),
182 ctx.sema.expand_hypothetical(
183 &actual_macro_call,
184 &hypothetical_args,
185 fake_ident_token,
186 ),
187 ) {
188 let new_offset = hypothetical_expansion.1.text_range().start();
189 if new_offset > actual_expansion.text_range().end() {
190 break;
191 }
192 original_file = actual_expansion;
193 hypothetical_file = hypothetical_expansion.0;
194 fake_ident_token = hypothetical_expansion.1;
195 offset = new_offset;
196 } else {
197 break;
198 }
199 }
200 ctx.fill_keyword_patterns(&hypothetical_file, offset);
201 ctx.fill(&original_file, hypothetical_file, offset);
202 Some(ctx)
203 }
204
205 // The range of the identifier that is being completed.
206 pub(crate) fn source_range(&self) -> TextRange {
207 // check kind of macro-expanded token, but use range of original token
208 if self.token.kind() == IDENT || self.token.kind().is_keyword() {
209 mark::hit!(completes_if_prefix_is_keyword);
210 self.original_token.text_range()
211 } else {
212 TextRange::empty(self.offset)
213 }
214 }
215
216 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
217 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
218 let syntax_element = NodeOrToken::Token(fake_ident_token);
219 self.block_expr_parent = has_block_expr_parent(syntax_element.clone());
220 self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone());
221 self.if_is_prev = if_is_prev(syntax_element.clone());
222 self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone());
223 self.ref_pat_parent = has_ref_parent(syntax_element.clone());
224 self.in_loop_body = is_in_loop_body(syntax_element.clone());
225 self.has_trait_parent = has_trait_parent(syntax_element.clone());
226 self.has_impl_parent = has_impl_parent(syntax_element.clone());
227 self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone());
228 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone());
229 self.is_match_arm = is_match_arm(syntax_element.clone());
230 self.has_item_list_or_source_file_parent =
231 has_item_list_or_source_file_parent(syntax_element);
232 }
233
234 fn fill(
235 &mut self,
236 original_file: &SyntaxNode,
237 file_with_fake_ident: SyntaxNode,
238 offset: TextSize,
239 ) {
240 // FIXME: this is wrong in at least two cases:
241 // * when there's no token `foo(<|>)`
242 // * when there is a token, but it happens to have type of it's own
243 self.expected_type = self
244 .token
245 .ancestors()
246 .find_map(|node| {
247 let ty = match_ast! {
248 match node {
249 ast::Pat(it) => self.sema.type_of_pat(&it),
250 ast::Expr(it) => self.sema.type_of_expr(&it),
251 _ => return None,
252 }
253 };
254 Some(ty)
255 })
256 .flatten();
257 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
258
259 // First, let's try to complete a reference to some declaration.
260 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
261 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
262 // See RFC#1685.
263 if is_node::<ast::Param>(name_ref.syntax()) {
264 self.is_param = true;
265 return;
266 }
267 // FIXME: remove this (V) duplication and make the check more precise
268 if name_ref.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() {
269 self.record_pat_syntax =
270 self.sema.find_node_at_offset_with_macros(&original_file, offset);
271 }
272 self.classify_name_ref(original_file, name_ref, offset);
273 }
274
275 // Otherwise, see if this is a declaration. We can use heuristics to
276 // suggest declaration names, see `CompletionKind::Magic`.
277 if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) {
278 if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) {
279 self.is_pat_binding_or_const = true;
280 if bind_pat.at_token().is_some()
281 || bind_pat.ref_token().is_some()
282 || bind_pat.mut_token().is_some()
283 {
284 self.is_pat_binding_or_const = false;
285 }
286 if bind_pat.syntax().parent().and_then(ast::RecordFieldPatList::cast).is_some() {
287 self.is_pat_binding_or_const = false;
288 }
289 if let Some(let_stmt) = bind_pat.syntax().ancestors().find_map(ast::LetStmt::cast) {
290 if let Some(pat) = let_stmt.pat() {
291 if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range())
292 {
293 self.is_pat_binding_or_const = false;
294 }
295 }
296 }
297 }
298 if is_node::<ast::Param>(name.syntax()) {
299 self.is_param = true;
300 return;
301 }
302 // FIXME: remove this (^) duplication and make the check more precise
303 if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() {
304 self.record_pat_syntax =
305 self.sema.find_node_at_offset_with_macros(&original_file, offset);
306 }
307 }
308 }
309
310 fn classify_name_ref(
311 &mut self,
312 original_file: &SyntaxNode,
313 name_ref: ast::NameRef,
314 offset: TextSize,
315 ) {
316 self.name_ref_syntax =
317 find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
318 let name_range = name_ref.syntax().text_range();
319 if ast::RecordExprField::for_field_name(&name_ref).is_some() {
320 self.record_lit_syntax =
321 self.sema.find_node_at_offset_with_macros(&original_file, offset);
322 }
323
324 self.impl_def = self
325 .sema
326 .ancestors_with_macros(self.token.parent())
327 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
328 .find_map(ast::Impl::cast);
329
330 let top_node = name_ref
331 .syntax()
332 .ancestors()
333 .take_while(|it| it.text_range() == name_range)
334 .last()
335 .unwrap();
336
337 match top_node.parent().map(|it| it.kind()) {
338 Some(SOURCE_FILE) | Some(ITEM_LIST) => {
339 self.is_new_item = true;
340 return;
341 }
342 _ => (),
343 }
344
345 self.use_item_syntax =
346 self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::Use::cast);
347
348 self.function_syntax = self
349 .sema
350 .ancestors_with_macros(self.token.parent())
351 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
352 .find_map(ast::Fn::cast);
353
354 self.record_field_syntax = self
355 .sema
356 .ancestors_with_macros(self.token.parent())
357 .take_while(|it| {
358 it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR
359 })
360 .find_map(ast::RecordExprField::cast);
361
362 let parent = match name_ref.syntax().parent() {
363 Some(it) => it,
364 None => return,
365 };
366
367 if let Some(segment) = ast::PathSegment::cast(parent.clone()) {
368 let path = segment.parent_path();
369 self.is_call = path
370 .syntax()
371 .parent()
372 .and_then(ast::PathExpr::cast)
373 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
374 .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();
378
379 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
380 self.has_type_args = segment.type_arg_list().is_some();
381
382 #[allow(deprecated)]
383 if let Some(path) = hir::Path::from_ast(path.clone()) {
384 if let Some(path_prefix) = path.qualifier() {
385 self.path_prefix = Some(path_prefix);
386 return;
387 }
388 }
389
390 if path.qualifier().is_none() {
391 self.is_trivial_path = true;
392
393 // Find either enclosing expr statement (thing with `;`) or a
394 // block. If block, check that we are the last expr.
395 self.can_be_stmt = name_ref
396 .syntax()
397 .ancestors()
398 .find_map(|node| {
399 if let Some(stmt) = ast::ExprStmt::cast(node.clone()) {
400 return Some(
401 stmt.syntax().text_range() == name_ref.syntax().text_range(),
402 );
403 }
404 if let Some(block) = ast::BlockExpr::cast(node) {
405 return Some(
406 block.expr().map(|e| e.syntax().text_range())
407 == Some(name_ref.syntax().text_range()),
408 );
409 }
410 None
411 })
412 .unwrap_or(false);
413 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
414
415 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
416 if let Some(if_expr) =
417 self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off)
418 {
419 if if_expr.syntax().text_range().end()
420 < name_ref.syntax().text_range().start()
421 {
422 self.after_if = true;
423 }
424 }
425 }
426 }
427 }
428 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
429 // The receiver comes before the point of insertion of the fake
430 // ident, so it should have the same range in the non-modified file
431 self.dot_receiver = field_expr
432 .expr()
433 .map(|e| e.syntax().text_range())
434 .and_then(|r| find_node_with_range(original_file, r));
435 self.dot_receiver_is_ambiguous_float_literal =
436 if let Some(ast::Expr::Literal(l)) = &self.dot_receiver {
437 match l.kind() {
438 ast::LiteralKind::FloatNumber { .. } => l.token().text().ends_with('.'),
439 _ => false,
440 }
441 } else {
442 false
443 }
444 }
445 if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) {
446 // As above
447 self.dot_receiver = method_call_expr
448 .expr()
449 .map(|e| e.syntax().text_range())
450 .and_then(|r| find_node_with_range(original_file, r));
451 self.is_call = true;
452 }
453 }
454}
455
456fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
457 find_covering_element(syntax, range).ancestors().find_map(N::cast)
458}
459
460fn is_node<N: AstNode>(node: &SyntaxNode) -> bool {
461 match node.ancestors().find_map(N::cast) {
462 None => false,
463 Some(n) => n.syntax().text_range() == node.text_range(),
464 }
465}
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
deleted file mode 100644
index 7bdda316c..000000000
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ /dev/null
@@ -1,384 +0,0 @@
1//! FIXME: write short doc here
2
3use std::fmt;
4
5use hir::Documentation;
6use ra_syntax::TextRange;
7use ra_text_edit::TextEdit;
8
9use crate::completion::completion_config::SnippetCap;
10
11/// `CompletionItem` describes a single completion variant in the editor pop-up.
12/// It is basically a POD with various properties. To construct a
13/// `CompletionItem`, use `new` method and the `Builder` struct.
14pub struct CompletionItem {
15 /// Used only internally in tests, to check only specific kind of
16 /// completion (postfix, keyword, reference, etc).
17 #[allow(unused)]
18 pub(crate) completion_kind: CompletionKind,
19 /// Label in the completion pop up which identifies completion.
20 label: String,
21 /// Range of identifier that is being completed.
22 ///
23 /// It should be used primarily for UI, but we also use this to convert
24 /// genetic TextEdit into LSP's completion edit (see conv.rs).
25 ///
26 /// `source_range` must contain the completion offset. `insert_text` should
27 /// start with what `source_range` points to, or VSCode will filter out the
28 /// completion silently.
29 source_range: TextRange,
30 /// What happens when user selects this item.
31 ///
32 /// Typically, replaces `source_range` with new identifier.
33 text_edit: TextEdit,
34 insert_text_format: InsertTextFormat,
35
36 /// What item (struct, function, etc) are we completing.
37 kind: Option<CompletionItemKind>,
38
39 /// Lookup is used to check if completion item indeed can complete current
40 /// ident.
41 ///
42 /// That is, in `foo.bar<|>` lookup of `abracadabra` will be accepted (it
43 /// contains `bar` sub sequence), and `quux` will rejected.
44 lookup: Option<String>,
45
46 /// Additional info to show in the UI pop up.
47 detail: Option<String>,
48 documentation: Option<Documentation>,
49
50 /// Whether this item is marked as deprecated
51 deprecated: bool,
52
53 /// If completing a function call, ask the editor to show parameter popup
54 /// after completion.
55 trigger_call_info: bool,
56
57 /// Score is useful to pre select or display in better order completion items
58 score: Option<CompletionScore>,
59}
60
61// We use custom debug for CompletionItem to make snapshot tests more readable.
62impl fmt::Debug for CompletionItem {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 let mut s = f.debug_struct("CompletionItem");
65 s.field("label", &self.label()).field("source_range", &self.source_range());
66 if self.text_edit().len() == 1 {
67 let atom = &self.text_edit().iter().next().unwrap();
68 s.field("delete", &atom.delete);
69 s.field("insert", &atom.insert);
70 } else {
71 s.field("text_edit", &self.text_edit);
72 }
73 if let Some(kind) = self.kind().as_ref() {
74 s.field("kind", kind);
75 }
76 if self.lookup() != self.label() {
77 s.field("lookup", &self.lookup());
78 }
79 if let Some(detail) = self.detail() {
80 s.field("detail", &detail);
81 }
82 if let Some(documentation) = self.documentation() {
83 s.field("documentation", &documentation);
84 }
85 if self.deprecated {
86 s.field("deprecated", &true);
87 }
88 if let Some(score) = &self.score {
89 s.field("score", score);
90 }
91 if self.trigger_call_info {
92 s.field("trigger_call_info", &true);
93 }
94 s.finish()
95 }
96}
97
98#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
99pub enum CompletionScore {
100 /// If only type match
101 TypeMatch,
102 /// If type and name match
103 TypeAndNameMatch,
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub enum CompletionItemKind {
108 Snippet,
109 Keyword,
110 Module,
111 Function,
112 BuiltinType,
113 Struct,
114 Enum,
115 EnumVariant,
116 Binding,
117 Field,
118 Static,
119 Const,
120 Trait,
121 TypeAlias,
122 Method,
123 TypeParam,
124 Macro,
125 Attribute,
126 UnresolvedReference,
127}
128
129impl CompletionItemKind {
130 #[cfg(test)]
131 pub(crate) fn tag(&self) -> &'static str {
132 match self {
133 CompletionItemKind::Attribute => "at",
134 CompletionItemKind::Binding => "bn",
135 CompletionItemKind::BuiltinType => "bt",
136 CompletionItemKind::Const => "ct",
137 CompletionItemKind::Enum => "en",
138 CompletionItemKind::EnumVariant => "ev",
139 CompletionItemKind::Field => "fd",
140 CompletionItemKind::Function => "fn",
141 CompletionItemKind::Keyword => "kw",
142 CompletionItemKind::Macro => "ma",
143 CompletionItemKind::Method => "me",
144 CompletionItemKind::Module => "md",
145 CompletionItemKind::Snippet => "sn",
146 CompletionItemKind::Static => "sc",
147 CompletionItemKind::Struct => "st",
148 CompletionItemKind::Trait => "tt",
149 CompletionItemKind::TypeAlias => "ta",
150 CompletionItemKind::TypeParam => "tp",
151 CompletionItemKind::UnresolvedReference => "??",
152 }
153 }
154}
155
156#[derive(Debug, PartialEq, Eq, Copy, Clone)]
157pub(crate) enum CompletionKind {
158 /// Parser-based keyword completion.
159 Keyword,
160 /// Your usual "complete all valid identifiers".
161 Reference,
162 /// "Secret sauce" completions.
163 Magic,
164 Snippet,
165 Postfix,
166 BuiltinType,
167 Attribute,
168}
169
170#[derive(Debug, PartialEq, Eq, Copy, Clone)]
171pub enum InsertTextFormat {
172 PlainText,
173 Snippet,
174}
175
176impl CompletionItem {
177 pub(crate) fn new(
178 completion_kind: CompletionKind,
179 source_range: TextRange,
180 label: impl Into<String>,
181 ) -> Builder {
182 let label = label.into();
183 Builder {
184 source_range,
185 completion_kind,
186 label,
187 insert_text: None,
188 insert_text_format: InsertTextFormat::PlainText,
189 detail: None,
190 documentation: None,
191 lookup: None,
192 kind: None,
193 text_edit: None,
194 deprecated: None,
195 trigger_call_info: None,
196 score: None,
197 }
198 }
199 /// What user sees in pop-up in the UI.
200 pub fn label(&self) -> &str {
201 &self.label
202 }
203 pub fn source_range(&self) -> TextRange {
204 self.source_range
205 }
206
207 pub fn insert_text_format(&self) -> InsertTextFormat {
208 self.insert_text_format
209 }
210
211 pub fn text_edit(&self) -> &TextEdit {
212 &self.text_edit
213 }
214
215 /// Short one-line additional information, like a type
216 pub fn detail(&self) -> Option<&str> {
217 self.detail.as_deref()
218 }
219 /// A doc-comment
220 pub fn documentation(&self) -> Option<Documentation> {
221 self.documentation.clone()
222 }
223 /// What string is used for filtering.
224 pub fn lookup(&self) -> &str {
225 self.lookup.as_deref().unwrap_or(&self.label)
226 }
227
228 pub fn kind(&self) -> Option<CompletionItemKind> {
229 self.kind
230 }
231
232 pub fn deprecated(&self) -> bool {
233 self.deprecated
234 }
235
236 pub fn score(&self) -> Option<CompletionScore> {
237 self.score
238 }
239
240 pub fn trigger_call_info(&self) -> bool {
241 self.trigger_call_info
242 }
243}
244
245/// A helper to make `CompletionItem`s.
246#[must_use]
247pub(crate) struct Builder {
248 source_range: TextRange,
249 completion_kind: CompletionKind,
250 label: String,
251 insert_text: Option<String>,
252 insert_text_format: InsertTextFormat,
253 detail: Option<String>,
254 documentation: Option<Documentation>,
255 lookup: Option<String>,
256 kind: Option<CompletionItemKind>,
257 text_edit: Option<TextEdit>,
258 deprecated: Option<bool>,
259 trigger_call_info: Option<bool>,
260 score: Option<CompletionScore>,
261}
262
263impl Builder {
264 pub(crate) fn add_to(self, acc: &mut Completions) {
265 acc.add(self.build())
266 }
267
268 pub(crate) fn build(self) -> CompletionItem {
269 let label = self.label;
270 let text_edit = match self.text_edit {
271 Some(it) => it,
272 None => TextEdit::replace(
273 self.source_range,
274 self.insert_text.unwrap_or_else(|| label.clone()),
275 ),
276 };
277
278 CompletionItem {
279 source_range: self.source_range,
280 label,
281 insert_text_format: self.insert_text_format,
282 text_edit,
283 detail: self.detail,
284 documentation: self.documentation,
285 lookup: self.lookup,
286 kind: self.kind,
287 completion_kind: self.completion_kind,
288 deprecated: self.deprecated.unwrap_or(false),
289 trigger_call_info: self.trigger_call_info.unwrap_or(false),
290 score: self.score,
291 }
292 }
293 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
294 self.lookup = Some(lookup.into());
295 self
296 }
297 pub(crate) fn label(mut self, label: impl Into<String>) -> Builder {
298 self.label = label.into();
299 self
300 }
301 pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder {
302 self.insert_text = Some(insert_text.into());
303 self
304 }
305 pub(crate) fn insert_snippet(
306 mut self,
307 _cap: SnippetCap,
308 snippet: impl Into<String>,
309 ) -> Builder {
310 self.insert_text_format = InsertTextFormat::Snippet;
311 self.insert_text(snippet)
312 }
313 pub(crate) fn kind(mut self, kind: CompletionItemKind) -> Builder {
314 self.kind = Some(kind);
315 self
316 }
317 pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder {
318 self.text_edit = Some(edit);
319 self
320 }
321 pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder {
322 self.insert_text_format = InsertTextFormat::Snippet;
323 self.text_edit(edit)
324 }
325 #[allow(unused)]
326 pub(crate) fn detail(self, detail: impl Into<String>) -> Builder {
327 self.set_detail(Some(detail))
328 }
329 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder {
330 self.detail = detail.map(Into::into);
331 self
332 }
333 #[allow(unused)]
334 pub(crate) fn documentation(self, docs: Documentation) -> Builder {
335 self.set_documentation(Some(docs))
336 }
337 pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder {
338 self.documentation = docs.map(Into::into);
339 self
340 }
341 pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder {
342 self.deprecated = Some(deprecated);
343 self
344 }
345 pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder {
346 self.score = Some(score);
347 self
348 }
349 pub(crate) fn trigger_call_info(mut self) -> Builder {
350 self.trigger_call_info = Some(true);
351 self
352 }
353}
354
355impl<'a> Into<CompletionItem> for Builder {
356 fn into(self) -> CompletionItem {
357 self.build()
358 }
359}
360
361/// Represents an in-progress set of completions being built.
362#[derive(Debug, Default)]
363pub(crate) struct Completions {
364 buf: Vec<CompletionItem>,
365}
366
367impl Completions {
368 pub(crate) fn add(&mut self, item: impl Into<CompletionItem>) {
369 self.buf.push(item.into())
370 }
371 pub(crate) fn add_all<I>(&mut self, items: I)
372 where
373 I: IntoIterator,
374 I::Item: Into<CompletionItem>,
375 {
376 items.into_iter().for_each(|item| self.add(item.into()))
377 }
378}
379
380impl Into<Vec<CompletionItem>> for Completions {
381 fn into(self) -> Vec<CompletionItem> {
382 self.buf
383 }
384}
diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs
deleted file mode 100644
index a68861e1c..000000000
--- a/crates/ra_ide/src/completion/patterns.rs
+++ /dev/null
@@ -1,194 +0,0 @@
1//! Patterns telling us certain facts about current syntax element, they are used in completion context
2
3use ra_syntax::{
4 algo::non_trivia_sibling,
5 ast::{self, LoopBodyOwner},
6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement,
7 SyntaxKind::*,
8 SyntaxNode, SyntaxToken,
9};
10
11#[cfg(test)]
12use crate::completion::test_utils::check_pattern_is_applicable;
13
14pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool {
15 not_same_range_ancestor(element)
16 .filter(|it| it.kind() == ASSOC_ITEM_LIST)
17 .and_then(|it| it.parent())
18 .filter(|it| it.kind() == TRAIT)
19 .is_some()
20}
21#[test]
22fn test_has_trait_parent() {
23 check_pattern_is_applicable(r"trait A { f<|> }", has_trait_parent);
24}
25
26pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool {
27 not_same_range_ancestor(element)
28 .filter(|it| it.kind() == ASSOC_ITEM_LIST)
29 .and_then(|it| it.parent())
30 .filter(|it| it.kind() == IMPL)
31 .is_some()
32}
33#[test]
34fn test_has_impl_parent() {
35 check_pattern_is_applicable(r"impl A { f<|> }", has_impl_parent);
36}
37
38pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool {
39 not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some()
40}
41#[test]
42fn test_has_block_expr_parent() {
43 check_pattern_is_applicable(r"fn my_fn() { let a = 2; f<|> }", has_block_expr_parent);
44}
45
46pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool {
47 element.ancestors().find(|it| it.kind() == BIND_PAT).is_some()
48}
49#[test]
50fn test_has_bind_pat_parent() {
51 check_pattern_is_applicable(r"fn my_fn(m<|>) {}", has_bind_pat_parent);
52 check_pattern_is_applicable(r"fn my_fn() { let m<|> }", has_bind_pat_parent);
53}
54
55pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool {
56 not_same_range_ancestor(element)
57 .filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR)
58 .is_some()
59}
60#[test]
61fn test_has_ref_parent() {
62 check_pattern_is_applicable(r"fn my_fn(&m<|>) {}", has_ref_parent);
63 check_pattern_is_applicable(r"fn my() { let &m<|> }", has_ref_parent);
64}
65
66pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool {
67 let ancestor = not_same_range_ancestor(element);
68 if !ancestor.is_some() {
69 return true;
70 }
71 ancestor.filter(|it| it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST).is_some()
72}
73#[test]
74fn test_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"mod foo { f<|> }", has_item_list_or_source_file_parent);
77}
78
79pub(crate) fn is_match_arm(element: SyntaxElement) -> bool {
80 not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some()
81 && previous_sibling_or_ancestor_sibling(element)
82 .and_then(|it| it.into_token())
83 .filter(|it| it.kind() == FAT_ARROW)
84 .is_some()
85}
86#[test]
87fn test_is_match_arm() {
88 check_pattern_is_applicable(r"fn my_fn() { match () { () => m<|> } }", is_match_arm);
89}
90
91pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool {
92 element
93 .into_token()
94 .and_then(|it| previous_non_trivia_token(it))
95 .filter(|it| it.kind() == UNSAFE_KW)
96 .is_some()
97}
98#[test]
99fn test_unsafe_is_prev() {
100 check_pattern_is_applicable(r"unsafe i<|>", unsafe_is_prev);
101}
102
103pub(crate) fn if_is_prev(element: SyntaxElement) -> bool {
104 element
105 .into_token()
106 .and_then(|it| previous_non_trivia_token(it))
107 .filter(|it| it.kind() == IF_KW)
108 .is_some()
109}
110#[test]
111fn test_if_is_prev() {
112 check_pattern_is_applicable(r"if l<|>", if_is_prev);
113}
114
115pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool {
116 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT).is_some()
117}
118#[test]
119fn test_has_trait_as_prev_sibling() {
120 check_pattern_is_applicable(r"trait A w<|> {}", has_trait_as_prev_sibling);
121}
122
123pub(crate) fn has_impl_as_prev_sibling(element: SyntaxElement) -> bool {
124 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == IMPL).is_some()
125}
126#[test]
127fn test_has_impl_as_prev_sibling() {
128 check_pattern_is_applicable(r"impl A w<|> {}", has_impl_as_prev_sibling);
129}
130
131pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
132 let leaf = match element {
133 NodeOrToken::Node(node) => node,
134 NodeOrToken::Token(token) => token.parent(),
135 };
136 for node in leaf.ancestors() {
137 if node.kind() == FN || node.kind() == LAMBDA_EXPR {
138 break;
139 }
140 let loop_body = match_ast! {
141 match node {
142 ast::ForExpr(it) => it.loop_body(),
143 ast::WhileExpr(it) => it.loop_body(),
144 ast::LoopExpr(it) => it.loop_body(),
145 _ => None,
146 }
147 };
148 if let Some(body) = loop_body {
149 if body.syntax().text_range().contains_range(leaf.text_range()) {
150 return true;
151 }
152 }
153 }
154 false
155}
156
157fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> {
158 element
159 .ancestors()
160 .take_while(|it| it.text_range() == element.text_range())
161 .last()
162 .and_then(|it| it.parent())
163}
164
165fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
166 let mut token = token.prev_token();
167 while let Some(inner) = token.clone() {
168 if !inner.kind().is_trivia() {
169 return Some(inner);
170 } else {
171 token = inner.prev_token();
172 }
173 }
174 None
175}
176
177fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option<SyntaxElement> {
178 let token_sibling = non_trivia_sibling(element.clone(), Direction::Prev);
179 if let Some(sibling) = token_sibling {
180 Some(sibling)
181 } else {
182 // if not trying to find first ancestor which has such a sibling
183 let node = match element {
184 NodeOrToken::Node(node) => node,
185 NodeOrToken::Token(token) => token.parent(),
186 };
187 let range = node.text_range();
188 let top_node = node.ancestors().take_while(|it| it.text_range() == range).last()?;
189 let prev_sibling_node = top_node.ancestors().find(|it| {
190 non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some()
191 })?;
192 non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)
193 }
194}
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
deleted file mode 100644
index 9a94ff476..000000000
--- a/crates/ra_ide/src/completion/presentation.rs
+++ /dev/null
@@ -1,1230 +0,0 @@
1//! This modules takes care of rendering various definitions as completion items.
2//! It also handles scoring (sorting) completions.
3
4use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type};
5use ra_syntax::ast::NameOwner;
6use stdx::SepBy;
7use test_utils::mark;
8
9use crate::{
10 completion::{
11 completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind,
12 CompletionKind, Completions,
13 },
14 display::{const_label, function_declaration, macro_label, type_label},
15 CompletionScore, RootDatabase,
16};
17
18impl Completions {
19 pub(crate) fn add_field(&mut self, ctx: &CompletionContext, field: hir::Field, ty: &Type) {
20 let is_deprecated = is_deprecated(field, ctx.db);
21 let name = field.name(ctx.db);
22 let mut completion_item =
23 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
24 .kind(CompletionItemKind::Field)
25 .detail(ty.display(ctx.db).to_string())
26 .set_documentation(field.docs(ctx.db))
27 .set_deprecated(is_deprecated);
28
29 if let Some(score) = compute_score(ctx, &ty, &name.to_string()) {
30 completion_item = completion_item.set_score(score);
31 }
32
33 completion_item.add_to(self);
34 }
35
36 pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) {
37 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), field.to_string())
38 .kind(CompletionItemKind::Field)
39 .detail(ty.display(ctx.db).to_string())
40 .add_to(self);
41 }
42
43 pub(crate) fn add_resolution(
44 &mut self,
45 ctx: &CompletionContext,
46 local_name: String,
47 resolution: &ScopeDef,
48 ) {
49 use hir::ModuleDef::*;
50
51 let completion_kind = match resolution {
52 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType,
53 _ => CompletionKind::Reference,
54 };
55
56 let kind = match resolution {
57 ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module,
58 ScopeDef::ModuleDef(Function(func)) => {
59 return self.add_function(ctx, *func, Some(local_name));
60 }
61 ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct,
62 // FIXME: add CompletionItemKind::Union
63 ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct,
64 ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum,
65
66 ScopeDef::ModuleDef(EnumVariant(var)) => {
67 return self.add_enum_variant(ctx, *var, Some(local_name));
68 }
69 ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const,
70 ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static,
71 ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait,
72 ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::TypeAlias,
73 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
74 ScopeDef::GenericParam(..) => CompletionItemKind::TypeParam,
75 ScopeDef::Local(..) => CompletionItemKind::Binding,
76 // (does this need its own kind?)
77 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => CompletionItemKind::TypeParam,
78 ScopeDef::MacroDef(mac) => {
79 return self.add_macro(ctx, Some(local_name), *mac);
80 }
81 ScopeDef::Unknown => {
82 return self.add(
83 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), local_name)
84 .kind(CompletionItemKind::UnresolvedReference),
85 );
86 }
87 };
88
89 let docs = match resolution {
90 ScopeDef::ModuleDef(Module(it)) => it.docs(ctx.db),
91 ScopeDef::ModuleDef(Adt(it)) => it.docs(ctx.db),
92 ScopeDef::ModuleDef(EnumVariant(it)) => it.docs(ctx.db),
93 ScopeDef::ModuleDef(Const(it)) => it.docs(ctx.db),
94 ScopeDef::ModuleDef(Static(it)) => it.docs(ctx.db),
95 ScopeDef::ModuleDef(Trait(it)) => it.docs(ctx.db),
96 ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(ctx.db),
97 _ => None,
98 };
99
100 let mut completion_item =
101 CompletionItem::new(completion_kind, ctx.source_range(), local_name.clone());
102 if let ScopeDef::Local(local) = resolution {
103 let ty = local.ty(ctx.db);
104 if !ty.is_unknown() {
105 completion_item = completion_item.detail(ty.display(ctx.db).to_string());
106 }
107 };
108
109 if let ScopeDef::Local(local) = resolution {
110 if let Some(score) = compute_score(ctx, &local.ty(ctx.db), &local_name) {
111 completion_item = completion_item.set_score(score);
112 }
113 }
114
115 // Add `<>` for generic types
116 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
117 if let Some(cap) = ctx.config.snippet_cap {
118 let has_non_default_type_params = match resolution {
119 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
120 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
121 _ => false,
122 };
123 if has_non_default_type_params {
124 mark::hit!(inserts_angle_brackets_for_generics);
125 completion_item = completion_item
126 .lookup_by(local_name.clone())
127 .label(format!("{}<…>", local_name))
128 .insert_snippet(cap, format!("{}<$0>", local_name));
129 }
130 }
131 }
132
133 completion_item.kind(kind).set_documentation(docs).add_to(self)
134 }
135
136 pub(crate) fn add_macro(
137 &mut self,
138 ctx: &CompletionContext,
139 name: Option<String>,
140 macro_: hir::MacroDef,
141 ) {
142 // FIXME: Currently proc-macro do not have ast-node,
143 // such that it does not have source
144 if macro_.is_proc_macro() {
145 return;
146 }
147
148 let name = match name {
149 Some(it) => it,
150 None => return,
151 };
152
153 let ast_node = macro_.source(ctx.db).value;
154 let detail = macro_label(&ast_node);
155
156 let docs = macro_.docs(ctx.db);
157
158 let mut builder = CompletionItem::new(
159 CompletionKind::Reference,
160 ctx.source_range(),
161 &format!("{}!", name),
162 )
163 .kind(CompletionItemKind::Macro)
164 .set_documentation(docs.clone())
165 .set_deprecated(is_deprecated(macro_, ctx.db))
166 .detail(detail);
167
168 let needs_bang = ctx.use_item_syntax.is_none() && !ctx.is_macro_call;
169 builder = match ctx.config.snippet_cap {
170 Some(cap) if needs_bang => {
171 let docs = docs.as_ref().map_or("", |s| s.as_str());
172 let (bra, ket) = guess_macro_braces(&name, docs);
173 builder
174 .insert_snippet(cap, format!("{}!{}$0{}", name, bra, ket))
175 .label(format!("{}!{}…{}", name, bra, ket))
176 .lookup_by(format!("{}!", name))
177 }
178 None if needs_bang => builder.insert_text(format!("{}!", name)),
179 _ => {
180 mark::hit!(dont_insert_macro_call_parens_unncessary);
181 builder.insert_text(name)
182 }
183 };
184
185 self.add(builder);
186 }
187
188 pub(crate) fn add_function(
189 &mut self,
190 ctx: &CompletionContext,
191 func: hir::Function,
192 local_name: Option<String>,
193 ) {
194 let has_self_param = func.has_self_param(ctx.db);
195
196 let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string());
197 let ast_node = func.source(ctx.db).value;
198
199 let mut builder =
200 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone())
201 .kind(if has_self_param {
202 CompletionItemKind::Method
203 } else {
204 CompletionItemKind::Function
205 })
206 .set_documentation(func.docs(ctx.db))
207 .set_deprecated(is_deprecated(func, ctx.db))
208 .detail(function_declaration(&ast_node));
209
210 let params = ast_node
211 .param_list()
212 .into_iter()
213 .flat_map(|it| it.params())
214 .flat_map(|it| it.pat())
215 .map(|pat| pat.to_string().trim_start_matches('_').into())
216 .collect();
217
218 builder = builder.add_call_parens(ctx, name, Params::Named(params));
219
220 self.add(builder)
221 }
222
223 pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
224 let ast_node = constant.source(ctx.db).value;
225 let name = match ast_node.name() {
226 Some(name) => name,
227 _ => return,
228 };
229 let detail = const_label(&ast_node);
230
231 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
232 .kind(CompletionItemKind::Const)
233 .set_documentation(constant.docs(ctx.db))
234 .set_deprecated(is_deprecated(constant, ctx.db))
235 .detail(detail)
236 .add_to(self);
237 }
238
239 pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) {
240 let type_def = type_alias.source(ctx.db).value;
241 let name = match type_def.name() {
242 Some(name) => name,
243 _ => return,
244 };
245 let detail = type_label(&type_def);
246
247 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
248 .kind(CompletionItemKind::TypeAlias)
249 .set_documentation(type_alias.docs(ctx.db))
250 .set_deprecated(is_deprecated(type_alias, ctx.db))
251 .detail(detail)
252 .add_to(self);
253 }
254
255 pub(crate) fn add_qualified_enum_variant(
256 &mut self,
257 ctx: &CompletionContext,
258 variant: hir::EnumVariant,
259 path: ModPath,
260 ) {
261 self.add_enum_variant_impl(ctx, variant, None, Some(path))
262 }
263
264 pub(crate) fn add_enum_variant(
265 &mut self,
266 ctx: &CompletionContext,
267 variant: hir::EnumVariant,
268 local_name: Option<String>,
269 ) {
270 self.add_enum_variant_impl(ctx, variant, local_name, None)
271 }
272
273 fn add_enum_variant_impl(
274 &mut self,
275 ctx: &CompletionContext,
276 variant: hir::EnumVariant,
277 local_name: Option<String>,
278 path: Option<ModPath>,
279 ) {
280 let is_deprecated = is_deprecated(variant, ctx.db);
281 let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string());
282 let qualified_name = match &path {
283 Some(it) => it.to_string(),
284 None => name.to_string(),
285 };
286 let detail_types = variant
287 .fields(ctx.db)
288 .into_iter()
289 .map(|field| (field.name(ctx.db), field.signature_ty(ctx.db)));
290 let variant_kind = variant.kind(ctx.db);
291 let detail = match variant_kind {
292 StructKind::Tuple | StructKind::Unit => detail_types
293 .map(|(_, t)| t.display(ctx.db).to_string())
294 .sep_by(", ")
295 .surround_with("(", ")")
296 .to_string(),
297 StructKind::Record => detail_types
298 .map(|(n, t)| format!("{}: {}", n, t.display(ctx.db).to_string()))
299 .sep_by(", ")
300 .surround_with("{ ", " }")
301 .to_string(),
302 };
303 let mut res = CompletionItem::new(
304 CompletionKind::Reference,
305 ctx.source_range(),
306 qualified_name.clone(),
307 )
308 .kind(CompletionItemKind::EnumVariant)
309 .set_documentation(variant.docs(ctx.db))
310 .set_deprecated(is_deprecated)
311 .detail(detail);
312
313 if path.is_some() {
314 res = res.lookup_by(name);
315 }
316
317 if variant_kind == StructKind::Tuple {
318 mark::hit!(inserts_parens_for_tuple_enums);
319 let params = Params::Anonymous(variant.fields(ctx.db).len());
320 res = res.add_call_parens(ctx, qualified_name, params)
321 }
322
323 res.add_to(self);
324 }
325}
326
327pub(crate) fn compute_score(
328 ctx: &CompletionContext,
329 ty: &Type,
330 name: &str,
331) -> Option<CompletionScore> {
332 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
333 mark::hit!(record_field_type_match);
334 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
335 (struct_field.name(ctx.db).to_string(), struct_field.signature_ty(ctx.db))
336 } else if let Some(active_parameter) = &ctx.active_parameter {
337 mark::hit!(active_param_type_match);
338 (active_parameter.name.clone(), active_parameter.ty.clone())
339 } else {
340 return None;
341 };
342
343 // Compute score
344 // For the same type
345 if &active_type != ty {
346 return None;
347 }
348
349 let mut res = CompletionScore::TypeMatch;
350
351 // If same type + same name then go top position
352 if active_name == name {
353 res = CompletionScore::TypeAndNameMatch
354 }
355
356 Some(res)
357}
358
359enum Params {
360 Named(Vec<String>),
361 Anonymous(usize),
362}
363
364impl Params {
365 fn len(&self) -> usize {
366 match self {
367 Params::Named(xs) => xs.len(),
368 Params::Anonymous(len) => *len,
369 }
370 }
371
372 fn is_empty(&self) -> bool {
373 self.len() == 0
374 }
375}
376
377impl Builder {
378 fn add_call_parens(mut self, ctx: &CompletionContext, name: String, params: Params) -> Builder {
379 if !ctx.config.add_call_parenthesis {
380 return self;
381 }
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 {
391 return self;
392 }
393
394 // Don't add parentheses if the expected type is some function reference.
395 if let Some(ty) = &ctx.expected_type {
396 if ty.is_fn() {
397 mark::hit!(no_call_parens_if_fn_ptr_needed);
398 return self;
399 }
400 }
401
402 let cap = match ctx.config.snippet_cap {
403 Some(it) => it,
404 None => return self,
405 };
406 // If not an import, add parenthesis automatically.
407 mark::hit!(inserts_parens_for_function_calls);
408
409 let (snippet, label) = if params.is_empty() {
410 (format!("{}()$0", name), format!("{}()", name))
411 } else {
412 self = self.trigger_call_info();
413 let snippet = match (ctx.config.add_call_argument_snippets, params) {
414 (true, Params::Named(params)) => {
415 let function_params_snippet = params
416 .iter()
417 .enumerate()
418 .map(|(index, param_name)| format!("${{{}:{}}}", index + 1, param_name))
419 .sep_by(", ");
420 format!("{}({})$0", name, function_params_snippet)
421 }
422 _ => {
423 mark::hit!(suppress_arg_snippets);
424 format!("{}($0)", name)
425 }
426 };
427
428 (snippet, format!("{}(…)", name))
429 };
430 self.lookup_by(name).label(label).insert_snippet(cap, snippet)
431 }
432}
433
434fn is_deprecated(node: impl HasAttrs, db: &RootDatabase) -> bool {
435 node.attrs(db).by_key("deprecated").exists()
436}
437
438fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static str) {
439 let mut votes = [0, 0, 0];
440 for (idx, s) in docs.match_indices(&macro_name) {
441 let (before, after) = (&docs[..idx], &docs[idx + s.len()..]);
442 // Ensure to match the full word
443 if after.starts_with('!')
444 && !before.ends_with(|c: char| c == '_' || c.is_ascii_alphanumeric())
445 {
446 // It may have spaces before the braces like `foo! {}`
447 match after[1..].chars().find(|&c| !c.is_whitespace()) {
448 Some('{') => votes[0] += 1,
449 Some('[') => votes[1] += 1,
450 Some('(') => votes[2] += 1,
451 _ => {}
452 }
453 }
454 }
455
456 // Insert a space before `{}`.
457 // We prefer the last one when some votes equal.
458 let (_vote, (bra, ket)) = votes
459 .iter()
460 .zip(&[(" {", "}"), ("[", "]"), ("(", ")")])
461 .max_by_key(|&(&vote, _)| vote)
462 .unwrap();
463 (*bra, *ket)
464}
465
466#[cfg(test)]
467mod tests {
468 use std::cmp::Reverse;
469
470 use expect::{expect, Expect};
471 use test_utils::mark;
472
473 use crate::{
474 completion::{
475 test_utils::{
476 check_edit, check_edit_with_config, do_completion, get_all_completion_items,
477 },
478 CompletionConfig, CompletionKind,
479 },
480 CompletionScore,
481 };
482
483 fn check(ra_fixture: &str, expect: Expect) {
484 let actual = do_completion(ra_fixture, CompletionKind::Reference);
485 expect.assert_debug_eq(&actual);
486 }
487
488 fn check_scores(ra_fixture: &str, expect: Expect) {
489 fn display_score(score: Option<CompletionScore>) -> &'static str {
490 match score {
491 Some(CompletionScore::TypeMatch) => "[type]",
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);
509 }
510
511 #[test]
512 fn enum_detail_includes_record_fields() {
513 check(
514 r#"
515enum Foo { Foo { x: i32, y: i32 } }
516
517fn main() { Foo::Fo<|> }
518"#,
519 expect![[r#"
520 [
521 CompletionItem {
522 label: "Foo",
523 source_range: 54..56,
524 delete: 54..56,
525 insert: "Foo",
526 kind: EnumVariant,
527 detail: "{ x: i32, y: i32 }",
528 },
529 ]
530 "#]],
531 );
532 }
533
534 #[test]
535 fn enum_detail_doesnt_include_tuple_fields() {
536 check(
537 r#"
538enum Foo { Foo (i32, i32) }
539
540fn main() { Foo::Fo<|> }
541"#,
542 expect![[r#"
543 [
544 CompletionItem {
545 label: "Foo(…)",
546 source_range: 46..48,
547 delete: 46..48,
548 insert: "Foo($0)",
549 kind: EnumVariant,
550 lookup: "Foo",
551 detail: "(i32, i32)",
552 trigger_call_info: true,
553 },
554 ]
555 "#]],
556 );
557 }
558
559 #[test]
560 fn enum_detail_just_parentheses_for_unit() {
561 check(
562 r#"
563enum Foo { Foo }
564
565fn main() { Foo::Fo<|> }
566"#,
567 expect![[r#"
568 [
569 CompletionItem {
570 label: "Foo",
571 source_range: 35..37,
572 delete: 35..37,
573 insert: "Foo",
574 kind: EnumVariant,
575 detail: "()",
576 },
577 ]
578 "#]],
579 );
580 }
581
582 #[test]
583 fn sets_deprecated_flag_in_completion_items() {
584 check(
585 r#"
586#[deprecated]
587fn something_deprecated() {}
588#[deprecated(since = "1.0.0")]
589fn something_else_deprecated() {}
590
591fn main() { som<|> }
592"#,
593 expect![[r#"
594 [
595 CompletionItem {
596 label: "main()",
597 source_range: 121..124,
598 delete: 121..124,
599 insert: "main()$0",
600 kind: Function,
601 lookup: "main",
602 detail: "fn main()",
603 },
604 CompletionItem {
605 label: "something_deprecated()",
606 source_range: 121..124,
607 delete: 121..124,
608 insert: "something_deprecated()$0",
609 kind: Function,
610 lookup: "something_deprecated",
611 detail: "fn something_deprecated()",
612 deprecated: true,
613 },
614 CompletionItem {
615 label: "something_else_deprecated()",
616 source_range: 121..124,
617 delete: 121..124,
618 insert: "something_else_deprecated()$0",
619 kind: Function,
620 lookup: "something_else_deprecated",
621 detail: "fn something_else_deprecated()",
622 deprecated: true,
623 },
624 ]
625 "#]],
626 );
627
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 "#]],
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 )
767 }
768
769 #[test]
770 fn inserts_parens_for_function_calls() {
771 mark::check!(inserts_parens_for_function_calls);
772 check_edit(
773 "no_args",
774 r#"
775fn no_args() {}
776fn main() { no_<|> }
777"#,
778 r#"
779fn no_args() {}
780fn main() { no_args()$0 }
781"#,
782 );
783
784 check_edit(
785 "with_args",
786 r#"
787fn with_args(x: i32, y: String) {}
788fn main() { with_<|> }
789"#,
790 r#"
791fn with_args(x: i32, y: String) {}
792fn main() { with_args(${1:x}, ${2:y})$0 }
793"#,
794 );
795
796 check_edit(
797 "foo",
798 r#"
799struct S;
800impl S {
801 fn foo(&self) {}
802}
803fn bar(s: &S) { s.f<|> }
804"#,
805 r#"
806struct S;
807impl S {
808 fn foo(&self) {}
809}
810fn bar(s: &S) { s.foo()$0 }
811"#,
812 );
813
814 check_edit(
815 "foo",
816 r#"
817struct S {}
818impl S {
819 fn foo(&self, x: i32) {}
820}
821fn bar(s: &S) {
822 s.f<|>
823}
824"#,
825 r#"
826struct S {}
827impl S {
828 fn foo(&self, x: i32) {}
829}
830fn bar(s: &S) {
831 s.foo(${1:x})$0
832}
833"#,
834 );
835 }
836
837 #[test]
838 fn suppress_arg_snippets() {
839 mark::check!(suppress_arg_snippets);
840 check_edit_with_config(
841 CompletionConfig { add_call_argument_snippets: false, ..CompletionConfig::default() },
842 "with_args",
843 r#"
844fn with_args(x: i32, y: String) {}
845fn main() { with_<|> }
846"#,
847 r#"
848fn with_args(x: i32, y: String) {}
849fn main() { with_args($0) }
850"#,
851 );
852 }
853
854 #[test]
855 fn strips_underscores_from_args() {
856 check_edit(
857 "foo",
858 r#"
859fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
860fn main() { f<|> }
861"#,
862 r#"
863fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
864fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
865"#,
866 );
867 }
868
869 #[test]
870 fn inserts_parens_for_tuple_enums() {
871 mark::check!(inserts_parens_for_tuple_enums);
872 check_edit(
873 "Some",
874 r#"
875enum Option<T> { Some(T), None }
876use Option::*;
877fn main() -> Option<i32> {
878 Som<|>
879}
880"#,
881 r#"
882enum Option<T> { Some(T), None }
883use Option::*;
884fn main() -> Option<i32> {
885 Some($0)
886}
887"#,
888 );
889 check_edit(
890 "Some",
891 r#"
892enum Option<T> { Some(T), None }
893use Option::*;
894fn main(value: Option<i32>) {
895 match value {
896 Som<|>
897 }
898}
899"#,
900 r#"
901enum Option<T> { Some(T), None }
902use Option::*;
903fn main(value: Option<i32>) {
904 match value {
905 Some($0)
906 }
907}
908"#,
909 );
910 }
911
912 #[test]
913 fn dont_duplicate_pattern_parens() {
914 mark::check!(dont_duplicate_pattern_parens);
915 check_edit(
916 "Var",
917 r#"
918enum E { Var(i32) }
919fn main() {
920 match E::Var(92) {
921 E::<|>(92) => (),
922 }
923}
924"#,
925 r#"
926enum E { Var(i32) }
927fn main() {
928 match E::Var(92) {
929 E::Var(92) => (),
930 }
931}
932"#,
933 );
934 }
935
936 #[test]
937 fn no_call_parens_if_fn_ptr_needed() {
938 mark::check!(no_call_parens_if_fn_ptr_needed);
939 check_edit(
940 "foo",
941 r#"
942fn foo(foo: u8, bar: u8) {}
943struct ManualVtable { f: fn(u8, u8) }
944
945fn main() -> ManualVtable {
946 ManualVtable { f: f<|> }
947}
948"#,
949 r#"
950fn foo(foo: u8, bar: u8) {}
951struct ManualVtable { f: fn(u8, u8) }
952
953fn main() -> ManualVtable {
954 ManualVtable { f: foo }
955}
956"#,
957 );
958 }
959
960 #[test]
961 fn no_parens_in_use_item() {
962 mark::check!(no_parens_in_use_item);
963 check_edit(
964 "foo",
965 r#"
966mod m { pub fn foo() {} }
967use crate::m::f<|>;
968"#,
969 r#"
970mod m { pub fn foo() {} }
971use crate::m::foo;
972"#,
973 );
974 }
975
976 #[test]
977 fn no_parens_in_call() {
978 check_edit(
979 "foo",
980 r#"
981fn foo(x: i32) {}
982fn main() { f<|>(); }
983"#,
984 r#"
985fn foo(x: i32) {}
986fn main() { foo(); }
987"#,
988 );
989 check_edit(
990 "foo",
991 r#"
992struct Foo;
993impl Foo { fn foo(&self){} }
994fn f(foo: &Foo) { foo.f<|>(); }
995"#,
996 r#"
997struct Foo;
998impl Foo { fn foo(&self){} }
999fn f(foo: &Foo) { foo.foo(); }
1000"#,
1001 );
1002 }
1003
1004 #[test]
1005 fn inserts_angle_brackets_for_generics() {
1006 mark::check!(inserts_angle_brackets_for_generics);
1007 check_edit(
1008 "Vec",
1009 r#"
1010struct Vec<T> {}
1011fn foo(xs: Ve<|>)
1012"#,
1013 r#"
1014struct Vec<T> {}
1015fn foo(xs: Vec<$0>)
1016"#,
1017 );
1018 check_edit(
1019 "Vec",
1020 r#"
1021type Vec<T> = (T,);
1022fn foo(xs: Ve<|>)
1023"#,
1024 r#"
1025type Vec<T> = (T,);
1026fn foo(xs: Vec<$0>)
1027"#,
1028 );
1029 check_edit(
1030 "Vec",
1031 r#"
1032struct Vec<T = i128> {}
1033fn foo(xs: Ve<|>)
1034"#,
1035 r#"
1036struct Vec<T = i128> {}
1037fn foo(xs: Vec)
1038"#,
1039 );
1040 check_edit(
1041 "Vec",
1042 r#"
1043struct Vec<T> {}
1044fn foo(xs: Ve<|><i128>)
1045"#,
1046 r#"
1047struct Vec<T> {}
1048fn foo(xs: Vec<i128>)
1049"#,
1050 );
1051 }
1052
1053 #[test]
1054 fn dont_insert_macro_call_parens_unncessary() {
1055 mark::check!(dont_insert_macro_call_parens_unncessary);
1056 check_edit(
1057 "frobnicate!",
1058 r#"
1059//- /main.rs
1060use foo::<|>;
1061//- /foo/lib.rs
1062#[macro_export]
1063macro_rules frobnicate { () => () }
1064"#,
1065 r#"
1066use foo::frobnicate;
1067"#,
1068 );
1069
1070 check_edit(
1071 "frobnicate!",
1072 r#"
1073macro_rules frobnicate { () => () }
1074fn main() { frob<|>!(); }
1075"#,
1076 r#"
1077macro_rules frobnicate { () => () }
1078fn main() { frobnicate!(); }
1079"#,
1080 );
1081 }
1082
1083 #[test]
1084 fn active_param_score() {
1085 mark::check!(active_param_type_match);
1086 check_scores(
1087 r#"
1088struct S { foo: i64, bar: u32, baz: u32 }
1089fn test(bar: u32) { }
1090fn foo(s: S) { test(s.<|>) }
1091"#,
1092 expect![[r#"
1093 fd bar [type+name]
1094 fd baz [type]
1095 fd foo []
1096 "#]],
1097 );
1098 }
1099
1100 #[test]
1101 fn record_field_scores() {
1102 mark::check!(record_field_type_match);
1103 check_scores(
1104 r#"
1105struct A { foo: i64, bar: u32, baz: u32 }
1106struct B { x: (), y: f32, bar: u32 }
1107fn foo(a: A) { B { bar: a.<|> }; }
1108"#,
1109 expect![[r#"
1110 fd bar [type+name]
1111 fd baz [type]
1112 fd foo []
1113 "#]],
1114 )
1115 }
1116
1117 #[test]
1118 fn record_field_and_call_scores() {
1119 check_scores(
1120 r#"
1121struct A { foo: i64, bar: u32, baz: u32 }
1122struct B { x: (), y: f32, bar: u32 }
1123fn f(foo: i64) { }
1124fn foo(a: A) { B { bar: f(a.<|>) }; }
1125"#,
1126 expect![[r#"
1127 fd foo [type+name]
1128 fd bar []
1129 fd baz []
1130 "#]],
1131 );
1132 check_scores(
1133 r#"
1134struct A { foo: i64, bar: u32, baz: u32 }
1135struct B { x: (), y: f32, bar: u32 }
1136fn f(foo: i64) { }
1137fn foo(a: A) { f(B { bar: a.<|> }); }
1138"#,
1139 expect![[r#"
1140 fd bar [type+name]
1141 fd baz [type]
1142 fd foo []
1143 "#]],
1144 );
1145 }
1146
1147 #[test]
1148 fn prioritize_exact_ref_match() {
1149 check_scores(
1150 r#"
1151struct WorldSnapshot { _f: () };
1152fn go(world: &WorldSnapshot) { go(w<|>) }
1153"#,
1154 expect![[r#"
1155 bn world [type+name]
1156 st WorldSnapshot []
1157 fn go(…) []
1158 "#]],
1159 );
1160 }
1161
1162 #[test]
1163 fn too_many_arguments() {
1164 mark::check!(too_many_arguments);
1165 check_scores(
1166 r#"
1167struct Foo;
1168fn f(foo: &Foo) { f(foo, w<|>) }
1169"#,
1170 expect![[r#"
1171 st Foo []
1172 fn f(…) []
1173 bn foo []
1174 "#]],
1175 );
1176 }
1177
1178 #[test]
1179 fn guesses_macro_braces() {
1180 check_edit(
1181 "vec!",
1182 r#"
1183/// Creates a [`Vec`] containing the arguments.
1184///
1185/// ```
1186/// let v = vec![1, 2, 3];
1187/// assert_eq!(v[0], 1);
1188/// assert_eq!(v[1], 2);
1189/// assert_eq!(v[2], 3);
1190/// ```
1191macro_rules! vec { () => {} }
1192
1193fn fn main() { v<|> }
1194"#,
1195 r#"
1196/// Creates a [`Vec`] containing the arguments.
1197///
1198/// ```
1199/// let v = vec![1, 2, 3];
1200/// assert_eq!(v[0], 1);
1201/// assert_eq!(v[1], 2);
1202/// assert_eq!(v[2], 3);
1203/// ```
1204macro_rules! vec { () => {} }
1205
1206fn fn main() { vec![$0] }
1207"#,
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 )
1229 }
1230}
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
deleted file mode 100644
index 919177745..000000000
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ /dev/null
@@ -1,114 +0,0 @@
1//! Runs completion for testing purposes.
2
3use hir::Semantics;
4use itertools::Itertools;
5use ra_syntax::{AstNode, NodeOrToken, SyntaxElement};
6use stdx::{format_to, trim_indent};
7use test_utils::assert_eq_text;
8
9use crate::{
10 completion::{completion_item::CompletionKind, CompletionConfig},
11 mock_analysis::analysis_and_position,
12 CompletionItem,
13};
14
15pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
16 do_completion_with_config(CompletionConfig::default(), code, kind)
17}
18
19pub(crate) fn do_completion_with_config(
20 config: CompletionConfig,
21 code: &str,
22 kind: CompletionKind,
23) -> Vec<CompletionItem> {
24 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(config, code)
25 .into_iter()
26 .filter(|c| c.completion_kind == kind)
27 .collect();
28 kind_completions.sort_by(|l, r| l.label().cmp(r.label()));
29 kind_completions
30}
31
32pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String {
33 completion_list_with_config(CompletionConfig::default(), code, kind)
34}
35
36pub(crate) fn completion_list_with_config(
37 config: CompletionConfig,
38 code: &str,
39 kind: CompletionKind,
40) -> String {
41 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(config, code)
42 .into_iter()
43 .filter(|c| c.completion_kind == kind)
44 .collect();
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);
52 kind_completions
53 .into_iter()
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 })
65 .collect()
66}
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
96pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) {
97 let (analysis, pos) = analysis_and_position(code);
98 analysis
99 .with_db(|db| {
100 let sema = Semantics::new(db);
101 let original_file = sema.parse(pos.file_id);
102 let token = original_file.syntax().token_at_offset(pos.offset).left_biased().unwrap();
103 assert!(check(NodeOrToken::Token(token)));
104 })
105 .unwrap();
106}
107
108pub(crate) fn get_all_completion_items(
109 config: CompletionConfig,
110 code: &str,
111) -> Vec<CompletionItem> {
112 let (analysis, position) = analysis_and_position(code);
113 analysis.completions(&config, position).unwrap().unwrap().into()
114}
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
deleted file mode 100644
index dd8a7ffd9..000000000
--- a/crates/ra_ide/src/diagnostics.rs
+++ /dev/null
@@ -1,797 +0,0 @@
1//! Collects diagnostics & fixits for a single file.
2//!
3//! The tricky bit here is that diagnostics are produced by hir in terms of
4//! macro-expanded files, but we need to present them to the users in terms of
5//! original files. So we need to map the ranges.
6
7use std::cell::RefCell;
8
9use hir::{
10 diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSinkBuilder},
11 HasSource, HirDisplay, Semantics, VariantDef,
12};
13use itertools::Itertools;
14use ra_db::SourceDatabase;
15use ra_ide_db::RootDatabase;
16use ra_prof::profile;
17use ra_syntax::{
18 algo,
19 ast::{self, edit::IndentLevel, make, AstNode},
20 SyntaxNode, TextRange, T,
21};
22use ra_text_edit::{TextEdit, TextEditBuilder};
23
24use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceFileEdit};
25
26#[derive(Debug, Copy, Clone)]
27pub enum Severity {
28 Error,
29 WeakWarning,
30}
31
32pub(crate) fn diagnostics(
33 db: &RootDatabase,
34 file_id: FileId,
35 enable_experimental: bool,
36) -> Vec<Diagnostic> {
37 let _p = profile("diagnostics");
38 let sema = Semantics::new(db);
39 let parse = db.parse(file_id);
40 let mut res = Vec::new();
41
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 {
44 range: err.range(),
45 message: format!("Syntax Error: {}", err),
46 severity: Severity::Error,
47 fix: None,
48 }));
49
50 for node in parse.tree().syntax().descendants() {
51 check_unnecessary_braces_in_use_statement(&mut res, file_id, &node);
52 check_struct_shorthand_initialization(&mut res, file_id, &node);
53 }
54 let res = RefCell::new(res);
55 let mut sink = DiagnosticSinkBuilder::new()
56 .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
57 let original_file = d.source().file_id.original_file(db);
58 let fix = Fix::new(
59 "Create module",
60 FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }
61 .into(),
62 );
63 res.borrow_mut().push(Diagnostic {
64 range: sema.diagnostics_range(d).range,
65 message: d.message(),
66 severity: Severity::Error,
67 fix: Some(fix),
68 })
69 })
70 .on::<hir::diagnostics::MissingFields, _>(|d| {
71 // Note that although we could add a diagnostics to
72 // fill the missing tuple field, e.g :
73 // `struct A(usize);`
74 // `let a = A { 0: () }`
75 // 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 None
78 } else {
79 let mut field_list = d.ast(db);
80 for f in d.missed_fields.iter() {
81 let field =
82 make::record_field(make::name_ref(&f.to_string()), Some(make::expr_unit()));
83 field_list = field_list.append_field(&field);
84 }
85
86 let edit = {
87 let mut builder = TextEditBuilder::default();
88 algo::diff(&d.ast(db).syntax(), &field_list.syntax())
89 .into_text_edit(&mut builder);
90 builder.finish()
91 };
92 Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()))
93 };
94
95 res.borrow_mut().push(Diagnostic {
96 range: sema.diagnostics_range(d).range,
97 message: d.message(),
98 severity: Severity::Error,
99 fix,
100 })
101 })
102 .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
103 let node = d.ast(db);
104 let replacement = format!("Ok({})", node.syntax());
105 let edit = TextEdit::replace(node.syntax().text_range(), replacement);
106 let source_change = SourceFileEdit { file_id, edit }.into();
107 let fix = Fix::new("Wrap with ok", source_change);
108 res.borrow_mut().push(Diagnostic {
109 range: sema.diagnostics_range(d).range,
110 message: d.message(),
111 severity: Severity::Error,
112 fix: Some(fix),
113 })
114 })
115 .on::<hir::diagnostics::NoSuchField, _>(|d| {
116 res.borrow_mut().push(Diagnostic {
117 range: sema.diagnostics_range(d).range,
118 message: d.message(),
119 severity: Severity::Error,
120 fix: missing_struct_field_fix(&sema, file_id, d),
121 })
122 })
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 });
134
135 if let Some(m) = sema.to_module_def(file_id) {
136 m.diagnostics(db, &mut sink);
137 };
138 drop(sink);
139 res.into_inner()
140}
141
142fn missing_struct_field_fix(
143 sema: &Semantics<RootDatabase>,
144 usage_file_id: FileId,
145 d: &hir::diagnostics::NoSuchField,
146) -> Option<Fix> {
147 let record_expr = sema.ast(d);
148
149 let record_lit = ast::RecordExpr::cast(record_expr.syntax().parent()?.parent()?)?;
150 let def_id = sema.resolve_variant(record_lit)?;
151 let module;
152 let def_file_id;
153 let record_fields = match VariantDef::from(def_id) {
154 VariantDef::Struct(s) => {
155 module = s.module(sema.db);
156 let source = s.source(sema.db);
157 def_file_id = source.file_id;
158 let fields = source.value.field_list()?;
159 record_field_list(fields)?
160 }
161 VariantDef::Union(u) => {
162 module = u.module(sema.db);
163 let source = u.source(sema.db);
164 def_file_id = source.file_id;
165 source.value.record_field_list()?
166 }
167 VariantDef::EnumVariant(e) => {
168 module = e.module(sema.db);
169 let source = e.source(sema.db);
170 def_file_id = source.file_id;
171 let fields = source.value.field_list()?;
172 record_field_list(fields)?
173 }
174 };
175 let def_file_id = def_file_id.original_file(sema.db);
176
177 let new_field_type = sema.type_of_expr(&record_expr.expr()?)?;
178 if new_field_type.is_unknown() {
179 return None;
180 }
181 let new_field = make::record_field_def(
182 record_expr.field_name()?,
183 make::type_ref(&new_field_type.display_source_code(sema.db, module.into()).ok()?),
184 );
185
186 let last_field = record_fields.fields().last()?;
187 let last_field_syntax = last_field.syntax();
188 let indent = IndentLevel::from_node(last_field_syntax);
189
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);
195
196 let needs_comma = !last_field_syntax.to_string().ends_with(',');
197 if needs_comma {
198 new_field = format!(",{}", new_field);
199 }
200
201 let source_change = SourceFileEdit {
202 file_id: def_file_id,
203 edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field),
204 };
205 let fix = Fix::new("Create field", source_change.into());
206 return Some(fix);
207
208 fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> {
209 match field_def_list {
210 ast::FieldList::RecordFieldList(it) => Some(it),
211 ast::FieldList::TupleFieldList(_) => None,
212 }
213 }
214}
215
216fn check_unnecessary_braces_in_use_statement(
217 acc: &mut Vec<Diagnostic>,
218 file_id: FileId,
219 node: &SyntaxNode,
220) -> Option<()> {
221 let use_tree_list = ast::UseTreeList::cast(node.clone())?;
222 if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
223 let range = use_tree_list.syntax().text_range();
224 let edit =
225 text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree)
226 .unwrap_or_else(|| {
227 let to_replace = single_use_tree.syntax().text().to_string();
228 let mut edit_builder = TextEditBuilder::default();
229 edit_builder.delete(range);
230 edit_builder.insert(range.start(), to_replace);
231 edit_builder.finish()
232 });
233
234 acc.push(Diagnostic {
235 range,
236 message: "Unnecessary braces in use statement".to_string(),
237 severity: Severity::WeakWarning,
238 fix: Some(Fix::new(
239 "Remove unnecessary braces",
240 SourceFileEdit { file_id, edit }.into(),
241 )),
242 });
243 }
244
245 Some(())
246}
247
248fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
249 single_use_tree: &ast::UseTree,
250) -> Option<TextEdit> {
251 let use_tree_list_node = single_use_tree.syntax().parent()?;
252 if single_use_tree.path()?.segment()?.syntax().first_child_or_token()?.kind() == T![self] {
253 let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start();
254 let end = use_tree_list_node.text_range().end();
255 let range = TextRange::new(start, end);
256 return Some(TextEdit::delete(range));
257 }
258 None
259}
260
261fn check_struct_shorthand_initialization(
262 acc: &mut Vec<Diagnostic>,
263 file_id: FileId,
264 node: &SyntaxNode,
265) -> Option<()> {
266 let record_lit = ast::RecordExpr::cast(node.clone())?;
267 let record_field_list = record_lit.record_expr_field_list()?;
268 for record_field in record_field_list.fields() {
269 if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) {
270 let field_name = name_ref.syntax().text().to_string();
271 let field_expr = expr.syntax().text().to_string();
272 let field_name_is_tup_index = name_ref.as_tuple_field().is_some();
273 if field_name == field_expr && !field_name_is_tup_index {
274 let mut edit_builder = TextEditBuilder::default();
275 edit_builder.delete(record_field.syntax().text_range());
276 edit_builder.insert(record_field.syntax().text_range().start(), field_name);
277 let edit = edit_builder.finish();
278
279 acc.push(Diagnostic {
280 range: record_field.syntax().text_range(),
281 message: "Shorthand struct initialization".to_string(),
282 severity: Severity::WeakWarning,
283 fix: Some(Fix::new(
284 "Use struct shorthand initialization",
285 SourceFileEdit { file_id, edit }.into(),
286 )),
287 });
288 }
289 }
290 }
291 Some(())
292}
293
294#[cfg(test)]
295mod tests {
296 use stdx::trim_indent;
297 use test_utils::assert_eq_text;
298
299 use crate::mock_analysis::{analysis_and_position, single_file, MockAnalysis};
300 use expect::{expect, Expect};
301
302 /// Takes a multi-file input fixture with annotated cursor positions,
303 /// and checks that:
304 /// * a diagnostic is produced
305 /// * this diagnostic touches the input cursor position
306 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
307 fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
308 let after = trim_indent(ra_fixture_after);
309
310 let (analysis, file_position) = analysis_and_position(ra_fixture_before);
311 let diagnostic = analysis.diagnostics(file_position.file_id, true).unwrap().pop().unwrap();
312 let mut fix = diagnostic.fix.unwrap();
313 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
314 let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
315 let actual = {
316 let mut actual = target_file_contents.to_string();
317 edit.apply(&mut actual);
318 actual
319 };
320
321 assert_eq_text!(&after, &actual);
322 assert!(
323 diagnostic.range.start() <= file_position.offset
324 && diagnostic.range.end() >= file_position.offset,
325 "diagnostic range {:?} does not touch cursor position {:?}",
326 diagnostic.range,
327 file_position.offset
328 );
329 }
330
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) {
334 let ra_fixture_after = &trim_indent(ra_fixture_after);
335 let (analysis, file_pos) = analysis_and_position(ra_fixture_before);
336 let current_file_id = file_pos.file_id;
337 let diagnostic = analysis.diagnostics(current_file_id, true).unwrap().pop().unwrap();
338 let mut fix = diagnostic.fix.unwrap();
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();
342 let actual = {
343 let mut actual = before.to_string();
344 edit.edit.apply(&mut actual);
345 actual
346 };
347 assert_eq_text!(ra_fixture_after, &actual);
348 }
349
350 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
351 /// apply to the file containing the cursor.
352 fn check_no_diagnostics(ra_fixture: &str) {
353 let mock = MockAnalysis::with_files(ra_fixture);
354 let files = mock.files().map(|(it, _)| it).collect::<Vec<_>>();
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);
361 }
362
363 fn check_expect(ra_fixture: &str, expect: Expect) {
364 let (analysis, file_id) = single_file(ra_fixture);
365 let diagnostics = analysis.diagnostics(file_id, true).unwrap();
366 expect.assert_debug_eq(&diagnostics)
367 }
368
369 #[test]
370 fn test_wrap_return_type() {
371 check_fix(
372 r#"
373//- /main.rs
374use core::result::Result::{self, Ok, Err};
375
376fn div(x: i32, y: i32) -> Result<i32, ()> {
377 if y == 0 {
378 return Err(());
379 }
380 x / y<|>
381}
382//- /core/lib.rs
383pub mod result {
384 pub enum Result<T, E> { Ok(T), Err(E) }
385}
386"#,
387 r#"
388use core::result::Result::{self, Ok, Err};
389
390fn div(x: i32, y: i32) -> Result<i32, ()> {
391 if y == 0 {
392 return Err(());
393 }
394 Ok(x / y)
395}
396"#,
397 );
398 }
399
400 #[test]
401 fn test_wrap_return_type_handles_generic_functions() {
402 check_fix(
403 r#"
404//- /main.rs
405use core::result::Result::{self, Ok, Err};
406
407fn div<T>(x: T) -> Result<T, i32> {
408 if x == 0 {
409 return Err(7);
410 }
411 <|>x
412}
413//- /core/lib.rs
414pub mod result {
415 pub enum Result<T, E> { Ok(T), Err(E) }
416}
417"#,
418 r#"
419use core::result::Result::{self, Ok, Err};
420
421fn div<T>(x: T) -> Result<T, i32> {
422 if x == 0 {
423 return Err(7);
424 }
425 Ok(x)
426}
427"#,
428 );
429 }
430
431 #[test]
432 fn test_wrap_return_type_handles_type_aliases() {
433 check_fix(
434 r#"
435//- /main.rs
436use core::result::Result::{self, Ok, Err};
437
438type MyResult<T> = Result<T, ()>;
439
440fn div(x: i32, y: i32) -> MyResult<i32> {
441 if y == 0 {
442 return Err(());
443 }
444 x <|>/ y
445}
446//- /core/lib.rs
447pub mod result {
448 pub enum Result<T, E> { Ok(T), Err(E) }
449}
450"#,
451 r#"
452use core::result::Result::{self, Ok, Err};
453
454type MyResult<T> = Result<T, ()>;
455
456fn div(x: i32, y: i32) -> MyResult<i32> {
457 if y == 0 {
458 return Err(());
459 }
460 Ok(x / y)
461}
462"#,
463 );
464 }
465
466 #[test]
467 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
468 check_no_diagnostics(
469 r#"
470//- /main.rs
471use core::result::Result::{self, Ok, Err};
472
473fn foo() -> Result<(), i32> { 0 }
474
475//- /core/lib.rs
476pub mod result {
477 pub enum Result<T, E> { Ok(T), Err(E) }
478}
479"#,
480 );
481 }
482
483 #[test]
484 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
485 check_no_diagnostics(
486 r#"
487//- /main.rs
488use core::result::Result::{self, Ok, Err};
489
490enum SomeOtherEnum { Ok(i32), Err(String) }
491
492fn foo() -> SomeOtherEnum { 0 }
493
494//- /core/lib.rs
495pub mod result {
496 pub enum Result<T, E> { Ok(T), Err(E) }
497}
498"#,
499 );
500 }
501
502 #[test]
503 fn test_fill_struct_fields_empty() {
504 check_fix(
505 r#"
506struct TestStruct { one: i32, two: i64 }
507
508fn test_fn() {
509 let s = TestStruct {<|>};
510}
511"#,
512 r#"
513struct TestStruct { one: i32, two: i64 }
514
515fn test_fn() {
516 let s = TestStruct { one: (), two: ()};
517}
518"#,
519 );
520 }
521
522 #[test]
523 fn test_fill_struct_fields_self() {
524 check_fix(
525 r#"
526struct TestStruct { one: i32 }
527
528impl TestStruct {
529 fn test_fn() { let s = Self {<|>}; }
530}
531"#,
532 r#"
533struct TestStruct { one: i32 }
534
535impl TestStruct {
536 fn test_fn() { let s = Self { one: ()}; }
537}
538"#,
539 );
540 }
541
542 #[test]
543 fn test_fill_struct_fields_enum() {
544 check_fix(
545 r#"
546enum Expr {
547 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
548}
549
550impl Expr {
551 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
552 Expr::Bin {<|> }
553 }
554}
555"#,
556 r#"
557enum Expr {
558 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
559}
560
561impl Expr {
562 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
563 Expr::Bin { lhs: (), rhs: () }
564 }
565}
566"#,
567 );
568 }
569
570 #[test]
571 fn test_fill_struct_fields_partial() {
572 check_fix(
573 r#"
574struct TestStruct { one: i32, two: i64 }
575
576fn test_fn() {
577 let s = TestStruct{ two: 2<|> };
578}
579"#,
580 r"
581struct TestStruct { one: i32, two: i64 }
582
583fn test_fn() {
584 let s = TestStruct{ two: 2, one: () };
585}
586",
587 );
588 }
589
590 #[test]
591 fn test_fill_struct_fields_no_diagnostic() {
592 check_no_diagnostics(
593 r"
594 struct TestStruct { one: i32, two: i64 }
595
596 fn test_fn() {
597 let one = 1;
598 let s = TestStruct{ one, two: 2 };
599 }
600 ",
601 );
602 }
603
604 #[test]
605 fn test_fill_struct_fields_no_diagnostic_on_spread() {
606 check_no_diagnostics(
607 r"
608 struct TestStruct { one: i32, two: i64 }
609
610 fn test_fn() {
611 let one = 1;
612 let s = TestStruct{ ..a };
613 }
614 ",
615 );
616 }
617
618 #[test]
619 fn test_unresolved_module_diagnostic() {
620 check_expect(
621 r#"mod foo;"#,
622 expect![[r#"
623 [
624 Diagnostic {
625 message: "unresolved module",
626 range: 0..8,
627 severity: Error,
628 fix: Some(
629 Fix {
630 label: "Create module",
631 source_change: SourceChange {
632 source_file_edits: [],
633 file_system_edits: [
634 CreateFile {
635 anchor: FileId(
636 1,
637 ),
638 dst: "foo.rs",
639 },
640 ],
641 is_snippet: false,
642 },
643 },
644 ),
645 },
646 ]
647 "#]],
648 );
649 }
650
651 #[test]
652 fn range_mapping_out_of_macros() {
653 // FIXME: this is very wrong, but somewhat tricky to fix.
654 check_fix(
655 r#"
656fn some() {}
657fn items() {}
658fn here() {}
659
660macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
661
662fn main() {
663 let _x = id![Foo { a: <|>42 }];
664}
665
666pub struct Foo { pub a: i32, pub b: i32 }
667"#,
668 r#"
669fn {a:42, b: ()} {}
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"#,
681 );
682 }
683
684 #[test]
685 fn test_check_unnecessary_braces_in_use_statement() {
686 check_no_diagnostics(
687 r#"
688use a;
689use a::{c, d::e};
690"#,
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};"#);
697 }
698
699 #[test]
700 fn test_check_struct_shorthand_initialization() {
701 check_no_diagnostics(
702 r#"
703struct A { a: &'static str }
704fn main() { A { a: "hello" } }
705"#,
706 );
707 check_no_diagnostics(
708 r#"
709struct A(usize);
710fn main() { A { 0: 0 } }
711"#,
712 );
713
714 check_fix(
715 r#"
716struct A { a: &'static str }
717fn main() {
718 let a = "haha";
719 A { a<|>: a }
720}
721"#,
722 r#"
723struct A { a: &'static str }
724fn main() {
725 let a = "haha";
726 A { a }
727}
728"#,
729 );
730
731 check_fix(
732 r#"
733struct A { a: &'static str, b: &'static str }
734fn main() {
735 let a = "haha";
736 let b = "bb";
737 A { a<|>: a, b }
738}
739"#,
740 r#"
741struct A { a: &'static str, b: &'static str }
742fn main() {
743 let a = "haha";
744 let b = "bb";
745 A { a, b }
746}
747"#,
748 );
749 }
750
751 #[test]
752 fn test_add_field_from_usage() {
753 check_fix(
754 r"
755fn main() {
756 Foo { bar: 3, baz<|>: false};
757}
758struct Foo {
759 bar: i32
760}
761",
762 r"
763fn main() {
764 Foo { bar: 3, baz: false};
765}
766struct Foo {
767 bar: i32,
768 baz: bool
769}
770",
771 )
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 }
797}
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
deleted file mode 100644
index fd42aa435..000000000
--- a/crates/ra_ide/src/display.rs
+++ /dev/null
@@ -1,83 +0,0 @@
1//! This module contains utilities for turning SyntaxNodes and HIR types
2//! into types that may be used to render in a UI.
3
4mod navigation_target;
5mod short_label;
6
7use ra_syntax::{
8 ast::{self, AstNode, AttrsOwner, GenericParamsOwner, NameOwner},
9 SyntaxKind::{ATTR, COMMENT},
10};
11
12use ast::VisibilityOwner;
13use stdx::format_to;
14
15pub use navigation_target::NavigationTarget;
16pub(crate) use navigation_target::{ToNav, TryToNav};
17pub(crate) use short_label::ShortLabel;
18
19pub(crate) fn function_declaration(node: &ast::Fn) -> 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
55}
56
57pub(crate) fn const_label(node: &ast::Const) -> String {
58 let label: String = node
59 .syntax()
60 .children_with_tokens()
61 .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
62 .map(|node| node.to_string())
63 .collect();
64
65 label.trim().to_owned()
66}
67
68pub(crate) fn type_label(node: &ast::TypeAlias) -> String {
69 let label: String = node
70 .syntax()
71 .children_with_tokens()
72 .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
73 .map(|node| node.to_string())
74 .collect();
75
76 label.trim().to_owned()
77}
78
79pub(crate) fn macro_label(node: &ast::MacroCall) -> String {
80 let name = node.name().map(|name| name.syntax().text().to_string()).unwrap_or_default();
81 let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" };
82 format!("{}macro_rules! {}", vis, name)
83}
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
deleted file mode 100644
index 45fbc86ef..000000000
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ /dev/null
@@ -1,491 +0,0 @@
1//! FIXME: write short doc here
2
3use either::Either;
4use hir::{original_range, AssocItem, FieldSource, HasSource, InFile, ModuleSource};
5use ra_db::{FileId, SourceDatabase};
6use ra_ide_db::{defs::Definition, RootDatabase};
7use ra_syntax::{
8 ast::{self, DocCommentsOwner, NameOwner},
9 match_ast, AstNode, SmolStr,
10 SyntaxKind::{self, BIND_PAT, TYPE_PARAM},
11 TextRange,
12};
13
14use crate::FileSymbol;
15
16use super::short_label::ShortLabel;
17
18/// `NavigationTarget` represents and element in the editor's UI which you can
19/// click on to navigate to a particular piece of code.
20///
21/// Typically, a `NavigationTarget` corresponds to some element in the source
22/// code, like a function or a struct, but this is not strictly required.
23#[derive(Debug, Clone, PartialEq, Eq, Hash)]
24pub struct NavigationTarget {
25 pub file_id: FileId,
26 /// Range which encompasses the whole element.
27 ///
28 /// Should include body, doc comments, attributes, etc.
29 ///
30 /// Clients should use this range to answer "is the cursor inside the
31 /// element?" question.
32 pub full_range: TextRange,
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>,
47}
48
49pub(crate) trait ToNav {
50 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget;
51}
52
53pub(crate) trait TryToNav {
54 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget>;
55}
56
57impl NavigationTarget {
58 pub fn focus_or_full_range(&self) -> TextRange {
59 self.focus_range.unwrap_or(self.full_range)
60 }
61
62 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
63 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
64 if let Some(src) = module.declaration_source(db) {
65 let frange = original_range(db, src.as_ref().map(|it| it.syntax()));
66 let mut res = NavigationTarget::from_syntax(
67 frange.file_id,
68 name,
69 None,
70 frange.range,
71 src.value.syntax().kind(),
72 );
73 res.docs = src.value.doc_comment_text();
74 res.description = src.value.short_label();
75 return res;
76 }
77 module.to_nav(db)
78 }
79
80 #[cfg(test)]
81 pub(crate) fn assert_match(&self, expected: &str) {
82 let actual = self.debug_render();
83 test_utils::assert_eq_text!(expected.trim(), actual.trim(),);
84 }
85
86 #[cfg(test)]
87 pub(crate) fn debug_render(&self) -> String {
88 let mut buf =
89 format!("{} {:?} {:?} {:?}", self.name, self.kind, self.file_id, self.full_range);
90 if let Some(focus_range) = self.focus_range {
91 buf.push_str(&format!(" {:?}", focus_range))
92 }
93 if let Some(container_name) = &self.container_name {
94 buf.push_str(&format!(" {}", container_name))
95 }
96 buf
97 }
98
99 /// Allows `NavigationTarget` to be created from a `NameOwner`
100 pub(crate) fn from_named(
101 db: &RootDatabase,
102 node: InFile<&dyn ast::NameOwner>,
103 ) -> NavigationTarget {
104 let name =
105 node.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_"));
106 let focus_range =
107 node.value.name().map(|it| original_range(db, node.with_value(it.syntax())).range);
108 let frange = original_range(db, node.map(|it| it.syntax()));
109
110 NavigationTarget::from_syntax(
111 frange.file_id,
112 name,
113 focus_range,
114 frange.range,
115 node.value.syntax().kind(),
116 )
117 }
118
119 /// Allows `NavigationTarget` to be created from a `DocCommentsOwner` and a `NameOwner`
120 pub(crate) fn from_doc_commented(
121 db: &RootDatabase,
122 named: InFile<&dyn ast::NameOwner>,
123 node: InFile<&dyn ast::DocCommentsOwner>,
124 ) -> NavigationTarget {
125 let name =
126 named.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_"));
127 let frange = original_range(db, node.map(|it| it.syntax()));
128
129 NavigationTarget::from_syntax(
130 frange.file_id,
131 name,
132 None,
133 frange.range,
134 node.value.syntax().kind(),
135 )
136 }
137
138 fn from_syntax(
139 file_id: FileId,
140 name: SmolStr,
141 focus_range: Option<TextRange>,
142 full_range: TextRange,
143 kind: SyntaxKind,
144 ) -> NavigationTarget {
145 NavigationTarget {
146 file_id,
147 name,
148 kind,
149 full_range,
150 focus_range,
151 container_name: None,
152 description: None,
153 docs: None,
154 }
155 }
156}
157
158impl ToNav for FileSymbol {
159 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
160 NavigationTarget {
161 file_id: self.file_id,
162 name: self.name.clone(),
163 kind: self.kind,
164 full_range: self.range,
165 focus_range: self.name_range,
166 container_name: self.container_name.clone(),
167 description: description_from_symbol(db, self),
168 docs: docs_from_symbol(db, self),
169 }
170 }
171}
172
173impl TryToNav for Definition {
174 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
175 match self {
176 Definition::Macro(it) => Some(it.to_nav(db)),
177 Definition::Field(it) => Some(it.to_nav(db)),
178 Definition::ModuleDef(it) => it.try_to_nav(db),
179 Definition::SelfType(it) => Some(it.to_nav(db)),
180 Definition::Local(it) => Some(it.to_nav(db)),
181 Definition::TypeParam(it) => Some(it.to_nav(db)),
182 }
183 }
184}
185
186impl TryToNav for hir::ModuleDef {
187 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
188 let res = match self {
189 hir::ModuleDef::Module(it) => it.to_nav(db),
190 hir::ModuleDef::Function(it) => it.to_nav(db),
191 hir::ModuleDef::Adt(it) => it.to_nav(db),
192 hir::ModuleDef::EnumVariant(it) => it.to_nav(db),
193 hir::ModuleDef::Const(it) => it.to_nav(db),
194 hir::ModuleDef::Static(it) => it.to_nav(db),
195 hir::ModuleDef::Trait(it) => it.to_nav(db),
196 hir::ModuleDef::TypeAlias(it) => it.to_nav(db),
197 hir::ModuleDef::BuiltinType(_) => return None,
198 };
199 Some(res)
200 }
201}
202
203pub(crate) trait ToNavFromAst {}
204impl ToNavFromAst for hir::Function {}
205impl ToNavFromAst for hir::Const {}
206impl ToNavFromAst for hir::Static {}
207impl ToNavFromAst for hir::Struct {}
208impl ToNavFromAst for hir::Enum {}
209impl ToNavFromAst for hir::EnumVariant {}
210impl ToNavFromAst for hir::Union {}
211impl ToNavFromAst for hir::TypeAlias {}
212impl ToNavFromAst for hir::Trait {}
213
214impl<D> ToNav for D
215where
216 D: HasSource + ToNavFromAst + Copy,
217 D::Ast: ast::DocCommentsOwner + ast::NameOwner + ShortLabel,
218{
219 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
220 let src = self.source(db);
221 let mut res =
222 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
223 res.docs = src.value.doc_comment_text();
224 res.description = src.value.short_label();
225 res
226 }
227}
228
229impl ToNav for hir::Module {
230 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
231 let src = self.definition_source(db);
232 let name = self.name(db).map(|it| it.to_string().into()).unwrap_or_default();
233 let (syntax, focus) = match &src.value {
234 ModuleSource::SourceFile(node) => (node.syntax(), None),
235 ModuleSource::Module(node) => {
236 (node.syntax(), node.name().map(|it| it.syntax().text_range()))
237 }
238 };
239 let frange = original_range(db, src.with_value(syntax));
240 NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, syntax.kind())
241 }
242}
243
244impl ToNav for hir::ImplDef {
245 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
246 let src = self.source(db);
247 let derive_attr = self.is_builtin_derive(db);
248 let frange = if let Some(item) = &derive_attr {
249 original_range(db, item.syntax())
250 } else {
251 original_range(db, src.as_ref().map(|it| it.syntax()))
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 };
258
259 NavigationTarget::from_syntax(
260 frange.file_id,
261 "impl".into(),
262 focus_range,
263 frange.range,
264 src.value.syntax().kind(),
265 )
266 }
267}
268
269impl ToNav for hir::Field {
270 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
271 let src = self.source(db);
272
273 match &src.value {
274 FieldSource::Named(it) => {
275 let mut res = NavigationTarget::from_named(db, src.with_value(it));
276 res.docs = it.doc_comment_text();
277 res.description = it.short_label();
278 res
279 }
280 FieldSource::Pos(it) => {
281 let frange = original_range(db, src.with_value(it.syntax()));
282 NavigationTarget::from_syntax(
283 frange.file_id,
284 "".into(),
285 None,
286 frange.range,
287 it.syntax().kind(),
288 )
289 }
290 }
291 }
292}
293
294impl ToNav for hir::MacroDef {
295 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
296 let src = self.source(db);
297 log::debug!("nav target {:#?}", src.value.syntax());
298 let mut res =
299 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
300 res.docs = src.value.doc_comment_text();
301 res
302 }
303}
304
305impl ToNav for hir::Adt {
306 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
307 match self {
308 hir::Adt::Struct(it) => it.to_nav(db),
309 hir::Adt::Union(it) => it.to_nav(db),
310 hir::Adt::Enum(it) => it.to_nav(db),
311 }
312 }
313}
314
315impl ToNav for hir::AssocItem {
316 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
317 match self {
318 AssocItem::Function(it) => it.to_nav(db),
319 AssocItem::Const(it) => it.to_nav(db),
320 AssocItem::TypeAlias(it) => it.to_nav(db),
321 }
322 }
323}
324
325impl ToNav for hir::Local {
326 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
327 let src = self.source(db);
328 let node = match &src.value {
329 Either::Left(bind_pat) => {
330 bind_pat.name().map_or_else(|| bind_pat.syntax().clone(), |it| it.syntax().clone())
331 }
332 Either::Right(it) => it.syntax().clone(),
333 };
334 let full_range = original_range(db, src.with_value(&node));
335 let name = match self.name(db) {
336 Some(it) => it.to_string().into(),
337 None => "".into(),
338 };
339 NavigationTarget {
340 file_id: full_range.file_id,
341 name,
342 kind: BIND_PAT,
343 full_range: full_range.range,
344 focus_range: None,
345 container_name: None,
346 description: None,
347 docs: None,
348 }
349 }
350}
351
352impl ToNav for hir::TypeParam {
353 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
354 let src = self.source(db);
355 let full_range = match &src.value {
356 Either::Left(it) => it.syntax().text_range(),
357 Either::Right(it) => it.syntax().text_range(),
358 };
359 let focus_range = match &src.value {
360 Either::Left(_) => None,
361 Either::Right(it) => it.name().map(|it| it.syntax().text_range()),
362 };
363 NavigationTarget {
364 file_id: src.file_id.original_file(db),
365 name: self.name(db).to_string().into(),
366 kind: TYPE_PARAM,
367 full_range,
368 focus_range,
369 container_name: None,
370 description: None,
371 docs: None,
372 }
373 }
374}
375
376pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> {
377 let parse = db.parse(symbol.file_id);
378 let node = symbol.ptr.to_node(parse.tree().syntax());
379
380 match_ast! {
381 match node {
382 ast::Fn(it) => it.doc_comment_text(),
383 ast::Struct(it) => it.doc_comment_text(),
384 ast::Enum(it) => it.doc_comment_text(),
385 ast::Trait(it) => it.doc_comment_text(),
386 ast::Module(it) => it.doc_comment_text(),
387 ast::TypeAlias(it) => it.doc_comment_text(),
388 ast::Const(it) => it.doc_comment_text(),
389 ast::Static(it) => it.doc_comment_text(),
390 ast::RecordField(it) => it.doc_comment_text(),
391 ast::Variant(it) => it.doc_comment_text(),
392 ast::MacroCall(it) => it.doc_comment_text(),
393 _ => None,
394 }
395 }
396}
397
398/// Get a description of a symbol.
399///
400/// e.g. `struct Name`, `enum Name`, `fn Name`
401pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> {
402 let parse = db.parse(symbol.file_id);
403 let node = symbol.ptr.to_node(parse.tree().syntax());
404
405 match_ast! {
406 match node {
407 ast::Fn(it) => it.short_label(),
408 ast::Struct(it) => it.short_label(),
409 ast::Enum(it) => it.short_label(),
410 ast::Trait(it) => it.short_label(),
411 ast::Module(it) => it.short_label(),
412 ast::TypeAlias(it) => it.short_label(),
413 ast::Const(it) => it.short_label(),
414 ast::Static(it) => it.short_label(),
415 ast::RecordField(it) => it.short_label(),
416 ast::Variant(it) => it.short_label(),
417 _ => None,
418 }
419 }
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
deleted file mode 100644
index bddf1bd47..000000000
--- a/crates/ra_ide/src/display/short_label.rs
+++ /dev/null
@@ -1,101 +0,0 @@
1//! FIXME: write short doc here
2
3use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
4use stdx::format_to;
5
6pub(crate) trait ShortLabel {
7 fn short_label(&self) -> Option<String>;
8}
9
10impl ShortLabel for ast::Fn {
11 fn short_label(&self) -> Option<String> {
12 Some(crate::display::function_declaration(self))
13 }
14}
15
16impl ShortLabel for ast::Struct {
17 fn short_label(&self) -> Option<String> {
18 short_label_from_node(self, "struct ")
19 }
20}
21
22impl ShortLabel for ast::Union {
23 fn short_label(&self) -> Option<String> {
24 short_label_from_node(self, "union ")
25 }
26}
27
28impl ShortLabel for ast::Enum {
29 fn short_label(&self) -> Option<String> {
30 short_label_from_node(self, "enum ")
31 }
32}
33
34impl ShortLabel for ast::Trait {
35 fn short_label(&self) -> Option<String> {
36 if self.unsafe_token().is_some() {
37 short_label_from_node(self, "unsafe trait ")
38 } else {
39 short_label_from_node(self, "trait ")
40 }
41 }
42}
43
44impl ShortLabel for ast::Module {
45 fn short_label(&self) -> Option<String> {
46 short_label_from_node(self, "mod ")
47 }
48}
49
50impl ShortLabel for ast::TypeAlias {
51 fn short_label(&self) -> Option<String> {
52 short_label_from_node(self, "type ")
53 }
54}
55
56impl ShortLabel for ast::Const {
57 fn short_label(&self) -> Option<String> {
58 short_label_from_ty(self, self.ty(), "const ")
59 }
60}
61
62impl ShortLabel for ast::Static {
63 fn short_label(&self) -> Option<String> {
64 short_label_from_ty(self, self.ty(), "static ")
65 }
66}
67
68impl ShortLabel for ast::RecordField {
69 fn short_label(&self) -> Option<String> {
70 short_label_from_ty(self, self.ty(), "")
71 }
72}
73
74impl ShortLabel for ast::Variant {
75 fn short_label(&self) -> Option<String> {
76 Some(self.name()?.text().to_string())
77 }
78}
79
80fn short_label_from_ty<T>(node: &T, ty: Option<ast::TypeRef>, prefix: &str) -> Option<String>
81where
82 T: NameOwner + VisibilityOwner,
83{
84 let mut buf = short_label_from_node(node, prefix)?;
85
86 if let Some(type_ref) = ty {
87 format_to!(buf, ": {}", type_ref.syntax());
88 }
89
90 Some(buf)
91}
92
93fn short_label_from_node<T>(node: &T, label: &str) -> Option<String>
94where
95 T: NameOwner + VisibilityOwner,
96{
97 let mut buf = node.visibility().map(|v| format!("{} ", v.syntax())).unwrap_or_default();
98 buf.push_str(label);
99 buf.push_str(node.name()?.text().as_str());
100 Some(buf)
101}
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs
deleted file mode 100644
index 043515f54..000000000
--- a/crates/ra_ide/src/expand_macro.rs
+++ /dev/null
@@ -1,283 +0,0 @@
1use hir::Semantics;
2use ra_ide_db::RootDatabase;
3use ra_syntax::{
4 algo::{find_node_at_offset, SyntaxRewriter},
5 ast, AstNode, NodeOrToken, SyntaxKind,
6 SyntaxKind::*,
7 SyntaxNode, WalkEvent, T,
8};
9
10use crate::FilePosition;
11
12pub struct ExpandedMacro {
13 pub name: String,
14 pub expansion: String,
15}
16
17// Feature: Expand Macro Recursively
18//
19// Shows the full macro expansion of the macro at current cursor.
20//
21// |===
22// | Edit