aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/libeditor/src/completion.rs222
-rw-r--r--crates/libsyntax2/src/ast/generated.rs33
-rw-r--r--crates/libsyntax2/src/ast/mod.rs6
-rw-r--r--crates/libsyntax2/src/grammar.ron11
-rw-r--r--crates/libsyntax2/src/grammar/patterns.rs2
-rw-r--r--crates/libsyntax2/src/grammar/types.rs5
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0018_incomplete_fn.txt245
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0021_incomplete_param.rs2
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0021_incomplete_param.txt34
-rw-r--r--crates/server/src/main_loop/handlers.rs3
10 files changed, 326 insertions, 237 deletions
diff --git a/crates/libeditor/src/completion.rs b/crates/libeditor/src/completion.rs
index b296f6fd5..6c3775127 100644
--- a/crates/libeditor/src/completion.rs
+++ b/crates/libeditor/src/completion.rs
@@ -1,11 +1,11 @@
1use std::collections::HashSet; 1use std::collections::{HashSet, HashMap};
2 2
3use libsyntax2::{ 3use libsyntax2::{
4 File, TextUnit, AstNode, SyntaxKind::*, 4 File, TextUnit, AstNode, SyntaxNodeRef, SyntaxKind::*,
5 ast::{self, LoopBodyOwner}, 5 ast::{self, LoopBodyOwner},
6 algo::{ 6 algo::{
7 ancestors, 7 ancestors,
8 visit::{visitor, Visitor}, 8 visit::{visitor, Visitor, visitor_ctx, VisitorCtx},
9 }, 9 },
10 text_utils::is_subrange, 10 text_utils::is_subrange,
11}; 11};
@@ -17,7 +17,11 @@ use {
17 17
18#[derive(Debug)] 18#[derive(Debug)]
19pub struct CompletionItem { 19pub struct CompletionItem {
20 pub name: String, 20 /// What user sees in pop-up
21 pub label: String,
22 /// What string is used for filtering, defaults to label
23 pub lookup: Option<String>,
24 /// What is inserted, defaults to label
21 pub snippet: Option<String> 25 pub snippet: Option<String>
22} 26}
23 27
@@ -27,40 +31,89 @@ pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionI
27 let edit = AtomEdit::insert(offset, "intellijRulezz".to_string()); 31 let edit = AtomEdit::insert(offset, "intellijRulezz".to_string());
28 file.reparse(&edit) 32 file.reparse(&edit)
29 }; 33 };
30 let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), offset)?; 34 let mut has_completions = false;
31 if !is_single_segment(name_ref) { 35 let mut res = Vec::new();
32 return None; 36 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), offset) {
37 has_completions = true;
38 complete_name_ref(&file, name_ref, &mut res)
39 }
40 if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), offset) {
41 has_completions = true;
42 complete_name(&file, name, &mut res)
43 }
44 if has_completions {
45 Some(res)
46 } else {
47 None
33 } 48 }
49}
34 50
35 let mut res = Vec::new(); 51fn complete_name_ref(file: &File, name_ref: ast::NameRef, acc: &mut Vec<CompletionItem>) {
52 if !is_node::<ast::Path>(name_ref.syntax()) {
53 return;
54 }
36 if let Some(fn_def) = ancestors(name_ref.syntax()).filter_map(ast::FnDef::cast).next() { 55 if let Some(fn_def) = ancestors(name_ref.syntax()).filter_map(ast::FnDef::cast).next() {
37 complete_expr_keywords(&file, fn_def, name_ref, &mut res); 56 complete_expr_keywords(&file, fn_def, name_ref, acc);
38 let scopes = FnScopes::new(fn_def); 57 let scopes = FnScopes::new(fn_def);
39 complete_fn(name_ref, &scopes, &mut res); 58 complete_fn(name_ref, &scopes, acc);
40 } 59 }
41 if let Some(root) = ancestors(name_ref.syntax()).filter_map(ast::Root::cast).next() { 60 if let Some(root) = ancestors(name_ref.syntax()).filter_map(ast::Root::cast).next() {
42 let scope = ModuleScope::new(root); 61 let scope = ModuleScope::new(root);
43 res.extend( 62 acc.extend(
44 scope.entries().iter() 63 scope.entries().iter()
45 .filter(|entry| entry.syntax() != name_ref.syntax()) 64 .filter(|entry| entry.syntax() != name_ref.syntax())
46 .map(|entry| CompletionItem { 65 .map(|entry| CompletionItem {
47 name: entry.name().to_string(), 66 label: entry.name().to_string(),
67 lookup: None,
48 snippet: None, 68 snippet: None,
49 }) 69 })
50 ); 70 );
51 } 71 }
52 Some(res)
53} 72}
54 73
55fn is_single_segment(name_ref: ast::NameRef) -> bool { 74fn complete_name(_file: &File, name: ast::Name, acc: &mut Vec<CompletionItem>) {
56 match ancestors(name_ref.syntax()).filter_map(ast::Path::cast).next() { 75 if !is_node::<ast::Param>(name.syntax()) {
76 return;
77 }
78
79 let mut params = HashMap::new();
80 for node in ancestors(name.syntax()) {
81 let _ = visitor_ctx(&mut params)
82 .visit::<ast::Root, _>(process)
83 .accept(node);
84 }
85 params.into_iter()
86 .filter_map(|(label, (count, param))| {
87 let lookup = param.pat()?.syntax().text().to_string();
88 if count < 2 { None } else { Some((label, lookup)) }
89 })
90 .for_each(|(label, lookup)| {
91 acc.push(CompletionItem {
92 label, lookup: Some(lookup), snippet: None
93 })
94 });
95
96 fn process<'a, N: ast::FnDefOwner<'a>>(node: N, params: &mut HashMap<String, (u32, ast::Param<'a>)>) {
97 node.functions()
98 .filter_map(|it| it.param_list())
99 .flat_map(|it| it.params())
100 .for_each(|param| {
101 let text = param.syntax().text().to_string();
102 params.entry(text)
103 .or_insert((0, param))
104 .0 += 1;
105 })
106 }
107}
108
109fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool {
110 match ancestors(node).filter_map(N::cast).next() {
57 None => false, 111 None => false,
58 Some(path) => { 112 Some(n) => n.syntax().range() == node.range(),
59 path.syntax().range() == name_ref.syntax().range()
60 }
61 } 113 }
62} 114}
63 115
116
64fn complete_expr_keywords(file: &File, fn_def: ast::FnDef, name_ref: ast::NameRef, acc: &mut Vec<CompletionItem>) { 117fn complete_expr_keywords(file: &File, fn_def: ast::FnDef, name_ref: ast::NameRef, acc: &mut Vec<CompletionItem>) {
65 acc.push(keyword("if", "if $0 {}")); 118 acc.push(keyword("if", "if $0 {}"));
66 acc.push(keyword("match", "match $0 {}")); 119 acc.push(keyword("match", "match $0 {}"));
@@ -127,7 +180,8 @@ fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<Complet
127 180
128fn keyword(kw: &str, snip: &str) -> CompletionItem { 181fn keyword(kw: &str, snip: &str) -> CompletionItem {
129 CompletionItem { 182 CompletionItem {
130 name: kw.to_string(), 183 label: kw.to_string(),
184 lookup: None,
131 snippet: Some(snip.to_string()), 185 snippet: Some(snip.to_string()),
132 } 186 }
133} 187}
@@ -139,13 +193,15 @@ fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<Completi
139 .flat_map(|scope| scopes.entries(scope).iter()) 193 .flat_map(|scope| scopes.entries(scope).iter())
140 .filter(|entry| shadowed.insert(entry.name())) 194 .filter(|entry| shadowed.insert(entry.name()))
141 .map(|entry| CompletionItem { 195 .map(|entry| CompletionItem {
142 name: entry.name().to_string(), 196 label: entry.name().to_string(),
197 lookup: None,
143 snippet: None, 198 snippet: None,
144 }) 199 })
145 ); 200 );
146 if scopes.self_param.is_some() { 201 if scopes.self_param.is_some() {
147 acc.push(CompletionItem { 202 acc.push(CompletionItem {
148 name: "self".to_string(), 203 label: "self".to_string(),
204 lookup: None,
149 snippet: None, 205 snippet: None,
150 }) 206 })
151 } 207 }
@@ -186,9 +242,9 @@ mod tests {
186 1 + <|>; 242 1 + <|>;
187 let z = (); 243 let z = ();
188 } 244 }
189 ", r#"[CompletionItem { name: "y", snippet: None }, 245 ", r#"[CompletionItem { label: "y", lookup: None, snippet: None },
190 CompletionItem { name: "x", snippet: None }, 246 CompletionItem { label: "x", lookup: None, snippet: None },
191 CompletionItem { name: "quux", snippet: None }]"#); 247 CompletionItem { label: "quux", lookup: None, snippet: None }]"#);
192 } 248 }
193 249
194 #[test] 250 #[test]
@@ -203,9 +259,9 @@ mod tests {
203 1 + <|> 259 1 + <|>
204 } 260 }
205 } 261 }
206 ", r#"[CompletionItem { name: "b", snippet: None }, 262 ", r#"[CompletionItem { label: "b", lookup: None, snippet: None },
207 CompletionItem { name: "a", snippet: None }, 263 CompletionItem { label: "a", lookup: None, snippet: None },
208 CompletionItem { name: "quux", snippet: None }]"#); 264 CompletionItem { label: "quux", lookup: None, snippet: None }]"#);
209 } 265 }
210 266
211 #[test] 267 #[test]
@@ -216,8 +272,8 @@ mod tests {
216 <|> 272 <|>
217 } 273 }
218 } 274 }
219 ", r#"[CompletionItem { name: "x", snippet: None }, 275 ", r#"[CompletionItem { label: "x", lookup: None, snippet: None },
220 CompletionItem { name: "quux", snippet: None }]"#); 276 CompletionItem { label: "quux", lookup: None, snippet: None }]"#);
221 } 277 }
222 278
223 #[test] 279 #[test]
@@ -228,9 +284,9 @@ mod tests {
228 fn quux() { 284 fn quux() {
229 <|> 285 <|>
230 } 286 }
231 ", r#"[CompletionItem { name: "Foo", snippet: None }, 287 ", r#"[CompletionItem { label: "Foo", lookup: None, snippet: None },
232 CompletionItem { name: "Baz", snippet: None }, 288 CompletionItem { label: "Baz", lookup: None, snippet: None },
233 CompletionItem { name: "quux", snippet: None }]"#); 289 CompletionItem { label: "quux", lookup: None, snippet: None }]"#);
234 } 290 }
235 291
236 #[test] 292 #[test]
@@ -245,8 +301,8 @@ mod tests {
245 check_scope_completion(r" 301 check_scope_completion(r"
246 struct Foo; 302 struct Foo;
247 fn x() -> <|> 303 fn x() -> <|>
248 ", r#"[CompletionItem { name: "Foo", snippet: None }, 304 ", r#"[CompletionItem { label: "Foo", lookup: None, snippet: None },
249 CompletionItem { name: "x", snippet: None }]"#) 305 CompletionItem { label: "x", lookup: None, snippet: None }]"#)
250 } 306 }
251 307
252 #[test] 308 #[test]
@@ -259,15 +315,15 @@ mod tests {
259 <|> 315 <|>
260 } 316 }
261 } 317 }
262 ", r#"[CompletionItem { name: "bar", snippet: None }, 318 ", r#"[CompletionItem { label: "bar", lookup: None, snippet: None },
263 CompletionItem { name: "foo", snippet: None }]"#) 319 CompletionItem { label: "foo", lookup: None, snippet: None }]"#)
264 } 320 }
265 321
266 #[test] 322 #[test]
267 fn test_complete_self() { 323 fn test_complete_self() {
268 check_scope_completion(r" 324 check_scope_completion(r"
269 impl S { fn foo(&self) { <|> } } 325 impl S { fn foo(&self) { <|> } }
270 ", r#"[CompletionItem { name: "self", snippet: None }]"#) 326 ", r#"[CompletionItem { label: "self", lookup: None, snippet: None }]"#)
271 } 327 }
272 328
273 #[test] 329 #[test]
@@ -276,11 +332,11 @@ mod tests {
276 fn quux() { 332 fn quux() {
277 <|> 333 <|>
278 } 334 }
279 ", r#"[CompletionItem { name: "if", snippet: Some("if $0 {}") }, 335 ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
280 CompletionItem { name: "match", snippet: Some("match $0 {}") }, 336 CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
281 CompletionItem { name: "while", snippet: Some("while $0 {}") }, 337 CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
282 CompletionItem { name: "loop", snippet: Some("loop {$0}") }, 338 CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
283 CompletionItem { name: "return", snippet: Some("return") }]"#); 339 CompletionItem { label: "return", lookup: None, snippet: Some("return") }]"#);
284 } 340 }
285 341
286 #[test] 342 #[test]
@@ -291,13 +347,13 @@ mod tests {
291 () 347 ()
292 } <|> 348 } <|>
293 } 349 }
294 ", r#"[CompletionItem { name: "if", snippet: Some("if $0 {}") }, 350 ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
295 CompletionItem { name: "match", snippet: Some("match $0 {}") }, 351 CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
296 CompletionItem { name: "while", snippet: Some("while $0 {}") }, 352 CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
297 CompletionItem { name: "loop", snippet: Some("loop {$0}") }, 353 CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
298 CompletionItem { name: "else", snippet: Some("else {$0}") }, 354 CompletionItem { label: "else", lookup: None, snippet: Some("else {$0}") },
299 CompletionItem { name: "else if", snippet: Some("else if $0 {}") }, 355 CompletionItem { label: "else if", lookup: None, snippet: Some("else if $0 {}") },
300 CompletionItem { name: "return", snippet: Some("return") }]"#); 356 CompletionItem { label: "return", lookup: None, snippet: Some("return") }]"#);
301 } 357 }
302 358
303 #[test] 359 #[test]
@@ -307,21 +363,21 @@ mod tests {
307 <|> 363 <|>
308 92 364 92
309 } 365 }
310 ", r#"[CompletionItem { name: "if", snippet: Some("if $0 {}") }, 366 ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
311 CompletionItem { name: "match", snippet: Some("match $0 {}") }, 367 CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
312 CompletionItem { name: "while", snippet: Some("while $0 {}") }, 368 CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
313 CompletionItem { name: "loop", snippet: Some("loop {$0}") }, 369 CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
314 CompletionItem { name: "return", snippet: Some("return $0;") }]"#); 370 CompletionItem { label: "return", lookup: None, snippet: Some("return $0;") }]"#);
315 check_snippet_completion(r" 371 check_snippet_completion(r"
316 fn quux() { 372 fn quux() {
317 <|> 373 <|>
318 92 374 92
319 } 375 }
320 ", r#"[CompletionItem { name: "if", snippet: Some("if $0 {}") }, 376 ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
321 CompletionItem { name: "match", snippet: Some("match $0 {}") }, 377 CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
322 CompletionItem { name: "while", snippet: Some("while $0 {}") }, 378 CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
323 CompletionItem { name: "loop", snippet: Some("loop {$0}") }, 379 CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
324 CompletionItem { name: "return", snippet: Some("return;") }]"#); 380 CompletionItem { label: "return", lookup: None, snippet: Some("return;") }]"#);
325 } 381 }
326 382
327 #[test] 383 #[test]
@@ -332,11 +388,11 @@ mod tests {
332 () => <|> 388 () => <|>
333 } 389 }
334 } 390 }
335 ", r#"[CompletionItem { name: "if", snippet: Some("if $0 {}") }, 391 ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
336 CompletionItem { name: "match", snippet: Some("match $0 {}") }, 392 CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
337 CompletionItem { name: "while", snippet: Some("while $0 {}") }, 393 CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
338 CompletionItem { name: "loop", snippet: Some("loop {$0}") }, 394 CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
339 CompletionItem { name: "return", snippet: Some("return $0") }]"#); 395 CompletionItem { label: "return", lookup: None, snippet: Some("return $0") }]"#);
340 } 396 }
341 397
342 #[test] 398 #[test]
@@ -345,21 +401,35 @@ mod tests {
345 fn quux() -> i32 { 401 fn quux() -> i32 {
346 loop { <|> } 402 loop { <|> }
347 } 403 }
348 ", r#"[CompletionItem { name: "if", snippet: Some("if $0 {}") }, 404 ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
349 CompletionItem { name: "match", snippet: Some("match $0 {}") }, 405 CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
350 CompletionItem { name: "while", snippet: Some("while $0 {}") }, 406 CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
351 CompletionItem { name: "loop", snippet: Some("loop {$0}") }, 407 CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
352 CompletionItem { name: "continue", snippet: Some("continue") }, 408 CompletionItem { label: "continue", lookup: None, snippet: Some("continue") },
353 CompletionItem { name: "break", snippet: Some("break") }, 409 CompletionItem { label: "break", lookup: None, snippet: Some("break") },
354 CompletionItem { name: "return", snippet: Some("return $0") }]"#); 410 CompletionItem { label: "return", lookup: None, snippet: Some("return $0") }]"#);
355 check_snippet_completion(r" 411 check_snippet_completion(r"
356 fn quux() -> i32 { 412 fn quux() -> i32 {
357 loop { || { <|> } } 413 loop { || { <|> } }
358 } 414 }
359 ", r#"[CompletionItem { name: "if", snippet: Some("if $0 {}") }, 415 ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
360 CompletionItem { name: "match", snippet: Some("match $0 {}") }, 416 CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
361 CompletionItem { name: "while", snippet: Some("while $0 {}") }, 417 CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
362 CompletionItem { name: "loop", snippet: Some("loop {$0}") }, 418 CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
363 CompletionItem { name: "return", snippet: Some("return $0") }]"#); 419 CompletionItem { label: "return", lookup: None, snippet: Some("return $0") }]"#);
420 }
421
422 #[test]
423 fn test_param_completion() {
424 check_scope_completion(r"
425 fn foo(file_id: FileId) {}
426 fn bar(file_id: FileId) {}
427 fn baz(file<|>) {}
428 ", r#"[CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#);
429 check_scope_completion(r"
430 fn foo(file_id: FileId) {}
431 fn bar(file_id: FileId) {}
432 fn baz(file<|>, x: i32) {}
433 ", r#"[CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#);
364 } 434 }
365} 435}
diff --git a/crates/libsyntax2/src/ast/generated.rs b/crates/libsyntax2/src/ast/generated.rs
index 11306a835..4a57837df 100644
--- a/crates/libsyntax2/src/ast/generated.rs
+++ b/crates/libsyntax2/src/ast/generated.rs
@@ -682,6 +682,28 @@ impl<'a> AstNode<'a> for IndexExpr<'a> {
682 682
683impl<'a> IndexExpr<'a> {} 683impl<'a> IndexExpr<'a> {}
684 684
685// ItemList
686#[derive(Debug, Clone, Copy)]
687pub struct ItemList<'a> {
688 syntax: SyntaxNodeRef<'a>,
689}
690
691impl<'a> AstNode<'a> for ItemList<'a> {
692 fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
693 match syntax.kind() {
694 ITEM_LIST => Some(ItemList { syntax }),
695 _ => None,
696 }
697 }
698 fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
699}
700
701impl<'a> ItemList<'a> {
702 pub fn items(self) -> impl Iterator<Item = ModuleItem<'a>> + 'a {
703 super::children(self)
704 }
705}
706
685// Label 707// Label
686#[derive(Debug, Clone, Copy)] 708#[derive(Debug, Clone, Copy)]
687pub struct Label<'a> { 709pub struct Label<'a> {
@@ -956,9 +978,9 @@ impl<'a> AstNode<'a> for Module<'a> {
956 978
957impl<'a> ast::NameOwner<'a> for Module<'a> {} 979impl<'a> ast::NameOwner<'a> for Module<'a> {}
958impl<'a> ast::AttrsOwner<'a> for Module<'a> {} 980impl<'a> ast::AttrsOwner<'a> for Module<'a> {}
959impl<'a> Module<'a> { 981impl<'a> ast::FnDefOwner<'a> for Module<'a> {}
960 pub fn items(self) -> impl Iterator<Item = ModuleItem<'a>> + 'a { 982impl<'a> Module<'a> {pub fn item_list(self) -> Option<ItemList<'a>> {
961 super::children(self) 983 super::child_opt(self)
962 } 984 }
963} 985}
964 986
@@ -1593,15 +1615,12 @@ impl<'a> AstNode<'a> for Root<'a> {
1593 fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } 1615 fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
1594} 1616}
1595 1617
1618impl<'a> ast::FnDefOwner<'a> for Root<'a> {}
1596impl<'a> Root<'a> { 1619impl<'a> Root<'a> {
1597 pub fn items(self) -> impl Iterator<Item = ModuleItem<'a>> + 'a { 1620 pub fn items(self) -> impl Iterator<Item = ModuleItem<'a>> + 'a {
1598 super::children(self) 1621 super::children(self)
1599 } 1622 }
1600 1623
1601 pub fn functions(self) -> impl Iterator<Item = FnDef<'a>> + 'a {
1602 super::children(self)
1603 }
1604
1605 pub fn modules(self) -> impl Iterator<Item = Module<'a>> + 'a { 1624 pub fn modules(self) -> impl Iterator<Item = Module<'a>> + 'a {
1606 super::children(self) 1625 super::children(self)
1607 } 1626 }
diff --git a/crates/libsyntax2/src/ast/mod.rs b/crates/libsyntax2/src/ast/mod.rs
index 274996171..881f380f3 100644
--- a/crates/libsyntax2/src/ast/mod.rs
+++ b/crates/libsyntax2/src/ast/mod.rs
@@ -32,6 +32,12 @@ pub trait ArgListOwner<'a>: AstNode<'a> {
32 } 32 }
33} 33}
34 34
35pub trait FnDefOwner<'a>: AstNode<'a> {
36 fn functions(self) -> Box<Iterator<Item=FnDef<'a>> + 'a> {
37 Box::new(children(self))
38 }
39}
40
35pub trait TypeParamsOwner<'a>: AstNode<'a> { 41pub trait TypeParamsOwner<'a>: AstNode<'a> {
36 fn type_param_list(self) -> Option<TypeParamList<'a>> { 42 fn type_param_list(self) -> Option<TypeParamList<'a>> {
37 child_opt(self) 43 child_opt(self)
diff --git a/crates/libsyntax2/src/grammar.ron b/crates/libsyntax2/src/grammar.ron
index 683623a5d..8a2b780f0 100644
--- a/crates/libsyntax2/src/grammar.ron
+++ b/crates/libsyntax2/src/grammar.ron
@@ -238,9 +238,9 @@ Grammar(
238 ], 238 ],
239 ast: { 239 ast: {
240 "Root": ( 240 "Root": (
241 traits: [ "FnDefOwner" ],
241 collections: [ 242 collections: [
242 ["items", "ModuleItem"], 243 ["items", "ModuleItem"],
243 ["functions", "FnDef"],
244 ["modules", "Module"], 244 ["modules", "Module"],
245 ] 245 ]
246 ), 246 ),
@@ -271,10 +271,11 @@ Grammar(
271 ] ), 271 ] ),
272 "TraitDef": ( traits: ["NameOwner", "AttrsOwner"] ), 272 "TraitDef": ( traits: ["NameOwner", "AttrsOwner"] ),
273 "Module": ( 273 "Module": (
274 traits: ["NameOwner", "AttrsOwner"], 274 traits: ["NameOwner", "AttrsOwner", "FnDefOwner" ],
275 collections: [ 275 options: [ "ItemList" ]
276 ["items", "ModuleItem"] 276 ),
277 ] 277 "ItemList": (
278 collections: [ ["items", "ModuleItem"] ]
278 ), 279 ),
279 "ConstDef": ( traits: [ 280 "ConstDef": ( traits: [
280 "NameOwner", 281 "NameOwner",
diff --git a/crates/libsyntax2/src/grammar/patterns.rs b/crates/libsyntax2/src/grammar/patterns.rs
index 065570b99..aa20ae8e4 100644
--- a/crates/libsyntax2/src/grammar/patterns.rs
+++ b/crates/libsyntax2/src/grammar/patterns.rs
@@ -23,7 +23,7 @@ pub(super) fn pattern(p: &mut Parser) {
23} 23}
24 24
25const PAT_RECOVERY_SET: TokenSet = 25const PAT_RECOVERY_SET: TokenSet =
26 token_set![LET_KW, IF_KW, WHILE_KW, LOOP_KW, MATCH_KW]; 26 token_set![LET_KW, IF_KW, WHILE_KW, LOOP_KW, MATCH_KW, R_PAREN, COMMA];
27 27
28 28
29fn atom_pat(p: &mut Parser) -> Option<CompletedMarker> { 29fn atom_pat(p: &mut Parser) -> Option<CompletedMarker> {
diff --git a/crates/libsyntax2/src/grammar/types.rs b/crates/libsyntax2/src/grammar/types.rs
index 89030e66c..a52355b50 100644
--- a/crates/libsyntax2/src/grammar/types.rs
+++ b/crates/libsyntax2/src/grammar/types.rs
@@ -8,6 +8,9 @@ pub(super) const TYPE_FIRST: TokenSet =
8 paths::PATH_FIRST, 8 paths::PATH_FIRST,
9 ]; 9 ];
10 10
11const TYPE_RECOVERY_SET: TokenSet =
12 token_set![R_PAREN, COMMA];
13
11pub(super) fn type_(p: &mut Parser) { 14pub(super) fn type_(p: &mut Parser) {
12 match p.current() { 15 match p.current() {
13 L_PAREN => paren_or_tuple_type(p), 16 L_PAREN => paren_or_tuple_type(p),
@@ -23,7 +26,7 @@ pub(super) fn type_(p: &mut Parser) {
23 L_ANGLE => path_type(p), 26 L_ANGLE => path_type(p),
24 _ if paths::is_path_start(p) => path_type(p), 27 _ if paths::is_path_start(p) => path_type(p),
25 _ => { 28 _ => {
26 p.err_and_bump("expected type"); 29 p.err_recover("expected type", TYPE_RECOVERY_SET);
27 } 30 }
28 } 31 }
29} 32}
diff --git a/crates/libsyntax2/tests/data/parser/err/0018_incomplete_fn.txt b/crates/libsyntax2/tests/data/parser/err/0018_incomplete_fn.txt
index 58e39a341..c4d9f5e7e 100644
--- a/crates/libsyntax2/tests/data/parser/err/0018_incomplete_fn.txt
+++ b/crates/libsyntax2/tests/data/parser/err/0018_incomplete_fn.txt
@@ -11,168 +11,121 @@ ROOT@[0; 183)
11 ITEM_LIST@[14; 182) 11 ITEM_LIST@[14; 182)
12 L_CURLY@[14; 15) 12 L_CURLY@[14; 15)
13 WHITESPACE@[15; 20) 13 WHITESPACE@[15; 20)
14 FN_DEF@[20; 180) 14 FN_DEF@[20; 161)
15 FN_KW@[20; 22) 15 FN_KW@[20; 22)
16 WHITESPACE@[22; 23) 16 WHITESPACE@[22; 23)
17 NAME@[23; 32) 17 NAME@[23; 32)
18 IDENT@[23; 32) "new_scope" 18 IDENT@[23; 32) "new_scope"
19 PARAM_LIST@[32; 180) 19 PARAM_LIST@[32; 35)
20 L_PAREN@[32; 33) 20 L_PAREN@[32; 33)
21 PARAM@[33; 38) 21 PARAM@[33; 34)
22 REF_PAT@[33; 35) 22 REF_PAT@[33; 34)
23 AMP@[33; 34) 23 AMP@[33; 34)
24 err: `expected pattern` 24 err: `expected pattern`
25 ERROR@[34; 35) 25 err: `expected COLON`
26 R_PAREN@[34; 35) 26 err: `expected type`
27 err: `expected COLON` 27 R_PAREN@[34; 35)
28 WHITESPACE@[35; 36) 28 WHITESPACE@[35; 36)
29 err: `expected type` 29 RET_TYPE@[36; 46)
30 ERROR@[36; 38) 30 THIN_ARROW@[36; 38)
31 THIN_ARROW@[36; 38)
32 err: `expected COMMA`
33 WHITESPACE@[38; 39) 31 WHITESPACE@[38; 39)
34 PARAM@[39; 169) 32 PATH_TYPE@[39; 46)
35 STRUCT_PAT@[39; 161) 33 PATH@[39; 46)
36 PATH@[39; 46) 34 PATH_SEGMENT@[39; 46)
37 PATH_SEGMENT@[39; 46) 35 NAME_REF@[39; 46)
38 NAME_REF@[39; 46) 36 IDENT@[39; 46) "ScopeId"
39 IDENT@[39; 46) "ScopeId" 37 WHITESPACE@[46; 47)
40 WHITESPACE@[46; 47) 38 BLOCK@[47; 161)
41 FIELD_PAT_LIST@[47; 161) 39 L_CURLY@[47; 48)
42 L_CURLY@[47; 48) 40 WHITESPACE@[48; 57)
43 WHITESPACE@[48; 57) 41 LET_STMT@[57; 85)
44 err: `expected a name` 42 LET_KW@[57; 60)
45 BIND_PAT@[57; 60) 43 WHITESPACE@[60; 61)
46 ERROR@[57; 60) 44 BIND_PAT@[61; 64)
47 LET_KW@[57; 60) 45 NAME@[61; 64)
48 err: `expected COMMA` 46 IDENT@[61; 64) "res"
49 WHITESPACE@[60; 61) 47 WHITESPACE@[64; 65)
50 BIND_PAT@[61; 64) 48 EQ@[65; 66)
51 NAME@[61; 64) 49 WHITESPACE@[66; 67)
52 IDENT@[61; 64) "res" 50 METHOD_CALL_EXPR@[67; 84)
53 err: `expected COMMA` 51 FIELD_EXPR@[67; 78)
54 WHITESPACE@[64; 65) 52 PATH_EXPR@[67; 71)
55 err: `expected a name` 53 PATH@[67; 71)
56 BIND_PAT@[65; 66) 54 PATH_SEGMENT@[67; 71)
57 ERROR@[65; 66) 55 SELF_KW@[67; 71)
58 EQ@[65; 66) 56 DOT@[71; 72)
59 err: `expected COMMA` 57 NAME_REF@[72; 78)
60 WHITESPACE@[66; 67) 58 IDENT@[72; 78) "scopes"
61 err: `expected a name` 59 DOT@[78; 79)
62 BIND_PAT@[67; 71) 60 NAME_REF@[79; 82)
63 ERROR@[67; 71) 61 IDENT@[79; 82) "len"
64 SELF_KW@[67; 71) 62 ARG_LIST@[82; 84)
65 err: `expected COMMA` 63 L_PAREN@[82; 83)
66 err: `expected a name` 64 R_PAREN@[83; 84)
67 BIND_PAT@[71; 72) 65 SEMI@[84; 85)
68 ERROR@[71; 72) 66 WHITESPACE@[85; 94)
69 DOT@[71; 72) 67 METHOD_CALL_EXPR@[94; 155)
70 err: `expected COMMA` 68 FIELD_EXPR@[94; 105)
71 BIND_PAT@[72; 78) 69 PATH_EXPR@[94; 98)
72 NAME@[72; 78) 70 PATH@[94; 98)
73 IDENT@[72; 78) "scopes" 71 PATH_SEGMENT@[94; 98)
74 err: `expected COMMA`
75 err: `expected a name`
76 BIND_PAT@[78; 79)
77 ERROR@[78; 79)
78 DOT@[78; 79)
79 err: `expected COMMA`
80 BIND_PAT@[79; 82)
81 NAME@[79; 82)
82 IDENT@[79; 82) "len"
83 err: `expected COMMA`
84 err: `expected a name`
85 BIND_PAT@[82; 83)
86 ERROR@[82; 83)
87 L_PAREN@[82; 83)
88 err: `expected COMMA`
89 err: `expected a name`
90 BIND_PAT@[83; 84)
91 ERROR@[83; 84)
92 R_PAREN@[83; 84)
93 err: `expected COMMA`
94 err: `expected a name`
95 BIND_PAT@[84; 85)
96 ERROR@[84; 85)
97 SEMI@[84; 85)
98 err: `expected COMMA`
99 WHITESPACE@[85; 94)
100 err: `expected a name`
101 BIND_PAT@[94; 98)
102 ERROR@[94; 98)
103 SELF_KW@[94; 98) 72 SELF_KW@[94; 98)
104 err: `expected COMMA` 73 DOT@[98; 99)
105 err: `expected a name` 74 NAME_REF@[99; 105)
106 BIND_PAT@[98; 99) 75 IDENT@[99; 105) "scopes"
107 ERROR@[98; 99) 76 DOT@[105; 106)
108 DOT@[98; 99) 77 NAME_REF@[106; 110)
109 err: `expected COMMA` 78 IDENT@[106; 110) "push"
110 BIND_PAT@[99; 105) 79 ARG_LIST@[110; 155)
111 NAME@[99; 105) 80 L_PAREN@[110; 111)
112 IDENT@[99; 105) "scopes" 81 STRUCT_LIT@[111; 154)
113 err: `expected COMMA` 82 PATH@[111; 120)
114 err: `expected a name` 83 PATH_SEGMENT@[111; 120)
115 BIND_PAT@[105; 106) 84 NAME_REF@[111; 120)
116 ERROR@[105; 106) 85 IDENT@[111; 120) "ScopeData"
117 DOT@[105; 106)
118 err: `expected COMMA`
119 BIND_PAT@[106; 110)
120 NAME@[106; 110)
121 IDENT@[106; 110) "push"
122 err: `expected COMMA`
123 err: `expected a name`
124 BIND_PAT@[110; 111)
125 ERROR@[110; 111)
126 L_PAREN@[110; 111)
127 err: `expected COMMA`
128 BIND_PAT@[111; 120)
129 NAME@[111; 120)
130 IDENT@[111; 120) "ScopeData"
131 err: `expected COMMA`
132 WHITESPACE@[120; 121) 86 WHITESPACE@[120; 121)
133 err: `expected ident` 87 NAMED_FIELD_LIST@[121; 154)
134 ERROR@[121; 154)
135 L_CURLY@[121; 122) 88 L_CURLY@[121; 122)
136 WHITESPACE@[122; 123) 89 WHITESPACE@[122; 123)
137 IDENT@[123; 129) "parent" 90 NAMED_FIELD@[123; 135)
138 COLON@[129; 130) 91 NAME_REF@[123; 129)
139 WHITESPACE@[130; 131) 92 IDENT@[123; 129) "parent"
140 IDENT@[131; 135) "None" 93 COLON@[129; 130)
94 WHITESPACE@[130; 131)
95 PATH_EXPR@[131; 135)
96 PATH@[131; 135)
97 PATH_SEGMENT@[131; 135)
98 NAME_REF@[131; 135)
99 IDENT@[131; 135) "None"
141 COMMA@[135; 136) 100 COMMA@[135; 136)
142 WHITESPACE@[136; 137) 101 WHITESPACE@[136; 137)
143 IDENT@[137; 144) "entries" 102 NAMED_FIELD@[137; 152)
144 COLON@[144; 145) 103 NAME_REF@[137; 144)
145 WHITESPACE@[145; 146) 104 IDENT@[137; 144) "entries"
146 IDENT@[146; 149) "vec" 105 COLON@[144; 145)
147 EXCL@[149; 150) 106 WHITESPACE@[145; 146)
148 L_BRACK@[150; 151) 107 MACRO_CALL@[146; 152)
149 R_BRACK@[151; 152) 108 PATH@[146; 149)
109 PATH_SEGMENT@[146; 149)
110 NAME_REF@[146; 149)
111 IDENT@[146; 149) "vec"
112 EXCL@[149; 150)
113 TOKEN_TREE@[150; 152)
114 L_BRACK@[150; 151)
115 R_BRACK@[151; 152)
150 WHITESPACE@[152; 153) 116 WHITESPACE@[152; 153)
151 R_CURLY@[153; 154) 117 R_CURLY@[153; 154)
152 err: `expected COMMA` 118 R_PAREN@[154; 155)
153 err: `expected a name` 119 WHITESPACE@[155; 160)
154 BIND_PAT@[154; 155) 120 R_CURLY@[160; 161)
155 ERROR@[154; 155) 121 WHITESPACE@[161; 167)
156 R_PAREN@[154; 155) 122 FN_DEF@[167; 180)
157 WHITESPACE@[155; 160) 123 FN_KW@[167; 169)
158 R_CURLY@[160; 161) 124 WHITESPACE@[169; 170)
159 err: `expected COLON` 125 NAME@[170; 180)
160 WHITESPACE@[161; 167) 126 IDENT@[170; 180) "set_parent"
161 FN_POINTER_TYPE@[167; 169) 127 err: `expected function arguments`
162 FN_KW@[167; 169) 128 err: `expected a block`
163 err: `expected parameters`
164 err: `expected COMMA`
165 WHITESPACE@[169; 170)
166 PARAM@[170; 180)
167 BIND_PAT@[170; 180)
168 NAME@[170; 180)
169 IDENT@[170; 180) "set_parent"
170 err: `expected COLON`
171 err: `expected type`
172 err: `expected COMMA`
173 err: `expected value parameter`
174 err: `expected R_PAREN`
175 err: `expected a block`
176 WHITESPACE@[180; 181) 129 WHITESPACE@[180; 181)
177 R_CURLY@[181; 182) 130 R_CURLY@[181; 182)
178 WHITESPACE@[182; 183) 131 WHITESPACE@[182; 183)
diff --git a/crates/libsyntax2/tests/data/parser/err/0021_incomplete_param.rs b/crates/libsyntax2/tests/data/parser/err/0021_incomplete_param.rs
new file mode 100644
index 000000000..7a6c264f6
--- /dev/null
+++ b/crates/libsyntax2/tests/data/parser/err/0021_incomplete_param.rs
@@ -0,0 +1,2 @@
1fn foo(x: i32, y) {
2}
diff --git a/crates/libsyntax2/tests/data/parser/err/0021_incomplete_param.txt b/crates/libsyntax2/tests/data/parser/err/0021_incomplete_param.txt
new file mode 100644
index 000000000..8dcb58ae2
--- /dev/null
+++ b/crates/libsyntax2/tests/data/parser/err/0021_incomplete_param.txt
@@ -0,0 +1,34 @@
1ROOT@[0; 22)
2 FN_DEF@[0; 21)
3 FN_KW@[0; 2)
4 WHITESPACE@[2; 3)
5 NAME@[3; 6)
6 IDENT@[3; 6) "foo"
7 PARAM_LIST@[6; 17)
8 L_PAREN@[6; 7)
9 PARAM@[7; 13)
10 BIND_PAT@[7; 8)
11 NAME@[7; 8)
12 IDENT@[7; 8) "x"
13 COLON@[8; 9)
14 WHITESPACE@[9; 10)
15 PATH_TYPE@[10; 13)
16 PATH@[10; 13)
17 PATH_SEGMENT@[10; 13)
18 NAME_REF@[10; 13)
19 IDENT@[10; 13) "i32"
20 COMMA@[13; 14)
21 WHITESPACE@[14; 15)
22 PARAM@[15; 16)
23 BIND_PAT@[15; 16)
24 NAME@[15; 16)
25 IDENT@[15; 16) "y"
26 err: `expected COLON`
27 err: `expected type`
28 R_PAREN@[16; 17)
29 WHITESPACE@[17; 18)
30 BLOCK@[18; 21)
31 L_CURLY@[18; 19)
32 WHITESPACE@[19; 20)
33 R_CURLY@[20; 21)
34 WHITESPACE@[21; 22)
diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs
index 93d8bd9fe..898195f6d 100644
--- a/crates/server/src/main_loop/handlers.rs
+++ b/crates/server/src/main_loop/handlers.rs
@@ -345,7 +345,8 @@ pub fn handle_completion(
345 let items = items.into_iter() 345 let items = items.into_iter()
346 .map(|item| { 346 .map(|item| {
347 let mut res = CompletionItem { 347 let mut res = CompletionItem {
348 label: item.name, 348 label: item.label,
349 filter_text: item.lookup,
349 .. Default::default() 350 .. Default::default()
350 }; 351 };
351 if let Some(snip) = item.snippet { 352 if let Some(snip) = item.snippet {