aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src')
-rw-r--r--crates/ra_ide/src/call_hierarchy.rs393
-rw-r--r--crates/ra_ide/src/call_info.rs742
-rw-r--r--crates/ra_ide/src/completion.rs211
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs654
-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.rs799
-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.rs422
-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.rs2387
-rw-r--r--crates/ra_ide/src/inlay_hints.rs922
-rw-r--r--crates/ra_ide/src/join_lines.rs750
-rw-r--r--crates/ra_ide/src/lib.rs534
-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.rs822
-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.rs401
-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
54 files changed, 0 insertions, 23391 deletions
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 9c33a5a43..000000000
--- a/crates/ra_ide/src/completion.rs
+++ /dev/null
@@ -1,211 +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;
21mod unstable_feature_descriptor;
22use ra_ide_db::RootDatabase;
23
24use crate::{
25 completion::{
26 completion_context::CompletionContext,
27 completion_item::{CompletionKind, Completions},
28 },
29 FilePosition,
30};
31
32//FIXME: cyclic imports caused by xtask generation, this should be better
33use crate::completion::{
34 complete_attribute::LintCompletion, unstable_feature_descriptor::UNSTABLE_FEATURE_DESCRIPTOR,
35};
36
37pub use crate::completion::{
38 completion_config::CompletionConfig,
39 completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
40};
41
42//FIXME: split the following feature into fine-grained features.
43
44// Feature: Magic Completions
45//
46// In addition to usual reference completion, rust-analyzer provides some ✨magic✨
47// completions as well:
48//
49// Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
50// is placed at the appropriate position. Even though `if` is easy to type, you
51// still want to complete it, to get ` { }` for free! `return` is inserted with a
52// space or `;` depending on the return type of the function.
53//
54// When completing a function call, `()` are automatically inserted. If a function
55// takes arguments, the cursor is positioned inside the parenthesis.
56//
57// There are postfix completions, which can be triggered by typing something like
58// `foo().if`. The word after `.` determines postfix completion. Possible variants are:
59//
60// - `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
61// - `expr.match` -> `match expr {}`
62// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
63// - `expr.ref` -> `&expr`
64// - `expr.refm` -> `&mut expr`
65// - `expr.not` -> `!expr`
66// - `expr.dbg` -> `dbg!(expr)`
67//
68// There also snippet completions:
69//
70// .Expressions
71// - `pd` -> `eprintln!(" = {:?}", );`
72// - `ppd` -> `eprintln!(" = {:#?}", );`
73//
74// .Items
75// - `tfn` -> `#[test] fn feature(){}`
76// - `tmod` ->
77// ```rust
78// #[cfg(test)]
79// mod tests {
80// use super::*;
81//
82// #[test]
83// fn test_name() {}
84// }
85// ```
86
87/// Main entry point for completion. We run completion as a two-phase process.
88///
89/// First, we look at the position and collect a so-called `CompletionContext.
90/// This is a somewhat messy process, because, during completion, syntax tree is
91/// incomplete and can look really weird.
92///
93/// Once the context is collected, we run a series of completion routines which
94/// look at the context and produce completion items. One subtlety about this
95/// phase is that completion engine should not filter by the substring which is
96/// already present, it should give all possible variants for the identifier at
97/// the caret. In other words, for
98///
99/// ```no-run
100/// fn f() {
101/// let foo = 92;
102/// let _ = bar<|>
103/// }
104/// ```
105///
106/// `foo` *should* be present among the completion variants. Filtering by
107/// identifier prefix/fuzzy match should be done higher in the stack, together
108/// with ordering of completions (currently this is done by the client).
109pub(crate) fn completions(
110 db: &RootDatabase,
111 config: &CompletionConfig,
112 position: FilePosition,
113) -> Option<Completions> {
114 let ctx = CompletionContext::new(db, position, config)?;
115
116 let mut acc = Completions::default();
117 complete_attribute::complete_attribute(&mut acc, &ctx);
118 complete_fn_param::complete_fn_param(&mut acc, &ctx);
119 complete_keyword::complete_expr_keyword(&mut acc, &ctx);
120 complete_keyword::complete_use_tree_keyword(&mut acc, &ctx);
121 complete_snippet::complete_expr_snippet(&mut acc, &ctx);
122 complete_snippet::complete_item_snippet(&mut acc, &ctx);
123 complete_qualified_path::complete_qualified_path(&mut acc, &ctx);
124 complete_unqualified_path::complete_unqualified_path(&mut acc, &ctx);
125 complete_dot::complete_dot(&mut acc, &ctx);
126 complete_record::complete_record(&mut acc, &ctx);
127 complete_pattern::complete_pattern(&mut acc, &ctx);
128 complete_postfix::complete_postfix(&mut acc, &ctx);
129 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
130 complete_trait_impl::complete_trait_impl(&mut acc, &ctx);
131
132 Some(acc)
133}
134
135#[cfg(test)]
136mod tests {
137 use crate::completion::completion_config::CompletionConfig;
138 use crate::mock_analysis::analysis_and_position;
139
140 struct DetailAndDocumentation<'a> {
141 detail: &'a str,
142 documentation: &'a str,
143 }
144
145 fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
146 let (analysis, position) = analysis_and_position(ra_fixture);
147 let config = CompletionConfig::default();
148 let completions = analysis.completions(&config, position).unwrap().unwrap();
149 for item in completions {
150 if item.detail() == Some(expected.detail) {
151 let opt = item.documentation();
152 let doc = opt.as_ref().map(|it| it.as_str());
153 assert_eq!(doc, Some(expected.documentation));
154 return;
155 }
156 }
157 panic!("completion detail not found: {}", expected.detail)
158 }
159
160 #[test]
161 fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
162 check_detail_and_documentation(
163 r#"
164 //- /lib.rs
165 macro_rules! bar {
166 () => {
167 struct Bar;
168 impl Bar {
169 #[doc = "Do the foo"]
170 fn foo(&self) {}
171 }
172 }
173 }
174
175 bar!();
176
177 fn foo() {
178 let bar = Bar;
179 bar.fo<|>;
180 }
181 "#,
182 DetailAndDocumentation { detail: "fn foo(&self)", documentation: "Do the foo" },
183 );
184 }
185
186 #[test]
187 fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
188 check_detail_and_documentation(
189 r#"
190 //- /lib.rs
191 macro_rules! bar {
192 () => {
193 struct Bar;
194 impl Bar {
195 /// Do the foo
196 fn foo(&self) {}
197 }
198 }
199 }
200
201 bar!();
202
203 fn foo() {
204 let bar = Bar;
205 bar.fo<|>;
206 }
207 "#,
208 DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" },
209 );
210 }
211}
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 f2782d4b9..000000000
--- a/crates/ra_ide/src/completion/complete_attribute.rs
+++ /dev/null
@@ -1,654 +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
14use crate::completion::UNSTABLE_FEATURE_DESCRIPTOR;
15
16pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
17 let attribute = ctx.attribute_under_caret.as_ref()?;
18 match (attribute.path(), attribute.token_tree()) {
19 (Some(path), Some(token_tree)) if path.to_string() == "derive" => {
20 complete_derive(acc, ctx, token_tree)
21 }
22 (Some(path), Some(token_tree)) if path.to_string() == "feature" => {
23 complete_lint(acc, ctx, token_tree, UNSTABLE_FEATURE_DESCRIPTOR);
24 }
25 (Some(path), Some(token_tree))
26 if ["allow", "warn", "deny", "forbid"]
27 .iter()
28 .any(|lint_level| lint_level == &path.to_string()) =>
29 {
30 complete_lint(acc, ctx, token_tree, DEFAULT_LINT_COMPLETIONS)
31 }
32 (_, Some(_token_tree)) => {}
33 _ => complete_attribute_start(acc, ctx, attribute),
34 }
35 Some(())
36}
37
38fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
39 for attr_completion in ATTRIBUTES {
40 let mut item = CompletionItem::new(
41 CompletionKind::Attribute,
42 ctx.source_range(),
43 attr_completion.label,
44 )
45 .kind(CompletionItemKind::Attribute);
46
47 if let Some(lookup) = attr_completion.lookup {
48 item = item.lookup_by(lookup);
49 }
50
51 match (attr_completion.snippet, ctx.config.snippet_cap) {
52 (Some(snippet), Some(cap)) => {
53 item = item.insert_snippet(cap, snippet);
54 }
55 _ => {}
56 }
57
58 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner {
59 acc.add(item);
60 }
61 }
62}
63
64struct AttrCompletion {
65 label: &'static str,
66 lookup: Option<&'static str>,
67 snippet: Option<&'static str>,
68 prefer_inner: bool,
69}
70
71impl AttrCompletion {
72 const fn prefer_inner(self) -> AttrCompletion {
73 AttrCompletion { prefer_inner: true, ..self }
74 }
75}
76
77const fn attr(
78 label: &'static str,
79 lookup: Option<&'static str>,
80 snippet: Option<&'static str>,
81) -> AttrCompletion {
82 AttrCompletion { label, lookup, snippet, prefer_inner: false }
83}
84
85const ATTRIBUTES: &[AttrCompletion] = &[
86 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
87 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
88 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
89 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
90 attr(r#"deprecated = "…""#, Some("deprecated"), Some(r#"deprecated = "${0:reason}""#)),
91 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
92 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
93 attr("feature(…)", Some("feature"), Some("feature(${0:lint})")).prefer_inner(),
94 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
95 // FIXME: resolve through macro resolution?
96 attr("global_allocator", None, None).prefer_inner(),
97 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
98 attr("inline(…)", Some("inline"), Some("inline(${0:lint})")),
99 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
100 attr("link", None, None),
101 attr("macro_export", None, None),
102 attr("macro_use", None, None),
103 attr(r#"must_use = "…""#, Some("must_use"), Some(r#"must_use = "${0:reason}""#)),
104 attr("no_mangle", None, None),
105 attr("no_std", None, None).prefer_inner(),
106 attr("non_exhaustive", None, None),
107 attr("panic_handler", None, None).prefer_inner(),
108 attr("path = \"…\"", Some("path"), Some("path =\"${0:path}\"")),
109 attr("proc_macro", None, None),
110 attr("proc_macro_attribute", None, None),
111 attr("proc_macro_derive(…)", Some("proc_macro_derive"), Some("proc_macro_derive(${0:Trait})")),
112 attr("recursion_limit = …", Some("recursion_limit"), Some("recursion_limit = ${0:128}"))
113 .prefer_inner(),
114 attr("repr(…)", Some("repr"), Some("repr(${0:C})")),
115 attr(
116 "should_panic(…)",
117 Some("should_panic"),
118 Some(r#"should_panic(expected = "${0:reason}")"#),
119 ),
120 attr(
121 r#"target_feature = "…""#,
122 Some("target_feature"),
123 Some("target_feature = \"${0:feature}\""),
124 ),
125 attr("test", None, None),
126 attr("used", None, None),
127 attr("warn(…)", Some("warn"), Some("warn(${0:lint})")),
128 attr(
129 r#"windows_subsystem = "…""#,
130 Some("windows_subsystem"),
131 Some(r#"windows_subsystem = "${0:subsystem}""#),
132 )
133 .prefer_inner(),
134];
135
136fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
137 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) {
138 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
139 .into_iter()
140 .filter(|completion| !existing_derives.contains(completion.label))
141 {
142 let mut label = derive_completion.label.to_owned();
143 for dependency in derive_completion
144 .dependencies
145 .into_iter()
146 .filter(|&&dependency| !existing_derives.contains(dependency))
147 {
148 label.push_str(", ");
149 label.push_str(dependency);
150 }
151 acc.add(
152 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label)
153 .kind(CompletionItemKind::Attribute),
154 );
155 }
156
157 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
158 acc.add(
159 CompletionItem::new(
160 CompletionKind::Attribute,
161 ctx.source_range(),
162 custom_derive_name,
163 )
164 .kind(CompletionItemKind::Attribute),
165 );
166 }
167 }
168}
169
170fn complete_lint(
171 acc: &mut Completions,
172 ctx: &CompletionContext,
173 derive_input: ast::TokenTree,
174 lints_completions: &[LintCompletion],
175) {
176 if let Ok(existing_lints) = parse_comma_sep_input(derive_input) {
177 for lint_completion in lints_completions
178 .into_iter()
179 .filter(|completion| !existing_lints.contains(completion.label))
180 {
181 acc.add(
182 CompletionItem::new(
183 CompletionKind::Attribute,
184 ctx.source_range(),
185 lint_completion.label,
186 )
187 .kind(CompletionItemKind::Attribute)
188 .detail(lint_completion.description),
189 );
190 }
191 }
192}
193
194fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
195 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
196 (Some(left_paren), Some(right_paren))
197 if left_paren.kind() == SyntaxKind::L_PAREN
198 && right_paren.kind() == SyntaxKind::R_PAREN =>
199 {
200 let mut input_derives = FxHashSet::default();
201 let mut current_derive = String::new();
202 for token in derive_input
203 .syntax()
204 .children_with_tokens()
205 .filter_map(|token| token.into_token())
206 .skip_while(|token| token != &left_paren)
207 .skip(1)
208 .take_while(|token| token != &right_paren)
209 {
210 if SyntaxKind::COMMA == token.kind() {
211 if !current_derive.is_empty() {
212 input_derives.insert(current_derive);
213 current_derive = String::new();
214 }
215 } else {
216 current_derive.push_str(token.to_string().trim());
217 }
218 }
219
220 if !current_derive.is_empty() {
221 input_derives.insert(current_derive);
222 }
223 Ok(input_derives)
224 }
225 _ => Err(()),
226 }
227}
228
229fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
230 let mut result = FxHashSet::default();
231 ctx.scope.process_all_names(&mut |name, scope_def| {
232 if let hir::ScopeDef::MacroDef(mac) = scope_def {
233 if mac.is_derive_macro() {
234 result.insert(name.to_string());
235 }
236 }
237 });
238 result
239}
240
241struct DeriveCompletion {
242 label: &'static str,
243 dependencies: &'static [&'static str],
244}
245
246/// Standard Rust derives and the information about their dependencies
247/// (the dependencies are needed so that the main derive don't break the compilation when added)
248#[rustfmt::skip]
249const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
250 DeriveCompletion { label: "Clone", dependencies: &[] },
251 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
252 DeriveCompletion { label: "Debug", dependencies: &[] },
253 DeriveCompletion { label: "Default", dependencies: &[] },
254 DeriveCompletion { label: "Hash", dependencies: &[] },
255 DeriveCompletion { label: "PartialEq", dependencies: &[] },
256 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
257 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
258 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
259];
260
261pub(crate) struct LintCompletion {
262 pub label: &'static str,
263 pub description: &'static str,
264}
265
266#[rustfmt::skip]
267const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
268 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"# },
269 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
270 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
271 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
272 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
273 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
274 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
275 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
276 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
277 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
278 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
279 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
280 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
281 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
282 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
283 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
284 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
285 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
286 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
287 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
288 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
289 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
290 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
291 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
292 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
293 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
294 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
295 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
296 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
297 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
298 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
299 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
300 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
301 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
302 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
303 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
304 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
305 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
306 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
307 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
308 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
309 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
310 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
311 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
312 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
313 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
314 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
315 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
316 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
317 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
318 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
319 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
320 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
321 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
322 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
323 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
324 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
325 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
326 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
327 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
328 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
329 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
330 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
331 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
332 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
333 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
334 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
335 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
336 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
337 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
338 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
339 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
340 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
341 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
342 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
343 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
344 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
345 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
346 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
347 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
348 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
349 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
350 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
351 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
352 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
353 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
354 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
355 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
356 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
357 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
358 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
359 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
360 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
361 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
362 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
363 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
364 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
365 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
366 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
367 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
368 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
369 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
370 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
371 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
372 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"# },
373 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
374 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
375 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
376 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
377 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
378 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
379 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
380 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
381 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
382 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
383];
384
385#[cfg(test)]
386mod tests {
387 use expect::{expect, Expect};
388
389 use crate::completion::{test_utils::completion_list, CompletionKind};
390
391 fn check(ra_fixture: &str, expect: Expect) {
392 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
393 expect.assert_eq(&actual);
394 }
395
396 #[test]
397 fn empty_derive_completion() {
398 check(
399 r#"
400#[derive(<|>)]
401struct Test {}
402 "#,
403 expect![[r#"
404 at Clone
405 at Copy, Clone
406 at Debug
407 at Default
408 at Eq, PartialEq
409 at Hash
410 at Ord, PartialOrd, Eq, PartialEq
411 at PartialEq
412 at PartialOrd, PartialEq
413 "#]],
414 );
415 }
416
417 #[test]
418 fn empty_lint_completion() {
419 check(
420 r#"#[allow(<|>)]"#,
421 expect![[r#"
422 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
423 at ambiguous_associated_items ambiguous associated items
424 at anonymous_parameters detects anonymous parameters
425 at arithmetic_overflow arithmetic operation overflows
426 at array_into_iter detects calling `into_iter` on arrays
427 at asm_sub_register using only a subset of a register for inline asm inputs
428 at bare_trait_objects suggest using `dyn Trait` for trait objects
429 at bindings_with_variant_name detects pattern bindings with the same name as one of the matched variants
430 at box_pointers use of owned (Box type) heap memory
431 at cenum_impl_drop_cast a C-like enum implementing Drop is cast
432 at clashing_extern_declarations detects when an extern fn has been declared with the same name but different types
433 at coherence_leak_check distinct impls distinguished only by the leak-check code
434 at conflicting_repr_hints conflicts between `#[repr(..)]` hints that were previously accepted and used in practice
435 at confusable_idents detects visually confusable pairs between identifiers
436 at const_err constant evaluation detected erroneous expression
437 at dead_code detect unused, unexported items
438 at deprecated detects use of deprecated items
439 at deprecated_in_future detects use of items that will be deprecated in a future version
440 at elided_lifetimes_in_paths hidden lifetime parameters in types are deprecated
441 at ellipsis_inclusive_range_patterns `...` range patterns are deprecated
442 at explicit_outlives_requirements outlives requirements can be inferred
443 at exported_private_dependencies public interface leaks type from a private dependency
444 at ill_formed_attribute_input ill-formed attribute inputs that were previously accepted and used in practice
445 at illegal_floating_point_literal_pattern floating-point literals cannot be used in patterns
446 at improper_ctypes proper use of libc types in foreign modules
447 at improper_ctypes_definitions proper use of libc types in foreign item definitions
448 at incomplete_features incomplete features that may function improperly in some or all cases
449 at incomplete_include trailing content in included file
450 at indirect_structural_match pattern with const indirectly referencing non-structural-match type
451 at inline_no_sanitize detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`
452 at intra_doc_link_resolution_failure failures in resolving intra-doc link targets
453 at invalid_codeblock_attributes codeblock attribute looks a lot like a known one
454 at invalid_type_param_default type parameter default erroneously allowed in invalid location
455 at invalid_value an invalid value is being created (such as a NULL reference)
456 at irrefutable_let_patterns detects irrefutable patterns in if-let and while-let statements
457 at keyword_idents detects edition keywords being used as an identifier
458 at late_bound_lifetime_arguments detects generic lifetime arguments in path segments with late bound lifetime parameters
459 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
460 at macro_use_extern_crate the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system
461 at meta_variable_misuse possible meta-variable misuse at macro definition
462 at missing_copy_implementations detects potentially-forgotten implementations of `Copy`
463 at missing_crate_level_docs detects crates with no crate-level documentation
464 at missing_debug_implementations detects missing implementations of Debug
465 at missing_doc_code_examples detects publicly-exported items without code samples in their documentation
466 at missing_docs detects missing documentation for public members
467 at missing_fragment_specifier detects missing fragment specifiers in unused `macro_rules!` patterns
468 at mixed_script_confusables detects Unicode scripts whose mixed script confusables codepoints are solely used
469 at mutable_borrow_reservation_conflict reservation of a two-phased borrow conflicts with other shared borrows
470 at mutable_transmutes mutating transmuted &mut T from &T may cause undefined behavior
471 at no_mangle_const_items const items will not have their symbols exported
472 at no_mangle_generic_items generic items must be mangled
473 at non_ascii_idents detects non-ASCII identifiers
474 at non_camel_case_types types, variants, traits and type parameters should have camel case names
475 at non_shorthand_field_patterns using `Struct { x: x }` instead of `Struct { x }` in a pattern
476 at non_snake_case variables, methods, functions, lifetime parameters and modules should have snake case names
477 at non_upper_case_globals static constants should have uppercase identifiers
478 at order_dependent_trait_objects trait-object types were treated as different depending on marker-trait order
479 at overflowing_literals literal out of range for its type
480 at overlapping_patterns detects overlapping patterns
481 at path_statements path statements with no effect
482 at patterns_in_fns_without_body patterns in functions without body were erroneously allowed
483 at private_doc_tests detects code samples in docs of private items not documented by rustdoc
484 at private_in_public detect private items in public interfaces not caught by the old implementation
485 at proc_macro_derive_resolution_fallback detects proc macro derives using inaccessible names from parent modules
486 at pub_use_of_private_extern_crate detect public re-exports of private extern crates
487 at redundant_semicolons detects unnecessary trailing semicolons
488 at renamed_and_removed_lints lints that have been renamed or removed
489 at safe_packed_borrows safe borrows of fields of packed structs were erroneously allowed
490 at single_use_lifetimes detects lifetime parameters that are only used once
491 at soft_unstable a feature gate that doesn't break dependent crates
492 at stable_features stable features found in `#[feature]` directive
493 at trivial_bounds these bounds don't depend on an type parameters
494 at trivial_casts detects trivial casts which could be removed
495 at trivial_numeric_casts detects trivial casts of numeric types which could be removed
496 at type_alias_bounds bounds in type aliases are not enforced
497 at tyvar_behind_raw_pointer raw pointer to an inference variable
498 at unaligned_references detects unaligned references to fields of packed structs
499 at uncommon_codepoints detects uncommon Unicode codepoints in identifiers
500 at unconditional_panic operation will cause a panic at runtime
501 at unconditional_recursion functions that cannot return without calling themselves
502 at unknown_crate_types unknown crate type found in `#[crate_type]` directive
503 at unknown_lints unrecognized lint attribute
504 at unnameable_test_items detects an item that cannot be named being marked as `#[test_case]`
505 at unreachable_code detects unreachable code paths
506 at unreachable_patterns detects unreachable patterns
507 at unreachable_pub `pub` items not reachable from crate root
508 at unsafe_code usage of `unsafe` code
509 at unsafe_op_in_unsafe_fn unsafe operations in unsafe functions without an explicit unsafe block are deprecated
510 at unstable_features enabling unstable features (deprecated. do not use)
511 at unstable_name_collisions detects name collision with an existing but unstable method
512 at unused_allocation detects unnecessary allocations that can be eliminated
513 at unused_assignments detect assignments that will never be read
514 at unused_attributes detects attributes that were not used by the compiler
515 at unused_braces unnecessary braces around an expression
516 at unused_comparisons comparisons made useless by limits of the types involved
517 at unused_crate_dependencies crate dependencies that are never used
518 at unused_doc_comments detects doc comments that aren't used by rustdoc
519 at unused_extern_crates extern crates that are never used
520 at unused_features unused features found in crate-level `#[feature]` directives
521 at unused_import_braces unnecessary braces around an imported item
522 at unused_imports imports that are never used
523 at unused_labels detects labels that are never used
524 at unused_lifetimes detects lifetime parameters that are never used
525 at unused_macros detects macros that were not used
526 at unused_must_use unused result of a type flagged as `#[must_use]`
527 at unused_mut detect mut variables which don't need to be mutable
528 at unused_parens `if`, `match`, `while` and `return` do not need parentheses
529 at unused_qualifications detects unnecessarily qualified names
530 at unused_results unused result of an expression in a statement
531 at unused_unsafe unnecessary use of an `unsafe` block
532 at unused_variables detect variables which are not used in any way
533 at variant_size_differences detects enums with widely varying variant sizes
534 at warnings mass-change the level for lints which produce warnings
535 at where_clauses_object_safety checks the object safety of where clauses
536 at while_true suggest using `loop { }` instead of `while true { }`
537 "#]],
538 )
539 }
540
541 #[test]
542 fn no_completion_for_incorrect_derive() {
543 check(
544 r#"
545#[derive{<|>)]
546struct Test {}
547"#,
548 expect![[r#""#]],
549 )
550 }
551
552 #[test]
553 fn derive_with_input_completion() {
554 check(
555 r#"
556#[derive(serde::Serialize, PartialEq, <|>)]
557struct Test {}
558"#,
559 expect![[r#"
560 at Clone
561 at Copy, Clone
562 at Debug
563 at Default
564 at Eq
565 at Hash
566 at Ord, PartialOrd, Eq
567 at PartialOrd
568 "#]],
569 )
570 }
571
572 #[test]
573 fn test_attribute_completion() {
574 check(
575 r#"#[<|>]"#,
576 expect![[r#"
577 at allow(…)
578 at cfg(…)
579 at cfg_attr(…)
580 at deny(…)
581 at deprecated = "…"
582 at derive(…)
583 at doc = "…"
584 at forbid(…)
585 at ignore = "…"
586 at inline(…)
587 at link
588 at link_name = "…"
589 at macro_export
590 at macro_use
591 at must_use = "…"
592 at no_mangle
593 at non_exhaustive
594 at path = "…"
595 at proc_macro
596 at proc_macro_attribute
597 at proc_macro_derive(…)
598 at repr(…)
599 at should_panic(…)
600 at target_feature = "…"
601 at test
602 at used
603 at warn(…)
604 "#]],
605 )
606 }
607
608 #[test]
609 fn test_attribute_completion_inside_nested_attr() {
610 check(r#"#[cfg(<|>)]"#, expect![[]])
611 }
612
613 #[test]
614 fn test_inner_attribute_completion() {
615 check(
616 r"#![<|>]",
617 expect![[r#"
618 at allow(…)
619 at cfg(…)
620 at cfg_attr(…)
621 at deny(…)
622 at deprecated = "…"
623 at derive(…)
624 at doc = "…"
625 at feature(…)
626 at forbid(…)
627 at global_allocator
628 at ignore = "…"
629 at inline(…)
630 at link
631 at link_name = "…"
632 at macro_export
633 at macro_use
634 at must_use = "…"
635 at no_mangle
636 at no_std
637 at non_exhaustive
638 at panic_handler
639 at path = "…"
640 at proc_macro
641 at proc_macro_attribute
642 at proc_macro_derive(…)
643 at recursion_limit = …
644 at repr(…)
645 at should_panic(…)
646 at target_feature = "…"
647 at test
648 at used
649 at warn(…)
650 at windows_subsystem = "…"
651 "#]],
652 );
653 }
654}
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