aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/hir/src/lib.rs11
-rw-r--r--crates/ide/src/folding_ranges.rs55
-rw-r--r--crates/ide/src/hover.rs135
-rw-r--r--crates/ide_assists/src/handlers/extract_variable.rs208
-rw-r--r--crates/ide_assists/src/tests.rs46
-rw-r--r--crates/ide_assists/src/utils.rs2
-rw-r--r--crates/ide_assists/src/utils/suggest_name.rs877
-rw-r--r--crates/ide_db/src/helpers.rs4
-rw-r--r--crates/ide_db/src/helpers/famous_defs_fixture.rs8
-rw-r--r--crates/mbe/Cargo.toml1
-rw-r--r--crates/mbe/src/benchmark.rs40
-rw-r--r--crates/mbe/src/expander.rs16
-rw-r--r--crates/mbe/src/expander/matcher.rs559
-rw-r--r--crates/mbe/src/expander/transcriber.rs12
-rw-r--r--crates/mbe/src/lib.rs11
-rw-r--r--crates/mbe/src/parser.rs80
-rw-r--r--crates/mbe/src/tests.rs23
-rw-r--r--crates/rust-analyzer/src/to_proto.rs2
18 files changed, 1892 insertions, 198 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 769945c47..69fcdab07 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -33,11 +33,11 @@ mod has_source;
33pub use crate::{ 33pub use crate::{
34 attrs::{HasAttrs, Namespace}, 34 attrs::{HasAttrs, Namespace},
35 code_model::{ 35 code_model::{
36 Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, Callable, CallableKind, Const, 36 Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, BuiltinType, Callable,
37 ConstParam, Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function, 37 CallableKind, Const, ConstParam, Crate, CrateDependency, DefWithBody, Enum, Field,
38 GenericDef, GenericParam, HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef, 38 FieldSource, Function, GenericDef, GenericParam, HasVisibility, Impl, Label, LifetimeParam,
39 Module, ModuleDef, ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, 39 Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, Trait, Type, TypeAlias,
40 Variant, VariantDef, 40 TypeParam, Union, Variant, VariantDef,
41 }, 41 },
42 has_source::HasSource, 42 has_source::HasSource,
43 semantics::{PathResolution, Semantics, SemanticsScope}, 43 semantics::{PathResolution, Semantics, SemanticsScope},
@@ -47,7 +47,6 @@ pub use hir_def::{
47 adt::StructKind, 47 adt::StructKind,
48 attr::{Attrs, Documentation}, 48 attr::{Attrs, Documentation},
49 body::scope::ExprScopes, 49 body::scope::ExprScopes,
50 builtin_type::BuiltinType,
51 find_path::PrefixKind, 50 find_path::PrefixKind,
52 import_map, 51 import_map,
53 item_scope::ItemInNs, 52 item_scope::ItemInNs,
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
index 45170dd29..4b1b24562 100644
--- a/crates/ide/src/folding_ranges.rs
+++ b/crates/ide/src/folding_ranges.rs
@@ -6,7 +6,7 @@ use syntax::{
6 ast::{self, AstNode, AstToken, VisibilityOwner}, 6 ast::{self, AstNode, AstToken, VisibilityOwner},
7 Direction, NodeOrToken, SourceFile, 7 Direction, NodeOrToken, SourceFile,
8 SyntaxKind::{self, *}, 8 SyntaxKind::{self, *},
9 SyntaxNode, TextRange, 9 SyntaxNode, TextRange, TextSize,
10}; 10};
11 11
12#[derive(Debug, PartialEq, Eq)] 12#[derive(Debug, PartialEq, Eq)]
@@ -16,6 +16,7 @@ pub enum FoldKind {
16 Mods, 16 Mods,
17 Block, 17 Block,
18 ArgList, 18 ArgList,
19 Region,
19} 20}
20 21
21#[derive(Debug)] 22#[derive(Debug)]
@@ -29,6 +30,8 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
29 let mut visited_comments = FxHashSet::default(); 30 let mut visited_comments = FxHashSet::default();
30 let mut visited_imports = FxHashSet::default(); 31 let mut visited_imports = FxHashSet::default();
31 let mut visited_mods = FxHashSet::default(); 32 let mut visited_mods = FxHashSet::default();
33 // regions can be nested, here is a LIFO buffer
34 let mut regions_starts: Vec<TextSize> = vec![];
32 35
33 for element in file.syntax().descendants_with_tokens() { 36 for element in file.syntax().descendants_with_tokens() {
34 // Fold items that span multiple lines 37 // Fold items that span multiple lines
@@ -48,10 +51,25 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
48 // Fold groups of comments 51 // Fold groups of comments
49 if let Some(comment) = ast::Comment::cast(token) { 52 if let Some(comment) = ast::Comment::cast(token) {
50 if !visited_comments.contains(&comment) { 53 if !visited_comments.contains(&comment) {
51 if let Some(range) = 54 // regions are not real comments
52 contiguous_range_for_comment(comment, &mut visited_comments) 55 if comment.text().trim().starts_with("// region:") {
53 { 56 regions_starts.push(comment.syntax().text_range().start());
54 res.push(Fold { range, kind: FoldKind::Comment }) 57 } else if comment.text().trim().starts_with("// endregion") {
58 if let Some(region) = regions_starts.pop() {
59 res.push(Fold {
60 range: TextRange::new(
61 region,
62 comment.syntax().text_range().end(),
63 ),
64 kind: FoldKind::Region,
65 })
66 }
67 } else {
68 if let Some(range) =
69 contiguous_range_for_comment(comment, &mut visited_comments)
70 {
71 res.push(Fold { range, kind: FoldKind::Comment })
72 }
55 } 73 }
56 } 74 }
57 } 75 }
@@ -175,9 +193,16 @@ fn contiguous_range_for_comment(
175 } 193 }
176 if let Some(c) = ast::Comment::cast(token) { 194 if let Some(c) = ast::Comment::cast(token) {
177 if c.kind() == group_kind { 195 if c.kind() == group_kind {
178 visited.insert(c.clone()); 196 // regions are not real comments
179 last = c; 197 if c.text().trim().starts_with("// region:")
180 continue; 198 || c.text().trim().starts_with("// endregion")
199 {
200 break;
201 } else {
202 visited.insert(c.clone());
203 last = c;
204 continue;
205 }
181 } 206 }
182 } 207 }
183 // The comment group ends because either: 208 // The comment group ends because either:
@@ -224,6 +249,7 @@ mod tests {
224 FoldKind::Mods => "mods", 249 FoldKind::Mods => "mods",
225 FoldKind::Block => "block", 250 FoldKind::Block => "block",
226 FoldKind::ArgList => "arglist", 251 FoldKind::ArgList => "arglist",
252 FoldKind::Region => "region",
227 }; 253 };
228 assert_eq!(kind, &attr.unwrap()); 254 assert_eq!(kind, &attr.unwrap());
229 } 255 }
@@ -418,4 +444,17 @@ fn foo<fold arglist>(
418"#, 444"#,
419 ) 445 )
420 } 446 }
447
448 #[test]
449 fn fold_region() {
450 check(
451 r#"
452// 1. some normal comment
453<fold region>// region: test
454// 2. some normal comment
455calling_function(x,y);
456// endregion: test</fold>
457"#,
458 )
459 }
421} 460}
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 20b799490..a9454cfa3 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -5,6 +5,7 @@ use hir::{
5use ide_db::{ 5use ide_db::{
6 base_db::SourceDatabase, 6 base_db::SourceDatabase,
7 defs::{Definition, NameClass, NameRefClass}, 7 defs::{Definition, NameClass, NameRefClass},
8 helpers::FamousDefs,
8 RootDatabase, 9 RootDatabase,
9}; 10};
10use itertools::Itertools; 11use itertools::Itertools;
@@ -107,16 +108,14 @@ pub(crate) fn hover(
107 } 108 }
108 }; 109 };
109 if let Some(definition) = definition { 110 if let Some(definition) = definition {
110 if let Some(markup) = hover_for_definition(db, definition) { 111 let famous_defs = match &definition {
111 let markup = markup.as_str(); 112 Definition::ModuleDef(ModuleDef::BuiltinType(_)) => {
112 let markup = if !markdown { 113 Some(FamousDefs(&sema, sema.scope(&node).krate()))
113 remove_markdown(markup) 114 }
114 } else if links_in_hover { 115 _ => None,
115 rewrite_links(db, markup, &definition) 116 };
116 } else { 117 if let Some(markup) = hover_for_definition(db, definition, famous_defs.as_ref()) {
117 remove_links(markup) 118 res.markup = process_markup(sema.db, definition, &markup, links_in_hover, markdown);
118 };
119 res.markup = Markup::from(markup);
120 if let Some(action) = show_implementations_action(db, definition) { 119 if let Some(action) = show_implementations_action(db, definition) {
121 res.actions.push(action); 120 res.actions.push(action);
122 } 121 }
@@ -138,6 +137,9 @@ pub(crate) fn hover(
138 // don't highlight the entire parent node on comment hover 137 // don't highlight the entire parent node on comment hover
139 return None; 138 return None;
140 } 139 }
140 if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) {
141 return res;
142 }
141 143
142 let node = token 144 let node = token
143 .ancestors() 145 .ancestors()
@@ -272,6 +274,24 @@ fn hover_markup(
272 } 274 }
273} 275}
274 276
277fn process_markup(
278 db: &RootDatabase,
279 def: Definition,
280 markup: &Markup,
281 links_in_hover: bool,
282 markdown: bool,
283) -> Markup {
284 let markup = markup.as_str();
285 let markup = if !markdown {
286 remove_markdown(markup)
287 } else if links_in_hover {
288 rewrite_links(db, markup, &def)
289 } else {
290 remove_links(markup)
291 };
292 Markup::from(markup)
293}
294
275fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> { 295fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> {
276 match def { 296 match def {
277 Definition::Field(f) => Some(f.parent_def(db).name(db)), 297 Definition::Field(f) => Some(f.parent_def(db).name(db)),
@@ -304,7 +324,11 @@ fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
304 def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def))) 324 def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def)))
305} 325}
306 326
307fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> { 327fn hover_for_definition(
328 db: &RootDatabase,
329 def: Definition,
330 famous_defs: Option<&FamousDefs>,
331) -> Option<Markup> {
308 let mod_path = definition_mod_path(db, &def); 332 let mod_path = definition_mod_path(db, &def);
309 return match def { 333 return match def {
310 Definition::Macro(it) => { 334 Definition::Macro(it) => {
@@ -339,7 +363,9 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
339 ModuleDef::Static(it) => from_def_source(db, it, mod_path), 363 ModuleDef::Static(it) => from_def_source(db, it, mod_path),
340 ModuleDef::Trait(it) => from_def_source(db, it, mod_path), 364 ModuleDef::Trait(it) => from_def_source(db, it, mod_path),
341 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), 365 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path),
342 ModuleDef::BuiltinType(it) => Some(Markup::fenced_block(&it.name())), 366 ModuleDef::BuiltinType(it) => famous_defs
367 .and_then(|fd| hover_for_builtin(fd, it))
368 .or_else(|| Some(Markup::fenced_block(&it.name()))),
343 }, 369 },
344 Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))), 370 Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))),
345 Definition::SelfType(impl_def) => { 371 Definition::SelfType(impl_def) => {
@@ -380,11 +406,52 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
380 } 406 }
381} 407}
382 408
409fn hover_for_keyword(
410 sema: &Semantics<RootDatabase>,
411 links_in_hover: bool,
412 markdown: bool,
413 token: &SyntaxToken,
414) -> Option<RangeInfo<HoverResult>> {
415 if !token.kind().is_keyword() {
416 return None;
417 }
418 let famous_defs = FamousDefs(&sema, sema.scope(&token.parent()).krate());
419 // std exposes {}_keyword modules with docstrings on the root to document keywords
420 let keyword_mod = format!("{}_keyword", token.text());
421 let doc_owner = find_std_module(&famous_defs, &keyword_mod)?;
422 let docs = doc_owner.attrs(sema.db).docs()?;
423 let markup = process_markup(
424 sema.db,
425 Definition::ModuleDef(doc_owner.into()),
426 &hover_markup(Some(docs.into()), Some(token.text().into()), None)?,
427 links_in_hover,
428 markdown,
429 );
430 Some(RangeInfo::new(token.text_range(), HoverResult { markup, actions: Default::default() }))
431}
432
433fn hover_for_builtin(famous_defs: &FamousDefs, builtin: hir::BuiltinType) -> Option<Markup> {
434 // std exposes prim_{} modules with docstrings on the root to document the builtins
435 let primitive_mod = format!("prim_{}", builtin.name());
436 let doc_owner = find_std_module(famous_defs, &primitive_mod)?;
437 let docs = doc_owner.attrs(famous_defs.0.db).docs()?;
438 hover_markup(Some(docs.into()), Some(builtin.name().to_string()), None)
439}
440
441fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module> {
442 let db = famous_defs.0.db;
443 let std_crate = famous_defs.std()?;
444 let std_root_module = std_crate.root_module(db);
445 std_root_module
446 .children(db)
447 .find(|module| module.name(db).map_or(false, |module| module.to_string() == name))
448}
449
383fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 450fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
384 return tokens.max_by_key(priority); 451 return tokens.max_by_key(priority);
385 fn priority(n: &SyntaxToken) -> usize { 452 fn priority(n: &SyntaxToken) -> usize {
386 match n.kind() { 453 match n.kind() {
387 IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 3, 454 IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,
388 T!['('] | T![')'] => 2, 455 T!['('] | T![')'] => 2,
389 kind if kind.is_trivia() => 0, 456 kind if kind.is_trivia() => 0,
390 _ => 1, 457 _ => 1,
@@ -3523,6 +3590,48 @@ use foo::bar::{self$0};
3523 3590
3524 But this should appear 3591 But this should appear
3525 "#]], 3592 "#]],
3593 )
3594 }
3595
3596 #[test]
3597 fn hover_keyword() {
3598 let ra_fixture = r#"//- /main.rs crate:main deps:std
3599fn f() { retur$0n; }"#;
3600 let fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE);
3601 check(
3602 &fixture,
3603 expect![[r#"
3604 *return*
3605
3606 ```rust
3607 return
3608 ```
3609
3610 ---
3611
3612 Docs for return_keyword
3613 "#]],
3614 );
3615 }
3616
3617 #[test]
3618 fn hover_builtin() {
3619 let ra_fixture = r#"//- /main.rs crate:main deps:std
3620cosnt _: &str$0 = ""; }"#;
3621 let fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE);
3622 check(
3623 &fixture,
3624 expect![[r#"
3625 *str*
3626
3627 ```rust
3628 str
3629 ```
3630
3631 ---
3632
3633 Docs for prim_str
3634 "#]],
3526 ); 3635 );
3527 } 3636 }
3528} 3637}
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs
index 98f3dc6ca..312ac7ac4 100644
--- a/crates/ide_assists/src/handlers/extract_variable.rs
+++ b/crates/ide_assists/src/handlers/extract_variable.rs
@@ -8,7 +8,7 @@ use syntax::{
8}; 8};
9use test_utils::mark; 9use test_utils::mark;
10 10
11use crate::{AssistContext, AssistId, AssistKind, Assists}; 11use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
12 12
13// Assist: extract_variable 13// Assist: extract_variable
14// 14//
@@ -54,7 +54,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
54 54
55 let var_name = match &field_shorthand { 55 let var_name = match &field_shorthand {
56 Some(it) => it.to_string(), 56 Some(it) => it.to_string(),
57 None => "var_name".to_string(), 57 None => suggest_name::variable(&to_extract, &ctx.sema),
58 }; 58 };
59 let expr_range = match &field_shorthand { 59 let expr_range = match &field_shorthand {
60 Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()), 60 Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()),
@@ -274,8 +274,8 @@ fn foo() {
274"#, 274"#,
275 r#" 275 r#"
276fn foo() { 276fn foo() {
277 let $0var_name = bar(1 + 1); 277 let $0bar = bar(1 + 1);
278 var_name 278 bar
279} 279}
280"#, 280"#,
281 ) 281 )
@@ -401,8 +401,8 @@ fn main() {
401", 401",
402 " 402 "
403fn main() { 403fn main() {
404 let $0var_name = bar.foo(); 404 let $0foo = bar.foo();
405 let v = var_name; 405 let v = foo;
406} 406}
407", 407",
408 ); 408 );
@@ -557,6 +557,202 @@ fn main() {
557 } 557 }
558 558
559 #[test] 559 #[test]
560 fn extract_var_name_from_type() {
561 check_assist(
562 extract_variable,
563 r#"
564struct Test(i32);
565
566fn foo() -> Test {
567 $0{ Test(10) }$0
568}
569"#,
570 r#"
571struct Test(i32);
572
573fn foo() -> Test {
574 let $0test = { Test(10) };
575 test
576}
577"#,
578 )
579 }
580
581 #[test]
582 fn extract_var_name_from_parameter() {
583 check_assist(
584 extract_variable,
585 r#"
586fn bar(test: u32, size: u32)
587
588fn foo() {
589 bar(1, $01+1$0);
590}
591"#,
592 r#"
593fn bar(test: u32, size: u32)
594
595fn foo() {
596 let $0size = 1+1;
597 bar(1, size);
598}
599"#,
600 )
601 }
602
603 #[test]
604 fn extract_var_parameter_name_has_precedence_over_type() {
605 check_assist(
606 extract_variable,
607 r#"
608struct TextSize(u32);
609fn bar(test: u32, size: TextSize)
610
611fn foo() {
612 bar(1, $0{ TextSize(1+1) }$0);
613}
614"#,
615 r#"
616struct TextSize(u32);
617fn bar(test: u32, size: TextSize)
618
619fn foo() {
620 let $0size = { TextSize(1+1) };
621 bar(1, size);
622}
623"#,
624 )
625 }
626
627 #[test]
628 fn extract_var_name_from_function() {
629 check_assist(
630 extract_variable,
631 r#"
632fn is_required(test: u32, size: u32) -> bool
633
634fn foo() -> bool {
635 $0is_required(1, 2)$0
636}
637"#,
638 r#"
639fn is_required(test: u32, size: u32) -> bool
640
641fn foo() -> bool {
642 let $0is_required = is_required(1, 2);
643 is_required
644}
645"#,
646 )
647 }
648
649 #[test]
650 fn extract_var_name_from_method() {
651 check_assist(
652 extract_variable,
653 r#"
654struct S;
655impl S {
656 fn bar(&self, n: u32) -> u32 { n }
657}
658
659fn foo() -> u32 {
660 $0S.bar(1)$0
661}
662"#,
663 r#"
664struct S;
665impl S {
666 fn bar(&self, n: u32) -> u32 { n }
667}
668
669fn foo() -> u32 {
670 let $0bar = S.bar(1);
671 bar
672}
673"#,
674 )
675 }
676
677 #[test]
678 fn extract_var_name_from_method_param() {
679 check_assist(
680 extract_variable,
681 r#"
682struct S;
683impl S {
684 fn bar(&self, n: u32, size: u32) { n }
685}
686
687fn foo() {
688 S.bar($01 + 1$0, 2)
689}
690"#,
691 r#"
692struct S;
693impl S {
694 fn bar(&self, n: u32, size: u32) { n }
695}
696
697fn foo() {
698 let $0n = 1 + 1;
699 S.bar(n, 2)
700}
701"#,
702 )
703 }
704
705 #[test]
706 fn extract_var_name_from_ufcs_method_param() {
707 check_assist(
708 extract_variable,
709 r#"
710struct S;
711impl S {
712 fn bar(&self, n: u32, size: u32) { n }
713}
714
715fn foo() {
716 S::bar(&S, $01 + 1$0, 2)
717}
718"#,
719 r#"
720struct S;
721impl S {
722 fn bar(&self, n: u32, size: u32) { n }
723}
724
725fn foo() {
726 let $0n = 1 + 1;
727 S::bar(&S, n, 2)
728}
729"#,
730 )
731 }
732
733 #[test]
734 fn extract_var_parameter_name_has_precedence_over_function() {
735 check_assist(
736 extract_variable,
737 r#"
738fn bar(test: u32, size: u32)
739
740fn foo() {
741 bar(1, $0symbol_size(1, 2)$0);
742}
743"#,
744 r#"
745fn bar(test: u32, size: u32)
746
747fn foo() {
748 let $0size = symbol_size(1, 2);
749 bar(1, size);
750}
751"#,
752 )
753 }
754
755 #[test]
560 fn test_extract_var_for_return_not_applicable() { 756 fn test_extract_var_for_return_not_applicable() {
561 check_assist_not_applicable(extract_variable, "fn foo() { $0return$0; } "); 757 check_assist_not_applicable(extract_variable, "fn foo() { $0return$0; } ");
562 } 758 }
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index b7f616760..66820058b 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -12,7 +12,7 @@ use ide_db::{
12 RootDatabase, 12 RootDatabase,
13}; 13};
14use stdx::{format_to, trim_indent}; 14use stdx::{format_to, trim_indent};
15use syntax::TextRange; 15use syntax::{ast, AstNode, TextRange};
16use test_utils::{assert_eq_text, extract_offset}; 16use test_utils::{assert_eq_text, extract_offset};
17 17
18use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists}; 18use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists};
@@ -180,6 +180,50 @@ fn labels(assists: &[Assist]) -> String {
180 labels.into_iter().collect::<String>() 180 labels.into_iter().collect::<String>()
181} 181}
182 182
183pub(crate) type NameSuggestion = fn(&ast::Expr, &Semantics<'_, RootDatabase>) -> Option<String>;
184
185#[track_caller]
186pub(crate) fn check_name_suggestion(
187 suggestion: NameSuggestion,
188 ra_fixture: &str,
189 suggested_name: &str,
190) {
191 check_name(suggestion, ra_fixture, Some(suggested_name));
192}
193
194#[track_caller]
195pub(crate) fn check_name_suggestion_not_applicable(suggestion: NameSuggestion, ra_fixture: &str) {
196 check_name(suggestion, ra_fixture, None);
197}
198
199#[track_caller]
200fn check_name(suggestion: NameSuggestion, ra_fixture: &str, expected: Option<&str>) {
201 let (db, file_with_carret_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
202 let frange = FileRange { file_id: file_with_carret_id, range: range_or_offset.into() };
203
204 let sema = Semantics::new(&db);
205 let source_file = sema.parse(frange.file_id);
206 let element = source_file.syntax().covering_element(frange.range);
207 let expr =
208 element.ancestors().find_map(ast::Expr::cast).expect("selection is not an expression");
209 assert_eq!(
210 expr.syntax().text_range(),
211 frange.range,
212 "selection is not an expression(yet contained in one)"
213 );
214
215 let name = suggestion(&expr, &sema);
216
217 match (name, expected) {
218 (Some(name), Some(expected_name)) => {
219 assert_eq_text!(&name, expected_name);
220 }
221 (Some(_), None) => panic!("name suggestion should not be applicable"),
222 (None, Some(_)) => panic!("name suggestion is not applicable"),
223 (None, None) => (),
224 }
225}
226
183#[test] 227#[test]
184fn assist_order_field_struct() { 228fn assist_order_field_struct() {
185 let before = "struct Foo { $0bar: u32 }"; 229 let before = "struct Foo { $0bar: u32 }";
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index 880ab6fe3..62f959082 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -1,5 +1,7 @@
1//! Assorted functions shared by several assists. 1//! Assorted functions shared by several assists.
2 2
3pub(crate) mod suggest_name;
4
3use std::ops; 5use std::ops;
4 6
5use ast::TypeBoundsOwner; 7use ast::TypeBoundsOwner;
diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs
new file mode 100644
index 000000000..d37c62642
--- /dev/null
+++ b/crates/ide_assists/src/utils/suggest_name.rs
@@ -0,0 +1,877 @@
1//! This module contains functions to suggest names for expressions, functions and other items
2
3use hir::Semantics;
4use ide_db::RootDatabase;
5use itertools::Itertools;
6use stdx::to_lower_snake_case;
7use syntax::{
8 ast::{self, NameOwner},
9 match_ast, AstNode,
10};
11
12/// Trait names, that will be ignored when in `impl Trait` and `dyn Trait`
13const USELESS_TRAITS: &[&str] = &["Send", "Sync", "Copy", "Clone", "Eq", "PartialEq"];
14/// Identifier names that won't be suggested, ever
15///
16/// **NOTE**: they all must be snake lower case
17const USELESS_NAMES: &[&str] =
18 &["new", "default", "option", "some", "none", "ok", "err", "str", "string"];
19/// Generic types replaced by their first argument
20///
21/// # Examples
22/// `Option<Name>` -> `Name`
23/// `Result<User, Error>` -> `User`
24const WRAPPER_TYPES: &[&str] = &["Box", "Option", "Result"];
25/// Prefixes to strip from methods names
26///
27/// # Examples
28/// `vec.as_slice()` -> `slice`
29/// `args.into_config()` -> `config`
30/// `bytes.to_vec()` -> `vec`
31const USELESS_METHOD_PREFIXES: &[&str] = &["into_", "as_", "to_"];
32/// Useless methods that are stripped from expression
33///
34/// # Examples
35/// `var.name().to_string()` -> `var.name()`
36const USELESS_METHODS: &[&str] = &[
37 "to_string",
38 "as_str",
39 "to_owned",
40 "as_ref",
41 "clone",
42 "cloned",
43 "expect",
44 "expect_none",
45 "unwrap",
46 "unwrap_none",
47 "unwrap_or",
48 "unwrap_or_default",
49 "unwrap_or_else",
50 "unwrap_unchecked",
51 "iter",
52 "into_iter",
53 "iter_mut",
54];
55
56/// Suggest name of variable for given expression
57///
58/// **NOTE**: it is caller's responsibility to guarantee uniqueness of the name.
59/// I.e. it doesn't look for names in scope.
60///
61/// # Current implementation
62///
63/// In current implementation, the function tries to get the name from
64/// the following sources:
65///
66/// * if expr is an argument to function/method, use paramter name
67/// * if expr is a function/method call, use function name
68/// * expression type name if it exists (E.g. `()`, `fn() -> ()` or `!` do not have names)
69/// * fallback: `var_name`
70///
71/// It also applies heuristics to filter out less informative names
72///
73/// Currently it sticks to the first name found.
74pub(crate) fn variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String {
75 // `from_param` does not benifit from stripping
76 // it need the largest context possible
77 // so we check firstmost
78 if let Some(name) = from_param(expr, sema) {
79 return name;
80 }
81
82 let mut next_expr = Some(expr.clone());
83 while let Some(expr) = next_expr {
84 let name = from_call(&expr).or_else(|| from_type(&expr, sema));
85 if let Some(name) = name {
86 return name;
87 }
88
89 match expr {
90 ast::Expr::RefExpr(inner) => next_expr = inner.expr(),
91 ast::Expr::BoxExpr(inner) => next_expr = inner.expr(),
92 ast::Expr::AwaitExpr(inner) => next_expr = inner.expr(),
93 // ast::Expr::BlockExpr(block) => expr = block.tail_expr(),
94 ast::Expr::CastExpr(inner) => next_expr = inner.expr(),
95 ast::Expr::MethodCallExpr(method) if is_useless_method(&method) => {
96 next_expr = method.receiver();
97 }
98 ast::Expr::ParenExpr(inner) => next_expr = inner.expr(),
99 ast::Expr::TryExpr(inner) => next_expr = inner.expr(),
100 ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(ast::PrefixOp::Deref) => {
101 next_expr = prefix.expr()
102 }
103 _ => break,
104 }
105 }
106
107 "var_name".to_string()
108}
109
110fn normalize(name: &str) -> Option<String> {
111 let name = to_lower_snake_case(name);
112
113 if USELESS_NAMES.contains(&name.as_str()) {
114 return None;
115 }
116
117 if !is_valid_name(&name) {
118 return None;
119 }
120
121 Some(name)
122}
123
124fn is_valid_name(name: &str) -> bool {
125 match syntax::lex_single_syntax_kind(name) {
126 Some((syntax::SyntaxKind::IDENT, _error)) => true,
127 _ => false,
128 }
129}
130
131fn is_useless_method(method: &ast::MethodCallExpr) -> bool {
132 let ident = method.name_ref().and_then(|it| it.ident_token());
133
134 if let Some(ident) = ident {
135 USELESS_METHODS.contains(&ident.text())
136 } else {
137 false
138 }
139}
140
141fn from_call(expr: &ast::Expr) -> Option<String> {
142 from_func_call(expr).or_else(|| from_method_call(expr))
143}
144
145fn from_func_call(expr: &ast::Expr) -> Option<String> {
146 let call = match expr {
147 ast::Expr::CallExpr(call) => call,
148 _ => return None,
149 };
150 let func = match call.expr()? {
151 ast::Expr::PathExpr(path) => path,
152 _ => return None,
153 };
154 let ident = func.path()?.segment()?.name_ref()?.ident_token()?;
155 normalize(ident.text())
156}
157
158fn from_method_call(expr: &ast::Expr) -> Option<String> {
159 let method = match expr {
160 ast::Expr::MethodCallExpr(call) => call,
161 _ => return None,
162 };
163 let ident = method.name_ref()?.ident_token()?;
164 let mut name = ident.text();
165
166 if USELESS_METHODS.contains(&name) {
167 return None;
168 }
169
170 for prefix in USELESS_METHOD_PREFIXES {
171 if let Some(suffix) = name.strip_prefix(prefix) {
172 name = suffix;
173 break;
174 }
175 }
176
177 normalize(&name)
178}
179
180fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
181 let arg_list = expr.syntax().parent().and_then(ast::ArgList::cast)?;
182 let args_parent = arg_list.syntax().parent()?;
183 let func = match_ast! {
184 match args_parent {
185 ast::CallExpr(call) => {
186 let func = call.expr()?;
187 let func_ty = sema.type_of_expr(&func)?;
188 func_ty.as_callable(sema.db)?
189 },
190 ast::MethodCallExpr(method) => sema.resolve_method_call_as_callable(&method)?,
191 _ => return None,
192 }
193 };
194
195 let (idx, _) = arg_list.args().find_position(|it| it == expr).unwrap();
196 let (pat, _) = func.params(sema.db).into_iter().nth(idx)?;
197 let pat = match pat? {
198 either::Either::Right(pat) => pat,
199 _ => return None,
200 };
201 let name = var_name_from_pat(&pat)?;
202 normalize(&name.to_string())
203}
204
205fn var_name_from_pat(pat: &ast::Pat) -> Option<ast::Name> {
206 match pat {
207 ast::Pat::IdentPat(var) => var.name(),
208 ast::Pat::RefPat(ref_pat) => var_name_from_pat(&ref_pat.pat()?),
209 ast::Pat::BoxPat(box_pat) => var_name_from_pat(&box_pat.pat()?),
210 _ => None,
211 }
212}
213
214fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
215 let ty = sema.type_of_expr(expr)?;
216 let ty = ty.remove_ref().unwrap_or(ty);
217
218 name_of_type(&ty, sema.db)
219}
220
221fn name_of_type(ty: &hir::Type, db: &RootDatabase) -> Option<String> {
222 let name = if let Some(adt) = ty.as_adt() {
223 let name = adt.name(db).to_string();
224
225 if WRAPPER_TYPES.contains(&name.as_str()) {
226 let inner_ty = ty.type_parameters().next()?;
227 return name_of_type(&inner_ty, db);
228 }
229
230 name
231 } else if let Some(trait_) = ty.as_dyn_trait() {
232 trait_name(&trait_, db)?
233 } else if let Some(traits) = ty.as_impl_traits(db) {
234 let mut iter = traits.into_iter().filter_map(|t| trait_name(&t, db));
235 let name = iter.next()?;
236 if iter.next().is_some() {
237 return None;
238 }
239 name
240 } else {
241 return None;
242 };
243 normalize(&name)
244}
245
246fn trait_name(trait_: &hir::Trait, db: &RootDatabase) -> Option<String> {
247 let name = trait_.name(db).to_string();
248 if USELESS_TRAITS.contains(&name.as_str()) {
249 return None;
250 }
251 Some(name)
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257
258 use crate::tests::check_name_suggestion;
259
260 mod from_func_call {
261 use super::*;
262
263 #[test]
264 fn no_args() {
265 check_name_suggestion(
266 |e, _| from_func_call(e),
267 r#"
268 fn foo() {
269 $0bar()$0
270 }"#,
271 "bar",
272 );
273 }
274
275 #[test]
276 fn single_arg() {
277 check_name_suggestion(
278 |e, _| from_func_call(e),
279 r#"
280 fn foo() {
281 $0bar(1)$0
282 }"#,
283 "bar",
284 );
285 }
286
287 #[test]
288 fn many_args() {
289 check_name_suggestion(
290 |e, _| from_func_call(e),
291 r#"
292 fn foo() {
293 $0bar(1, 2, 3)$0
294 }"#,
295 "bar",
296 );
297 }
298
299 #[test]
300 fn path() {
301 check_name_suggestion(
302 |e, _| from_func_call(e),
303 r#"
304 fn foo() {
305 $0i32::bar(1, 2, 3)$0
306 }"#,
307 "bar",
308 );
309 }
310
311 #[test]
312 fn generic_params() {
313 check_name_suggestion(
314 |e, _| from_func_call(e),
315 r#"
316 fn foo() {
317 $0bar::<i32>(1, 2, 3)$0
318 }"#,
319 "bar",
320 );
321 }
322 }
323
324 mod from_method_call {
325 use super::*;
326
327 #[test]
328 fn no_args() {
329 check_name_suggestion(
330 |e, _| from_method_call(e),
331 r#"
332 fn foo() {
333 $0bar.frobnicate()$0
334 }"#,
335 "frobnicate",
336 );
337 }
338
339 #[test]
340 fn generic_params() {
341 check_name_suggestion(
342 |e, _| from_method_call(e),
343 r#"
344 fn foo() {
345 $0bar.frobnicate::<i32, u32>()$0
346 }"#,
347 "frobnicate",
348 );
349 }
350
351 #[test]
352 fn to_name() {
353 check_name_suggestion(
354 |e, _| from_method_call(e),
355 r#"
356 struct Args;
357 struct Config;
358 impl Args {
359 fn to_config(&self) -> Config {}
360 }
361 fn foo() {
362 $0Args.to_config()$0;
363 }"#,
364 "config",
365 );
366 }
367 }
368
369 mod from_param {
370 use crate::tests::check_name_suggestion_not_applicable;
371
372 use super::*;
373
374 #[test]
375 fn plain_func() {
376 check_name_suggestion(
377 from_param,
378 r#"
379 fn bar(n: i32, m: u32);
380 fn foo() {
381 bar($01$0, 2)
382 }"#,
383 "n",
384 );
385 }
386
387 #[test]
388 fn mut_param() {
389 check_name_suggestion(
390 from_param,
391 r#"
392 fn bar(mut n: i32, m: u32);
393 fn foo() {
394 bar($01$0, 2)
395 }"#,
396 "n",
397 );
398 }
399
400 #[test]
401 fn func_does_not_exist() {
402 check_name_suggestion_not_applicable(
403 from_param,
404 r#"
405 fn foo() {
406 bar($01$0, 2)
407 }"#,
408 );
409 }
410
411 #[test]
412 fn unnamed_param() {
413 check_name_suggestion_not_applicable(
414 from_param,
415 r#"
416 fn bar(_: i32, m: u32);
417 fn foo() {
418 bar($01$0, 2)
419 }"#,
420 );
421 }
422
423 #[test]
424 fn tuple_pat() {
425 check_name_suggestion_not_applicable(
426 from_param,
427 r#"
428 fn bar((n, k): (i32, i32), m: u32);
429 fn foo() {
430 bar($0(1, 2)$0, 3)
431 }"#,
432 );
433 }
434
435 #[test]
436 fn ref_pat() {
437 check_name_suggestion(
438 from_param,
439 r#"
440 fn bar(&n: &i32, m: u32);
441 fn foo() {
442 bar($0&1$0, 3)
443 }"#,
444 "n",
445 );
446 }
447
448 #[test]
449 fn box_pat() {
450 check_name_suggestion(
451 from_param,
452 r#"
453 fn bar(box n: &i32, m: u32);
454 fn foo() {
455 bar($01$0, 3)
456 }"#,
457 "n",
458 );
459 }
460
461 #[test]
462 fn param_out_of_index() {
463 check_name_suggestion_not_applicable(
464 from_param,
465 r#"
466 fn bar(n: i32, m: u32);
467 fn foo() {
468 bar(1, 2, $03$0)
469 }"#,
470 );
471 }
472
473 #[test]
474 fn generic_param_resolved() {
475 check_name_suggestion(
476 from_param,
477 r#"
478 fn bar<T>(n: T, m: u32);
479 fn foo() {
480 bar($01$0, 2)
481 }"#,
482 "n",
483 );
484 }
485
486 #[test]
487 fn generic_param_unresolved() {
488 check_name_suggestion(
489 from_param,
490 r#"
491 fn bar<T>(n: T, m: u32);
492 fn foo<T>(x: T) {
493 bar($0x$0, 2)
494 }"#,
495 "n",
496 );
497 }
498
499 #[test]
500 fn method() {
501 check_name_suggestion(
502 from_param,
503 r#"
504 struct S;
505 impl S {
506 fn bar(&self, n: i32, m: u32);
507 }
508 fn foo() {
509 S.bar($01$0, 2)
510 }"#,
511 "n",
512 );
513 }
514
515 #[test]
516 fn method_ufcs() {
517 check_name_suggestion(
518 from_param,
519 r#"
520 struct S;
521 impl S {
522 fn bar(&self, n: i32, m: u32);
523 }
524 fn foo() {
525 S::bar(&S, $01$0, 2)
526 }"#,
527 "n",
528 );
529 }
530
531 #[test]
532 fn method_self() {
533 check_name_suggestion_not_applicable(
534 from_param,
535 r#"
536 struct S;
537 impl S {
538 fn bar(&self, n: i32, m: u32);
539 }
540 fn foo() {
541 S::bar($0&S$0, 1, 2)
542 }"#,
543 );
544 }
545
546 #[test]
547 fn method_self_named() {
548 check_name_suggestion(
549 from_param,
550 r#"
551 struct S;
552 impl S {
553 fn bar(strukt: &Self, n: i32, m: u32);
554 }
555 fn foo() {
556 S::bar($0&S$0, 1, 2)
557 }"#,
558 "strukt",
559 );
560 }
561 }
562
563 mod from_type {
564 use crate::tests::check_name_suggestion_not_applicable;
565
566 use super::*;
567
568 #[test]
569 fn i32() {
570 check_name_suggestion_not_applicable(
571 from_type,
572 r#"
573 fn foo() {
574 let _: i32 = $01$0;
575 }"#,
576 );
577 }
578
579 #[test]
580 fn u64() {
581 check_name_suggestion_not_applicable(
582 from_type,
583 r#"
584 fn foo() {
585 let _: u64 = $01$0;
586 }"#,
587 );
588 }
589
590 #[test]
591 fn bool() {
592 check_name_suggestion_not_applicable(
593 from_type,
594 r#"
595 fn foo() {
596 let _: bool = $0true$0;
597 }"#,
598 );
599 }
600
601 #[test]
602 fn struct_unit() {
603 check_name_suggestion(
604 from_type,
605 r#"
606 struct Seed;
607 fn foo() {
608 let _ = $0Seed$0;
609 }"#,
610 "seed",
611 );
612 }
613
614 #[test]
615 fn struct_unit_to_snake() {
616 check_name_suggestion(
617 from_type,
618 r#"
619 struct SeedState;
620 fn foo() {
621 let _ = $0SeedState$0;
622 }"#,
623 "seed_state",
624 );
625 }
626
627 #[test]
628 fn struct_single_arg() {
629 check_name_suggestion(
630 from_type,
631 r#"
632 struct Seed(u32);
633 fn foo() {
634 let _ = $0Seed(0)$0;
635 }"#,
636 "seed",
637 );
638 }
639
640 #[test]
641 fn struct_with_fields() {
642 check_name_suggestion(
643 from_type,
644 r#"
645 struct Seed { value: u32 }
646 fn foo() {
647 let _ = $0Seed { value: 0 }$0;
648 }"#,
649 "seed",
650 );
651 }
652
653 #[test]
654 fn enum_() {
655 check_name_suggestion(
656 from_type,
657 r#"
658 enum Kind { A, B }
659 fn foo() {
660 let _ = $0Kind::A$0;
661 }"#,
662 "kind",
663 );
664 }
665
666 #[test]
667 fn enum_generic_resolved() {
668 check_name_suggestion(
669 from_type,
670 r#"
671 enum Kind<T> { A(T), B }
672 fn foo() {
673 let _ = $0Kind::A(1)$0;
674 }"#,
675 "kind",
676 );
677 }
678
679 #[test]
680 fn enum_generic_unresolved() {
681 check_name_suggestion(
682 from_type,
683 r#"
684 enum Kind<T> { A(T), B }
685 fn foo<T>(x: T) {
686 let _ = $0Kind::A(x)$0;
687 }"#,
688 "kind",
689 );
690 }
691
692 #[test]
693 fn dyn_trait() {
694 check_name_suggestion(
695 from_type,
696 r#"
697 trait DynHandler {}
698 fn bar() -> dyn DynHandler {}
699 fn foo() {
700 $0bar()$0;
701 }"#,
702 "dyn_handler",
703 );
704 }
705
706 #[test]
707 fn impl_trait() {
708 check_name_suggestion(
709 from_type,
710 r#"
711 trait StaticHandler {}
712 fn bar() -> impl StaticHandler {}
713 fn foo() {
714 $0bar()$0;
715 }"#,
716 "static_handler",
717 );
718 }
719
720 #[test]
721 fn impl_trait_plus_clone() {
722 check_name_suggestion(
723 from_type,
724 r#"
725 trait StaticHandler {}
726 trait Clone {}
727 fn bar() -> impl StaticHandler + Clone {}
728 fn foo() {
729 $0bar()$0;
730 }"#,
731 "static_handler",
732 );
733 }
734
735 #[test]
736 fn impl_trait_plus_lifetime() {
737 check_name_suggestion(
738 from_type,
739 r#"
740 trait StaticHandler {}
741 trait Clone {}
742 fn bar<'a>(&'a i32) -> impl StaticHandler + 'a {}
743 fn foo() {
744 $0bar(&1)$0;
745 }"#,
746 "static_handler",
747 );
748 }
749
750 #[test]
751 fn impl_trait_plus_trait() {
752 check_name_suggestion_not_applicable(
753 from_type,
754 r#"
755 trait Handler {}
756 trait StaticHandler {}
757 fn bar() -> impl StaticHandler + Handler {}
758 fn foo() {
759 $0bar()$0;
760 }"#,
761 );
762 }
763
764 #[test]
765 fn ref_value() {
766 check_name_suggestion(
767 from_type,
768 r#"
769 struct Seed;
770 fn bar() -> &Seed {}
771 fn foo() {
772 $0bar()$0;
773 }"#,
774 "seed",
775 );
776 }
777
778 #[test]
779 fn box_value() {
780 check_name_suggestion(
781 from_type,
782 r#"
783 struct Box<T>(*const T);
784 struct Seed;
785 fn bar() -> Box<Seed> {}
786 fn foo() {
787 $0bar()$0;
788 }"#,
789 "seed",
790 );
791 }
792
793 #[test]
794 fn box_generic() {
795 check_name_suggestion_not_applicable(
796 from_type,
797 r#"
798 struct Box<T>(*const T);
799 fn bar<T>() -> Box<T> {}
800 fn foo<T>() {
801 $0bar::<T>()$0;
802 }"#,
803 );
804 }
805
806 #[test]
807 fn option_value() {
808 check_name_suggestion(
809 from_type,
810 r#"
811 enum Option<T> { Some(T) }
812 struct Seed;
813 fn bar() -> Option<Seed> {}
814 fn foo() {
815 $0bar()$0;
816 }"#,
817 "seed",
818 );
819 }
820
821 #[test]
822 fn result_value() {
823 check_name_suggestion(
824 from_type,
825 r#"
826 enum Result<T, E> { Ok(T), Err(E) }
827 struct Seed;
828 struct Error;
829 fn bar() -> Result<Seed, Error> {}
830 fn foo() {
831 $0bar()$0;
832 }"#,
833 "seed",
834 );
835 }
836 }
837
838 mod variable {
839 use super::*;
840
841 #[test]
842 fn ref_call() {
843 check_name_suggestion(
844 |e, c| Some(variable(e, c)),
845 r#"
846 fn foo() {
847 $0&bar(1, 3)$0
848 }"#,
849 "bar",
850 );
851 }
852
853 #[test]
854 fn name_to_string() {
855 check_name_suggestion(
856 |e, c| Some(variable(e, c)),
857 r#"
858 fn foo() {
859 $0function.name().to_string()$0
860 }"#,
861 "name",
862 );
863 }
864
865 #[test]
866 fn nested_useless_method() {
867 check_name_suggestion(
868 |e, c| Some(variable(e, c)),
869 r#"
870 fn foo() {
871 $0function.name().as_ref().unwrap().to_string()$0
872 }"#,
873 "name",
874 );
875 }
876 }
877}
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index f9de8ce0e..3ff77400b 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -41,6 +41,10 @@ pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option<Cr
41impl FamousDefs<'_, '_> { 41impl FamousDefs<'_, '_> {
42 pub const FIXTURE: &'static str = include_str!("helpers/famous_defs_fixture.rs"); 42 pub const FIXTURE: &'static str = include_str!("helpers/famous_defs_fixture.rs");
43 43
44 pub fn std(&self) -> Option<Crate> {
45 self.find_crate("std")
46 }
47
44 pub fn core(&self) -> Option<Crate> { 48 pub fn core(&self) -> Option<Crate> {
45 self.find_crate("core") 49 self.find_crate("core")
46 } 50 }
diff --git a/crates/ide_db/src/helpers/famous_defs_fixture.rs b/crates/ide_db/src/helpers/famous_defs_fixture.rs
index bb4e9666b..d3464ae17 100644
--- a/crates/ide_db/src/helpers/famous_defs_fixture.rs
+++ b/crates/ide_db/src/helpers/famous_defs_fixture.rs
@@ -129,3 +129,11 @@ pub mod prelude {
129} 129}
130#[prelude_import] 130#[prelude_import]
131pub use prelude::*; 131pub use prelude::*;
132//- /libstd.rs crate:std deps:core
133//! Signatures of traits, types and functions from the std lib for use in tests.
134
135/// Docs for return_keyword
136mod return_keyword {}
137
138/// Docs for prim_str
139mod prim_str {}
diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml
index 8b5cca22c..bb2656a80 100644
--- a/crates/mbe/Cargo.toml
+++ b/crates/mbe/Cargo.toml
@@ -18,6 +18,7 @@ syntax = { path = "../syntax", version = "0.0.0" }
18parser = { path = "../parser", version = "0.0.0" } 18parser = { path = "../parser", version = "0.0.0" }
19tt = { path = "../tt", version = "0.0.0" } 19tt = { path = "../tt", version = "0.0.0" }
20test_utils = { path = "../test_utils", version = "0.0.0" } 20test_utils = { path = "../test_utils", version = "0.0.0" }
21stdx = { path = "../stdx", version = "0.0.0" }
21 22
22[dev-dependencies] 23[dev-dependencies]
23profile = { path = "../profile" } 24profile = { path = "../profile" }
diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs
index 6d81be880..503ad1355 100644
--- a/crates/mbe/src/benchmark.rs
+++ b/crates/mbe/src/benchmark.rs
@@ -40,18 +40,12 @@ fn benchmark_expand_macro_rules() {
40 .into_iter() 40 .into_iter()
41 .map(|(id, tt)| { 41 .map(|(id, tt)| {
42 let res = rules[&id].expand(&tt); 42 let res = rules[&id].expand(&tt);
43 if res.err.is_some() { 43 assert!(res.err.is_none());
44 // FIXME:
45 // Currently `invocation_fixtures` will generate some correct invocations but
46 // cannot be expanded by mbe. We ignore errors here.
47 // See: https://github.com/rust-analyzer/rust-analyzer/issues/4777
48 eprintln!("err from {} {:?}", id, res.err);
49 }
50 res.value.token_trees.len() 44 res.value.token_trees.len()
51 }) 45 })
52 .sum() 46 .sum()
53 }; 47 };
54 assert_eq!(hash, 66995); 48 assert_eq!(hash, 69413);
55} 49}
56 50
57fn macro_rules_fixtures() -> FxHashMap<String, MacroRules> { 51fn macro_rules_fixtures() -> FxHashMap<String, MacroRules> {
@@ -77,7 +71,7 @@ fn macro_rules_fixtures_tt() -> FxHashMap<String, tt::Subtree> {
77 .collect() 71 .collect()
78} 72}
79 73
80// Generate random invocation fixtures from rules 74/// Generate random invocation fixtures from rules
81fn invocation_fixtures(rules: &FxHashMap<String, MacroRules>) -> Vec<(String, tt::Subtree)> { 75fn invocation_fixtures(rules: &FxHashMap<String, MacroRules>) -> Vec<(String, tt::Subtree)> {
82 let mut seed = 123456789; 76 let mut seed = 123456789;
83 let mut res = Vec::new(); 77 let mut res = Vec::new();
@@ -86,11 +80,31 @@ fn invocation_fixtures(rules: &FxHashMap<String, MacroRules>) -> Vec<(String, tt
86 for rule in &it.rules { 80 for rule in &it.rules {
87 // Generate twice 81 // Generate twice
88 for _ in 0..2 { 82 for _ in 0..2 {
89 let mut subtree = tt::Subtree::default(); 83 // The input are generated by filling the `Op` randomly.
90 for op in rule.lhs.iter() { 84 // However, there are some cases generated are ambiguous for expanding, for example:
91 collect_from_op(op, &mut subtree, &mut seed); 85 // ```rust
86 // macro_rules! m {
87 // ($($t:ident),* as $ty:ident) => {}
88 // }
89 // m!(as u32); // error: local ambiguity: multiple parsing options: built-in NTs ident ('t') or 1 other option.
90 // ```
91 //
92 // So we just skip any error cases and try again
93 let mut try_cnt = 0;
94 loop {
95 let mut subtree = tt::Subtree::default();
96 for op in rule.lhs.iter() {
97 collect_from_op(op, &mut subtree, &mut seed);
98 }
99 if it.expand(&subtree).err.is_none() {
100 res.push((name.clone(), subtree));
101 break;
102 }
103 try_cnt += 1;
104 if try_cnt > 100 {
105 panic!("invocaton fixture {} cannot be generated.\n", name);
106 }
92 } 107 }
93 res.push((name.clone(), subtree));
94 } 108 }
95 } 109 }
96 } 110 }
diff --git a/crates/mbe/src/expander.rs b/crates/mbe/src/expander.rs
index e7e14b3cc..2efff8f52 100644
--- a/crates/mbe/src/expander.rs
+++ b/crates/mbe/src/expander.rs
@@ -5,7 +5,7 @@
5mod matcher; 5mod matcher;
6mod transcriber; 6mod transcriber;
7 7
8use rustc_hash::FxHashMap; 8use smallvec::SmallVec;
9use syntax::SmolStr; 9use syntax::SmolStr;
10 10
11use crate::{ExpandError, ExpandResult}; 11use crate::{ExpandError, ExpandResult};
@@ -28,10 +28,10 @@ pub(crate) fn expand_rules(
28 return ExpandResult::ok(value); 28 return ExpandResult::ok(value);
29 } 29 }
30 } 30 }
31 // Use the rule if we matched more tokens, or had fewer errors 31 // Use the rule if we matched more tokens, or bound variables count
32 if let Some((prev_match, _)) = &match_ { 32 if let Some((prev_match, _)) = &match_ {
33 if (new_match.unmatched_tts, new_match.err_count) 33 if (new_match.unmatched_tts, -(new_match.bound_count as i32))
34 < (prev_match.unmatched_tts, prev_match.err_count) 34 < (prev_match.unmatched_tts, -(prev_match.bound_count as i32))
35 { 35 {
36 match_ = Some((new_match, rule)); 36 match_ = Some((new_match, rule));
37 } 37 }
@@ -94,19 +94,19 @@ pub(crate) fn expand_rules(
94/// In other words, `Bindings` is a *multi* mapping from `SmolStr` to 94/// In other words, `Bindings` is a *multi* mapping from `SmolStr` to
95/// `tt::TokenTree`, where the index to select a particular `TokenTree` among 95/// `tt::TokenTree`, where the index to select a particular `TokenTree` among
96/// many is not a plain `usize`, but an `&[usize]`. 96/// many is not a plain `usize`, but an `&[usize]`.
97#[derive(Debug, Default)] 97#[derive(Debug, Default, Clone, PartialEq, Eq)]
98struct Bindings { 98struct Bindings {
99 inner: FxHashMap<SmolStr, Binding>, 99 inner: SmallVec<[(SmolStr, Binding); 4]>,
100} 100}
101 101
102#[derive(Debug)] 102#[derive(Debug, Clone, PartialEq, Eq)]
103enum Binding { 103enum Binding {
104 Fragment(Fragment), 104 Fragment(Fragment),
105 Nested(Vec<Binding>), 105 Nested(Vec<Binding>),
106 Empty, 106 Empty,
107} 107}
108 108
109#[derive(Debug, Clone)] 109#[derive(Debug, Clone, PartialEq, Eq)]
110enum Fragment { 110enum Fragment {
111 /// token fragments are just copy-pasted into the output 111 /// token fragments are just copy-pasted into the output
112 Tokens(tt::TokenTree), 112 Tokens(tt::TokenTree),
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs
index e3bd4c09a..9d3d28055 100644
--- a/crates/mbe/src/expander/matcher.rs
+++ b/crates/mbe/src/expander/matcher.rs
@@ -1,14 +1,74 @@
1//! FIXME: write short doc here 1//! An NFA-based parser, which is porting from rustc mbe parsing code
2//!
3//! See https://github.com/rust-lang/rust/blob/70b18bc2cbac4712020019f5bf57c00905373205/compiler/rustc_expand/src/mbe/macro_parser.rs
4//! Here is a quick intro to how the parser works, copied from rustc:
5//!
6//! A 'position' is a dot in the middle of a matcher, usually represented as a
7//! dot. For example `· a $( a )* a b` is a position, as is `a $( · a )* a b`.
8//!
9//! The parser walks through the input a character at a time, maintaining a list
10//! of threads consistent with the current position in the input string: `cur_items`.
11//!
12//! As it processes them, it fills up `eof_items` with threads that would be valid if
13//! the macro invocation is now over, `bb_items` with threads that are waiting on
14//! a Rust non-terminal like `$e:expr`, and `next_items` with threads that are waiting
15//! on a particular token. Most of the logic concerns moving the · through the
16//! repetitions indicated by Kleene stars. The rules for moving the · without
17//! consuming any input are called epsilon transitions. It only advances or calls
18//! out to the real Rust parser when no `cur_items` threads remain.
19//!
20//! Example:
21//!
22//! ```text, ignore
23//! Start parsing a a a a b against [· a $( a )* a b].
24//!
25//! Remaining input: a a a a b
26//! next: [· a $( a )* a b]
27//!
28//! - - - Advance over an a. - - -
29//!
30//! Remaining input: a a a b
31//! cur: [a · $( a )* a b]
32//! Descend/Skip (first item).
33//! next: [a $( · a )* a b] [a $( a )* · a b].
34//!
35//! - - - Advance over an a. - - -
36//!
37//! Remaining input: a a b
38//! cur: [a $( a · )* a b] [a $( a )* a · b]
39//! Follow epsilon transition: Finish/Repeat (first item)
40//! next: [a $( a )* · a b] [a $( · a )* a b] [a $( a )* a · b]
41//!
42//! - - - Advance over an a. - - - (this looks exactly like the last step)
43//!
44//! Remaining input: a b
45//! cur: [a $( a · )* a b] [a $( a )* a · b]
46//! Follow epsilon transition: Finish/Repeat (first item)
47//! next: [a $( a )* · a b] [a $( · a )* a b] [a $( a )* a · b]
48//!
49//! - - - Advance over an a. - - - (this looks exactly like the last step)
50//!
51//! Remaining input: b
52//! cur: [a $( a · )* a b] [a $( a )* a · b]
53//! Follow epsilon transition: Finish/Repeat (first item)
54//! next: [a $( a )* · a b] [a $( · a )* a b] [a $( a )* a · b]
55//!
56//! - - - Advance over a b. - - -
57//!
58//! Remaining input: ''
59//! eof: [a $( a )* a b ·]
60//! ```
2 61
3use crate::{ 62use crate::{
4 expander::{Binding, Bindings, Fragment}, 63 expander::{Binding, Bindings, Fragment},
5 parser::{Op, RepeatKind, Separator}, 64 parser::{Op, OpDelimited, OpDelimitedIter, RepeatKind, Separator},
6 tt_iter::TtIter, 65 tt_iter::TtIter,
7 ExpandError, MetaTemplate, 66 ExpandError, MetaTemplate,
8}; 67};
9 68
10use super::ExpandResult; 69use super::ExpandResult;
11use parser::FragmentKind::*; 70use parser::FragmentKind::*;
71use smallvec::{smallvec, SmallVec};
12use syntax::SmolStr; 72use syntax::SmolStr;
13 73
14impl Bindings { 74impl Bindings {
@@ -16,19 +76,19 @@ impl Bindings {
16 // FIXME: Do we have a better way to represent an empty token ? 76 // FIXME: Do we have a better way to represent an empty token ?
17 // Insert an empty subtree for empty token 77 // Insert an empty subtree for empty token
18 let tt = tt::Subtree::default().into(); 78 let tt = tt::Subtree::default().into();
19 self.inner.insert(name.clone(), Binding::Fragment(Fragment::Tokens(tt))); 79 self.inner.push((name.clone(), Binding::Fragment(Fragment::Tokens(tt))));
20 } 80 }
21 81
22 fn push_empty(&mut self, name: &SmolStr) { 82 fn push_empty(&mut self, name: &SmolStr) {
23 self.inner.insert(name.clone(), Binding::Empty); 83 self.inner.push((name.clone(), Binding::Empty));
24 } 84 }
25 85
26 fn push_nested(&mut self, idx: usize, nested: Bindings) -> Result<(), ExpandError> { 86 fn push_nested(&mut self, idx: usize, nested: Bindings) -> Result<(), ExpandError> {
27 for (key, value) in nested.inner { 87 for (key, value) in nested.inner {
28 if !self.inner.contains_key(&key) { 88 if self.get_mut(&key).is_none() {
29 self.inner.insert(key.clone(), Binding::Nested(Vec::new())); 89 self.inner.push((key.clone(), Binding::Nested(Vec::new())));
30 } 90 }
31 match self.inner.get_mut(&key) { 91 match self.get_mut(&key) {
32 Some(Binding::Nested(it)) => { 92 Some(Binding::Nested(it)) => {
33 // insert empty nested bindings before this one 93 // insert empty nested bindings before this one
34 while it.len() < idx { 94 while it.len() < idx {
@@ -46,6 +106,14 @@ impl Bindings {
46 } 106 }
47 Ok(()) 107 Ok(())
48 } 108 }
109
110 fn get_mut(&mut self, name: &str) -> Option<&mut Binding> {
111 self.inner.iter_mut().find_map(|(n, b)| if n == name { Some(b) } else { None })
112 }
113
114 fn bindings(&self) -> impl Iterator<Item = &Binding> {
115 self.inner.iter().map(|(_, b)| b)
116 }
49} 117}
50 118
51macro_rules! err { 119macro_rules! err {
@@ -57,7 +125,7 @@ macro_rules! err {
57 }; 125 };
58} 126}
59 127
60#[derive(Debug, Default)] 128#[derive(Clone, Debug, Default, PartialEq, Eq)]
61pub(super) struct Match { 129pub(super) struct Match {
62 pub(super) bindings: Bindings, 130 pub(super) bindings: Bindings,
63 /// We currently just keep the first error and count the rest to compare matches. 131 /// We currently just keep the first error and count the rest to compare matches.
@@ -65,6 +133,8 @@ pub(super) struct Match {
65 pub(super) err_count: usize, 133 pub(super) err_count: usize,
66 /// How many top-level token trees were left to match. 134 /// How many top-level token trees were left to match.
67 pub(super) unmatched_tts: usize, 135 pub(super) unmatched_tts: usize,
136 /// The number of bound variables
137 pub(super) bound_count: usize,
68} 138}
69 139
70impl Match { 140impl Match {
@@ -76,72 +146,373 @@ impl Match {
76} 146}
77 147
78/// Matching errors are added to the `Match`. 148/// Matching errors are added to the `Match`.
79pub(super) fn match_(pattern: &MetaTemplate, src: &tt::Subtree) -> Match { 149pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match {
80 let mut res = Match::default(); 150 let mut res = match_loop(pattern, &input);
81 let mut src = TtIter::new(src); 151 res.bound_count = count(res.bindings.bindings());
152 return res;
153
154 fn count<'a>(bindings: impl Iterator<Item = &'a Binding>) -> usize {
155 bindings
156 .map(|it| match it {
157 Binding::Fragment(_) => 1,
158 Binding::Empty => 1,
159 Binding::Nested(it) => count(it.iter()),
160 })
161 .sum()
162 }
163}
82 164
83 match_tokens(&mut res, pattern, &mut src); 165#[derive(Debug, Clone)]
166struct MatchState<'t> {
167 /// The position of the "dot" in this matcher
168 dot: OpDelimitedIter<'t>,
84 169
85 if src.len() > 0 { 170 /// Token subtree stack
86 res.unmatched_tts += src.len(); 171 /// When matching against matchers with nested delimited submatchers (e.g., `pat ( pat ( .. )
87 res.add_err(err!("leftover tokens")); 172 /// pat ) pat`), we need to keep track of the matchers we are descending into. This stack does
88 } 173 /// that where the bottom of the stack is the outermost matcher.
174 stack: SmallVec<[OpDelimitedIter<'t>; 4]>,
175
176 /// The "parent" matcher position if we are in a repetition. That is, the matcher position just
177 /// before we enter the repetition.
178 up: Option<Box<MatchState<'t>>>,
179
180 /// The separator if we are in a repetition.
181 sep: Option<Separator>,
182
183 /// The KleeneOp of this sequence if we are in a repetition.
184 sep_kind: Option<RepeatKind>,
89 185
90 res 186 /// Number of tokens of seperator parsed
187 sep_parsed: Option<usize>,
188
189 /// Matched meta variables bindings
190 bindings: SmallVec<[Bindings; 4]>,
191
192 /// Cached result of meta variable parsing
193 meta_result: Option<(TtIter<'t>, ExpandResult<Option<Fragment>>)>,
194
195 /// Is error occuried in this state, will `poised` to "parent"
196 is_error: bool,
91} 197}
92 198
93fn match_tokens(res: &mut Match, pattern: &MetaTemplate, src: &mut TtIter) { 199/// Process the matcher positions of `cur_items` until it is empty. In the process, this will
94 for op in pattern.iter() { 200/// produce more items in `next_items`, `eof_items`, and `bb_items`.
95 match op { 201///
96 Op::Leaf(lhs) => { 202/// For more info about the how this happens, see the module-level doc comments and the inline
97 if let Err(err) = match_leaf(lhs, src) { 203/// comments of this function.
98 res.add_err(err); 204///
99 continue; 205/// # Parameters
206///
207/// - `src`: the current token of the parser.
208/// - `stack`: the "parent" frames of the token tree
209/// - `res`: the match result to store errors
210/// - `cur_items`: the set of current items to be processed. This should be empty by the end of a
211/// successful execution of this function.
212/// - `next_items`: the set of newly generated items. These are used to replenish `cur_items` in
213/// the function `parse`.
214/// - `eof_items`: the set of items that would be valid if this was the EOF.
215/// - `bb_items`: the set of items that are waiting for the black-box parser.
216/// - `error_items`: the set of items in errors, used for error-resilient parsing
217fn match_loop_inner<'t>(
218 src: TtIter<'t>,
219 stack: &[TtIter<'t>],
220 res: &mut Match,
221 cur_items: &mut SmallVec<[MatchState<'t>; 1]>,
222 bb_items: &mut SmallVec<[MatchState<'t>; 1]>,
223 next_items: &mut Vec<MatchState<'t>>,
224 eof_items: &mut SmallVec<[MatchState<'t>; 1]>,
225 error_items: &mut SmallVec<[MatchState<'t>; 1]>,
226) {
227 macro_rules! try_push {
228 ($items: expr, $it:expr) => {
229 if $it.is_error {
230 error_items.push($it);
231 } else {
232 $items.push($it);
233 }
234 };
235 }
236
237 while let Some(mut item) = cur_items.pop() {
238 while item.dot.is_eof() {
239 match item.stack.pop() {
240 Some(frame) => {
241 item.dot = frame;
242 item.dot.next();
100 } 243 }
244 None => break,
101 } 245 }
102 Op::Subtree { tokens, delimiter: delim } => { 246 }
103 let rhs = match src.expect_subtree() { 247 let op = match item.dot.peek() {
104 Ok(s) => s, 248 None => {
105 Err(()) => { 249 // We are at or past the end of the matcher of `item`.
106 res.add_err(err!("expected subtree")); 250 if item.up.is_some() {
107 continue; 251 if item.sep_parsed.is_none() {
252 // Get the `up` matcher
253 let mut new_pos = *item.up.clone().unwrap();
254 // Add matches from this repetition to the `matches` of `up`
255 if let Some(bindings) = new_pos.bindings.last_mut() {
256 for (i, b) in item.bindings.iter_mut().enumerate() {
257 bindings.push_nested(i, b.clone()).unwrap();
258 }
259 }
260 // Move the "dot" past the repetition in `up`
261 new_pos.dot.next();
262 new_pos.is_error = new_pos.is_error || item.is_error;
263 cur_items.push(new_pos);
264 }
265
266 // Check if we need a separator.
267 // We check the separator one by one
268 let sep_idx = *item.sep_parsed.as_ref().unwrap_or(&0);
269 let sep_len = item.sep.as_ref().map_or(0, Separator::tt_count);
270 if item.sep.is_some() && sep_idx != sep_len {
271 let sep = item.sep.as_ref().unwrap();
272 if src.clone().expect_separator(&sep, sep_idx) {
273 item.dot.next();
274 item.sep_parsed = Some(sep_idx + 1);
275 try_push!(next_items, item);
276 }
277 }
278 // We don't need a separator. Move the "dot" back to the beginning of the matcher
279 // and try to match again UNLESS we are only allowed to have _one_ repetition.
280 else if item.sep_kind != Some(RepeatKind::ZeroOrOne) {
281 item.dot = item.dot.reset();
282 item.sep_parsed = None;
283 item.bindings.push(Bindings::default());
284 cur_items.push(item);
285 }
286 } else {
287 // If we are not in a repetition, then being at the end of a matcher means that we have
288 // reached the potential end of the input.
289 try_push!(eof_items, item);
290 }
291 continue;
292 }
293 Some(it) => it,
294 };
295
296 // We are in the middle of a matcher.
297 match op {
298 OpDelimited::Op(Op::Repeat { tokens, kind, separator }) => {
299 if matches!(kind, RepeatKind::ZeroOrMore | RepeatKind::ZeroOrOne) {
300 let mut new_item = item.clone();
301 new_item.dot.next();
302 let mut vars = Vec::new();
303 let bindings = new_item.bindings.last_mut().unwrap();
304 collect_vars(&mut vars, tokens);
305 for var in vars {
306 bindings.push_empty(&var);
108 } 307 }
109 }; 308 cur_items.push(new_item);
110 if delim.map(|it| it.kind) != rhs.delimiter_kind() {
111 res.add_err(err!("mismatched delimiter"));
112 continue;
113 } 309 }
114 let mut src = TtIter::new(rhs); 310 cur_items.push(MatchState {
115 match_tokens(res, tokens, &mut src); 311 dot: tokens.iter_delimited(None),
116 if src.len() > 0 { 312 stack: Default::default(),
117 res.add_err(err!("leftover tokens")); 313 up: Some(Box::new(item)),
314 sep: separator.clone(),
315 sep_kind: Some(*kind),
316 sep_parsed: None,
317 bindings: smallvec![Bindings::default()],
318 meta_result: None,
319 is_error: false,
320 })
321 }
322 OpDelimited::Op(Op::Subtree { tokens, delimiter }) => {
323 if let Ok(subtree) = src.clone().expect_subtree() {
324 if subtree.delimiter_kind() == delimiter.map(|it| it.kind) {
325 item.stack.push(item.dot);
326 item.dot = tokens.iter_delimited(delimiter.as_ref());
327 cur_items.push(item);
328 }
118 } 329 }
119 } 330 }
120 Op::Var { name, kind, .. } => { 331 OpDelimited::Op(Op::Var { kind, name, .. }) => {
121 let kind = match kind { 332 if let Some(kind) = kind {
122 Some(k) => k, 333 let mut fork = src.clone();
123 None => { 334 let match_res = match_meta_var(kind.as_str(), &mut fork);
124 res.add_err(ExpandError::UnexpectedToken); 335 match match_res.err {
125 continue; 336 None => {
337 // Some meta variables are optional (e.g. vis)
338 if match_res.value.is_some() {
339 item.meta_result = Some((fork, match_res));
340 try_push!(bb_items, item);
341 } else {
342 item.bindings.last_mut().unwrap().push_optional(name);
343 item.dot.next();
344 cur_items.push(item);
345 }
346 }
347 Some(err) => {
348 res.add_err(err);
349 match match_res.value {
350 Some(fragment) => {
351 item.bindings
352 .last_mut()
353 .unwrap()
354 .inner
355 .push((name.clone(), Binding::Fragment(fragment)));
356 }
357 _ => {}
358 }
359 item.is_error = true;
360 error_items.push(item);
361 }
126 } 362 }
127 }; 363 }
128 let ExpandResult { value: matched, err: match_err } = 364 }
129 match_meta_var(kind.as_str(), src); 365 OpDelimited::Op(Op::Leaf(leaf)) => {
130 match matched { 366 if let Err(err) = match_leaf(&leaf, &mut src.clone()) {
367 res.add_err(err);
368 item.is_error = true;
369 } else {
370 item.dot.next();
371 }
372 try_push!(next_items, item);
373 }
374 OpDelimited::Open => {
375 if matches!(src.clone().next(), Some(tt::TokenTree::Subtree(..))) {
376 item.dot.next();
377 try_push!(next_items, item);
378 }
379 }
380 OpDelimited::Close => {
381 let is_delim_closed = src.peek_n(0).is_none() && !stack.is_empty();
382 if is_delim_closed {
383 item.dot.next();
384 try_push!(next_items, item);
385 }
386 }
387 }
388 }
389}
390
391fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
392 let mut src = TtIter::new(src);
393 let mut stack: SmallVec<[TtIter; 1]> = SmallVec::new();
394 let mut res = Match::default();
395 let mut error_reover_item = None;
396
397 let mut cur_items = smallvec![MatchState {
398 dot: pattern.iter_delimited(None),
399 stack: Default::default(),
400 up: None,
401 sep: None,
402 sep_kind: None,
403 sep_parsed: None,
404 bindings: smallvec![Bindings::default()],
405 is_error: false,
406 meta_result: None,
407 }];
408
409 let mut next_items = vec![];
410
411 loop {
412 let mut bb_items = SmallVec::new();
413 let mut eof_items = SmallVec::new();
414 let mut error_items = SmallVec::new();
415
416 stdx::always!(next_items.is_empty());
417
418 match_loop_inner(
419 src.clone(),
420 &stack,
421 &mut res,
422 &mut cur_items,
423 &mut bb_items,
424 &mut next_items,
425 &mut eof_items,
426 &mut error_items,
427 );
428 stdx::always!(cur_items.is_empty());
429
430 if error_items.len() > 0 {
431 error_reover_item = error_items.pop();
432 } else if eof_items.len() > 0 {
433 error_reover_item = Some(eof_items[0].clone());
434 }
435
436 // We need to do some post processing after the `match_loop_inner`.
437 // If we reached the EOF, check that there is EXACTLY ONE possible matcher. Otherwise,
438 // either the parse is ambiguous (which should never happen) or there is a syntax error.
439 if src.peek_n(0).is_none() && stack.is_empty() {
440 if eof_items.len() == 1 {
441 // remove all errors, because it is the correct answer !
442 res = Match::default();
443 res.bindings = eof_items[0].bindings[0].clone();
444 } else {
445 // Error recovery
446 if error_reover_item.is_some() {
447 res.bindings = error_reover_item.unwrap().bindings[0].clone();
448 }
449 res.add_err(ExpandError::UnexpectedToken);
450 }
451 return res;
452 }
453
454 // If there are no possible next positions AND we aren't waiting for the black-box parser,
455 // then there is a syntax error.
456 //
457 // Another possibility is that we need to call out to parse some rust nonterminal
458 // (black-box) parser. However, if there is not EXACTLY ONE of these, something is wrong.
459 if (bb_items.is_empty() && next_items.is_empty())
460 || (!bb_items.is_empty() && !next_items.is_empty())
461 || bb_items.len() > 1
462 {
463 res.unmatched_tts += src.len();
464 while let Some(it) = stack.pop() {
465 src = it;
466 res.unmatched_tts += src.len();
467 }
468 res.add_err(err!("leftover tokens"));
469
470 if let Some(mut error_reover_item) = error_reover_item {
471 res.bindings = error_reover_item.bindings.remove(0);
472 }
473 return res;
474 }
475 // Dump all possible `next_items` into `cur_items` for the next iteration.
476 else if !next_items.is_empty() {
477 // Now process the next token
478 cur_items.extend(next_items.drain(..));
479
480 match src.next() {
481 Some(tt::TokenTree::Subtree(subtree)) => {
482 stack.push(src.clone());
483 src = TtIter::new(subtree);
484 }
485 None if !stack.is_empty() => src = stack.pop().unwrap(),
486 _ => (),
487 }
488 }
489 // Finally, we have the case where we need to call the black-box parser to get some
490 // nonterminal.
491 else {
492 stdx::always!(bb_items.len() == 1);
493 let mut item = bb_items.pop().unwrap();
494
495 if let Some(OpDelimited::Op(Op::Var { name, .. })) = item.dot.peek() {
496 let (iter, match_res) = item.meta_result.take().unwrap();
497 let bindings = item.bindings.last_mut().unwrap();
498 match match_res.value {
131 Some(fragment) => { 499 Some(fragment) => {
132 res.bindings.inner.insert(name.clone(), Binding::Fragment(fragment)); 500 bindings.inner.push((name.clone(), Binding::Fragment(fragment)));
133 } 501 }
134 None if match_err.is_none() => res.bindings.push_optional(name), 502 None if match_res.err.is_none() => bindings.push_optional(name),
135 _ => {} 503 _ => {}
136 } 504 }
137 if let Some(err) = match_err { 505 if let Some(err) = match_res.err {
138 res.add_err(err); 506 res.add_err(err);
139 } 507 }
508 src = iter.clone();
509 item.dot.next();
510 } else {
511 unreachable!()
140 } 512 }
141 Op::Repeat { tokens: subtree, kind, separator } => { 513 cur_items.push(item);
142 match_repeat(res, subtree, *kind, separator, src);
143 }
144 } 514 }
515 stdx::always!(!cur_items.is_empty());
145 } 516 }
146} 517}
147 518
@@ -173,73 +544,6 @@ fn match_leaf(lhs: &tt::Leaf, src: &mut TtIter) -> Result<(), ExpandError> {
173 Ok(()) 544 Ok(())
174} 545}
175 546
176fn match_repeat(
177 res: &mut Match,
178 pattern: &MetaTemplate,
179 kind: RepeatKind,
180 separator: &Option<Separator>,
181 src: &mut TtIter,
182) {
183 // Dirty hack to make macro-expansion terminate.
184 // This should be replaced by a proper macro-by-example implementation
185 let mut limit = 65536;
186 let mut counter = 0;
187
188 for i in 0.. {
189 let mut fork = src.clone();
190
191 if let Some(separator) = &separator {
192 if i != 0 && !fork.eat_separator(separator) {
193 break;
194 }
195 }
196
197 let mut nested = Match::default();
198 match_tokens(&mut nested, pattern, &mut fork);
199 if nested.err.is_none() {
200 limit -= 1;
201 if limit == 0 {
202 log::warn!(
203 "match_lhs exceeded repeat pattern limit => {:#?}\n{:#?}\n{:#?}\n{:#?}",
204 pattern,
205 src,
206 kind,
207 separator
208 );
209 break;
210 }
211 *src = fork;
212
213 if let Err(err) = res.bindings.push_nested(counter, nested.bindings) {
214 res.add_err(err);
215 }
216 counter += 1;
217 if counter == 1 {
218 if let RepeatKind::ZeroOrOne = kind {
219 break;
220 }
221 }
222 } else {
223 break;
224 }
225 }
226
227 match (kind, counter) {
228 (RepeatKind::OneOrMore, 0) => {
229 res.add_err(ExpandError::UnexpectedToken);
230 }
231 (_, 0) => {
232 // Collect all empty variables in subtrees
233 let mut vars = Vec::new();
234 collect_vars(&mut vars, pattern);
235 for var in vars {
236 res.bindings.push_empty(&var)
237 }
238 }
239 _ => (),
240 }
241}
242
243fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragment>> { 547fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragment>> {
244 let fragment = match kind { 548 let fragment = match kind {
245 "path" => Path, 549 "path" => Path,
@@ -303,14 +607,14 @@ fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &MetaTemplate) {
303} 607}
304 608
305impl<'a> TtIter<'a> { 609impl<'a> TtIter<'a> {
306 fn eat_separator(&mut self, separator: &Separator) -> bool { 610 fn expect_separator(&mut self, separator: &Separator, idx: usize) -> bool {
307 let mut fork = self.clone(); 611 let mut fork = self.clone();
308 let ok = match separator { 612 let ok = match separator {
309 Separator::Ident(lhs) => match fork.expect_ident() { 613 Separator::Ident(lhs) if idx == 0 => match fork.expect_ident() {
310 Ok(rhs) => rhs.text == lhs.text, 614 Ok(rhs) => rhs.text == lhs.text,
311 _ => false, 615 _ => false,
312 }, 616 },
313 Separator::Literal(lhs) => match fork.expect_literal() { 617 Separator::Literal(lhs) if idx == 0 => match fork.expect_literal() {
314 Ok(rhs) => match rhs { 618 Ok(rhs) => match rhs {
315 tt::Leaf::Literal(rhs) => rhs.text == lhs.text, 619 tt::Leaf::Literal(rhs) => rhs.text == lhs.text,
316 tt::Leaf::Ident(rhs) => rhs.text == lhs.text, 620 tt::Leaf::Ident(rhs) => rhs.text == lhs.text,
@@ -318,10 +622,11 @@ impl<'a> TtIter<'a> {
318 }, 622 },
319 _ => false, 623 _ => false,
320 }, 624 },
321 Separator::Puncts(lhss) => lhss.iter().all(|lhs| match fork.expect_punct() { 625 Separator::Puncts(lhss) if idx < lhss.len() => match fork.expect_punct() {
322 Ok(rhs) => rhs.char == lhs.char, 626 Ok(rhs) => rhs.char == lhss[idx].char,
323 _ => false, 627 _ => false,
324 }), 628 },
629 _ => false,
325 }; 630 };
326 if ok { 631 if ok {
327 *self = fork; 632 *self = fork;
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs
index 78368a33e..ad9953a7d 100644
--- a/crates/mbe/src/expander/transcriber.rs
+++ b/crates/mbe/src/expander/transcriber.rs
@@ -13,13 +13,17 @@ use crate::{
13 13
14impl Bindings { 14impl Bindings {
15 fn contains(&self, name: &str) -> bool { 15 fn contains(&self, name: &str) -> bool {
16 self.inner.contains_key(name) 16 self.inner.iter().any(|(n, _)| n == name)
17 } 17 }
18 18
19 fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<&Fragment, ExpandError> { 19 fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<&Fragment, ExpandError> {
20 let mut b = self.inner.get(name).ok_or_else(|| { 20 let mut b: &Binding = self
21 ExpandError::BindingError(format!("could not find binding `{}`", name)) 21 .inner
22 })?; 22 .iter()
23 .find_map(|(n, b)| if n == name { Some(b) } else { None })
24 .ok_or_else(|| {
25 ExpandError::BindingError(format!("could not find binding `{}`", name))
26 })?;
23 for nesting_state in nesting.iter_mut() { 27 for nesting_state in nesting.iter_mut() {
24 nesting_state.hit = true; 28 nesting_state.hit = true;
25 b = match b { 29 b = match b {
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 4c298f85f..f3d2da55a 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -21,7 +21,7 @@ use test_utils::mark;
21pub use tt::{Delimiter, DelimiterKind, Punct}; 21pub use tt::{Delimiter, DelimiterKind, Punct};
22 22
23use crate::{ 23use crate::{
24 parser::{parse_pattern, parse_template, Op}, 24 parser::{parse_pattern, parse_template, MetaTemplate, Op},
25 tt_iter::TtIter, 25 tt_iter::TtIter,
26}; 26};
27 27
@@ -94,15 +94,6 @@ struct Rule {
94 rhs: MetaTemplate, 94 rhs: MetaTemplate,
95} 95}
96 96
97#[derive(Clone, Debug, PartialEq, Eq)]
98struct MetaTemplate(Vec<Op>);
99
100impl<'a> MetaTemplate {
101 fn iter(&self) -> impl Iterator<Item = &Op> {
102 self.0.iter()
103 }
104}
105
106#[derive(Clone, Copy, Debug, PartialEq, Eq)] 97#[derive(Clone, Copy, Debug, PartialEq, Eq)]
107struct Shift(u32); 98struct Shift(u32);
108 99
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index f891ec29c..8671322e1 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -5,7 +5,75 @@ use smallvec::SmallVec;
5use syntax::SmolStr; 5use syntax::SmolStr;
6use tt::Delimiter; 6use tt::Delimiter;
7 7
8use crate::{tt_iter::TtIter, MetaTemplate, ParseError}; 8use crate::{tt_iter::TtIter, ParseError};
9
10#[derive(Clone, Debug, PartialEq, Eq)]
11pub(crate) struct MetaTemplate(pub(crate) Vec<Op>);
12
13#[derive(Debug, Clone, Copy)]
14pub(crate) enum OpDelimited<'a> {
15 Op(&'a Op),
16 Open,
17 Close,
18}
19
20#[derive(Debug, Clone, Copy)]
21pub(crate) struct OpDelimitedIter<'a> {
22 inner: &'a Vec<Op>,
23 delimited: Option<&'a Delimiter>,
24 idx: usize,
25}
26
27impl<'a> OpDelimitedIter<'a> {
28 pub(crate) fn is_eof(&self) -> bool {
29 let len = self.inner.len() + if self.delimited.is_some() { 2 } else { 0 };
30 self.idx >= len
31 }
32
33 pub(crate) fn peek(&self) -> Option<OpDelimited<'a>> {
34 match self.delimited {
35 None => self.inner.get(self.idx).map(OpDelimited::Op),
36 Some(_) => match self.idx {
37 0 => Some(OpDelimited::Open),
38 i if i == self.inner.len() + 1 => Some(OpDelimited::Close),
39 i => self.inner.get(i - 1).map(OpDelimited::Op),
40 },
41 }
42 }
43
44 pub(crate) fn reset(&self) -> Self {
45 Self { inner: &self.inner, idx: 0, delimited: self.delimited }
46 }
47}
48
49impl<'a> Iterator for OpDelimitedIter<'a> {
50 type Item = OpDelimited<'a>;
51
52 fn next(&mut self) -> Option<Self::Item> {
53 let res = self.peek();
54 self.idx += 1;
55 res
56 }
57
58 fn size_hint(&self) -> (usize, Option<usize>) {
59 let len = self.inner.len() + if self.delimited.is_some() { 2 } else { 0 };
60 let remain = len.checked_sub(self.idx).unwrap_or(0);
61 (remain, Some(remain))
62 }
63}
64
65impl<'a> MetaTemplate {
66 pub(crate) fn iter(&self) -> impl Iterator<Item = &Op> {
67 self.0.iter()
68 }
69
70 pub(crate) fn iter_delimited(
71 &'a self,
72 delimited: Option<&'a Delimiter>,
73 ) -> OpDelimitedIter<'a> {
74 OpDelimitedIter { inner: &self.0, idx: 0, delimited }
75 }
76}
9 77
10#[derive(Clone, Debug, PartialEq, Eq)] 78#[derive(Clone, Debug, PartialEq, Eq)]
11pub(crate) enum Op { 79pub(crate) enum Op {
@@ -47,6 +115,16 @@ impl PartialEq for Separator {
47 } 115 }
48} 116}
49 117
118impl Separator {
119 pub(crate) fn tt_count(&self) -> usize {
120 match self {
121 Separator::Literal(_) => 1,
122 Separator::Ident(_) => 1,
123 Separator::Puncts(it) => it.len(),
124 }
125 }
126}
127
50pub(crate) fn parse_template(template: &tt::Subtree) -> Result<Vec<Op>, ParseError> { 128pub(crate) fn parse_template(template: &tt::Subtree) -> Result<Vec<Op>, ParseError> {
51 parse_inner(&template, Mode::Template).into_iter().collect() 129 parse_inner(&template, Mode::Template).into_iter().collect()
52} 130}
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index f1eadcd1e..5c641ebf2 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -457,6 +457,17 @@ fn test_match_group_with_multichar_sep() {
457} 457}
458 458
459#[test] 459#[test]
460fn test_match_group_with_multichar_sep2() {
461 parse_macro(
462 r#"
463 macro_rules! foo {
464 (fn $name:ident {$($i:literal)&&*} ) => ( fn $name() -> bool { $($i)&&*} );
465 }"#,
466 )
467 .assert_expand_items("foo! (fn baz {true && true} );", "fn baz () -> bool {true &&true}");
468}
469
470#[test]
460fn test_match_group_zero_match() { 471fn test_match_group_zero_match() {
461 parse_macro( 472 parse_macro(
462 r#" 473 r#"
@@ -1267,6 +1278,18 @@ macro_rules! m {
1267 .is_some()); 1278 .is_some());
1268} 1279}
1269 1280
1281#[test]
1282fn test_match_is_not_greedy() {
1283 parse_macro(
1284 r#"
1285macro_rules! foo {
1286 ($($i:ident $(,)*),*) => {};
1287}
1288"#,
1289 )
1290 .assert_expand_items(r#"foo!(a,b);"#, r#""#);
1291}
1292
1270// The following tests are based on real world situations 1293// The following tests are based on real world situations
1271#[test] 1294#[test]
1272fn test_vec() { 1295fn test_vec() {
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 70cb7fbab..c1ca88df6 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -474,7 +474,7 @@ pub(crate) fn folding_range(
474 let kind = match fold.kind { 474 let kind = match fold.kind {
475 FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), 475 FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment),
476 FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), 476 FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports),
477 FoldKind::Mods | FoldKind::Block | FoldKind::ArgList => None, 477 FoldKind::Mods | FoldKind::Block | FoldKind::ArgList | FoldKind::Region => None,
478 }; 478 };
479 479
480 let range = range(line_index, fold.range); 480 let range = range(line_index, fold.range);