aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--crates/base_db/src/lib.rs3
-rw-r--r--crates/hir/src/lib.rs13
-rw-r--r--crates/hir/src/semantics.rs4
-rw-r--r--crates/hir/src/semantics/source_to_def.rs5
-rw-r--r--crates/hir_def/src/body/lower.rs68
-rw-r--r--crates/hir_def/src/data.rs10
-rw-r--r--crates/hir_def/src/expr.rs4
-rw-r--r--crates/hir_def/src/item_tree.rs52
-rw-r--r--crates/hir_def/src/item_tree/lower.rs25
-rw-r--r--crates/hir_def/src/nameres/collector.rs102
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs18
-rw-r--r--crates/hir_def/src/path/lower.rs4
-rw-r--r--crates/hir_def/src/type_ref.rs17
-rw-r--r--crates/hir_expand/src/db.rs119
-rw-r--r--crates/hir_expand/src/hygiene.rs2
-rw-r--r--crates/hir_expand/src/lib.rs2
-rw-r--r--crates/hir_expand/src/name.rs13
-rw-r--r--crates/hir_ty/src/diagnostics/match_check.rs40
-rw-r--r--crates/hir_ty/src/infer/expr.rs3
-rw-r--r--crates/hir_ty/src/lower.rs12
-rw-r--r--crates/hir_ty/src/tests/macros.rs123
-rw-r--r--crates/hir_ty/src/tests/traits.rs43
-rw-r--r--crates/ide/src/annotations.rs2
-rw-r--r--crates/ide/src/diagnostics.rs1
-rw-r--r--crates/ide/src/diagnostics/fixes.rs2
-rw-r--r--crates/ide/src/diagnostics/unlinked_file.rs2
-rw-r--r--crates/ide/src/doc_links.rs67
-rw-r--r--crates/ide/src/expand_macro.rs2
-rw-r--r--crates/ide/src/extend_selection.rs2
-rw-r--r--crates/ide/src/file_structure.rs5
-rw-r--r--crates/ide/src/folding_ranges.rs39
-rw-r--r--crates/ide/src/goto_definition.rs17
-rw-r--r--crates/ide/src/goto_implementation.rs2
-rw-r--r--crates/ide/src/goto_type_definition.rs2
-rw-r--r--crates/ide/src/hover.rs40
-rw-r--r--crates/ide/src/inlay_hints.rs4
-rw-r--r--crates/ide/src/join_lines.rs2
-rw-r--r--crates/ide/src/matching_brace.rs2
-rw-r--r--crates/ide/src/move_item.rs271
-rw-r--r--crates/ide/src/parent_module.rs2
-rw-r--r--crates/ide/src/references.rs2
-rw-r--r--crates/ide/src/references/rename.rs4
-rw-r--r--crates/ide/src/runnables.rs3
-rw-r--r--crates/ide/src/status.rs1
-rw-r--r--crates/ide/src/syntax_highlighting.rs28
-rw-r--r--crates/ide/src/syntax_highlighting/format.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs35
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs35
-rw-r--r--crates/ide/src/syntax_highlighting/macro_.rs (renamed from crates/ide/src/syntax_highlighting/macro_rules.rs)10
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs4
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html8
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_injection.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html6
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html16
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs10
-rw-r--r--crates/ide/src/syntax_tree.rs1
-rw-r--r--crates/ide/src/typing/on_enter.rs2
-rw-r--r--crates/ide/src/view_hir.rs1
-rw-r--r--crates/ide_assists/src/handlers/auto_import.rs2
-rw-r--r--crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_default_from_new.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_enum_is_method.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_enum_projection_method.rs3
-rw-r--r--crates/ide_assists/src/handlers/generate_impl.rs17
-rw-r--r--crates/ide_assists/src/handlers/generate_is_empty_from_len.rs2
-rw-r--r--crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs5
-rw-r--r--crates/ide_assists/src/utils.rs17
-rw-r--r--crates/ide_completion/src/completions.rs2
-rw-r--r--crates/ide_completion/src/completions/pattern.rs2
-rw-r--r--crates/ide_completion/src/completions/postfix/format_like.rs2
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs2
-rw-r--r--crates/ide_completion/src/lib.rs2
-rw-r--r--crates/ide_db/src/apply_change.rs1
-rw-r--r--crates/ide_db/src/call_info/tests.rs3
-rw-r--r--crates/ide_db/src/defs.rs43
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs4
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs5
-rw-r--r--crates/ide_db/src/label.rs2
-rw-r--r--crates/ide_db/src/search.rs3
-rw-r--r--crates/ide_db/src/source_change.rs2
-rw-r--r--crates/ide_db/src/symbol_index.rs3
-rw-r--r--crates/ide_db/src/traits.rs3
-rw-r--r--crates/ide_db/src/traits/tests.rs3
-rw-r--r--crates/ide_db/src/ty_filter.rs6
-rw-r--r--crates/ide_ssr/src/resolving.rs2
-rw-r--r--crates/proc_macro_srv/src/rustc_server.rs19
-rw-r--r--crates/profile/src/google_cpu_profiler.rs19
-rw-r--r--crates/profile/src/hprof.rs107
-rw-r--r--crates/profile/src/lib.rs39
-rw-r--r--crates/rust-analyzer/src/benchmarks.rs71
-rw-r--r--crates/rust-analyzer/src/bin/flags.rs53
-rw-r--r--crates/rust-analyzer/src/bin/main.rs13
-rw-r--r--crates/rust-analyzer/src/cli.rs4
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs196
-rw-r--r--crates/rust-analyzer/src/lib.rs3
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs1
-rw-r--r--crates/rust-analyzer/src/to_proto.rs7
-rw-r--r--crates/syntax/src/ast/make.rs4
-rw-r--r--crates/syntax/src/ast/node_ext.rs20
-rw-r--r--crates/syntax/src/lib.rs2
-rw-r--r--crates/syntax/src/parsing/text_tree_sink.rs4
-rw-r--r--crates/syntax/src/token_text.rs77
-rw-r--r--docs/dev/README.md72
-rw-r--r--docs/user/manual.adoc28
105 files changed, 1470 insertions, 729 deletions
diff --git a/README.md b/README.md
index 74f944ae4..085c8c0f6 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
4 alt="rust-analyzer logo"> 4 alt="rust-analyzer logo">
5</p> 5</p>
6 6
7rust-analyzer is an **experimental** modular compiler frontend for the Rust language. 7rust-analyzer is a modular compiler frontend for the Rust language.
8It is a part of a larger rls-2.0 effort to create excellent IDE support for Rust. 8It is a part of a larger rls-2.0 effort to create excellent IDE support for Rust.
9 9
10Work on rust-analyzer is sponsored by 10Work on rust-analyzer is sponsored by
diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs
index 5f77a0b1f..980a0ed98 100644
--- a/crates/base_db/src/lib.rs
+++ b/crates/base_db/src/lib.rs
@@ -59,6 +59,8 @@ pub trait CheckCanceled {
59 Self: Sized + panic::RefUnwindSafe, 59 Self: Sized + panic::RefUnwindSafe,
60 F: FnOnce(&Self) -> T + panic::UnwindSafe, 60 F: FnOnce(&Self) -> T + panic::UnwindSafe,
61 { 61 {
62 // Uncomment to debug missing cancellations.
63 // let _span = profile::heartbeat_span();
62 panic::catch_unwind(|| f(self)).map_err(|err| match err.downcast::<Canceled>() { 64 panic::catch_unwind(|| f(self)).map_err(|err| match err.downcast::<Canceled>() {
63 Ok(canceled) => *canceled, 65 Ok(canceled) => *canceled,
64 Err(payload) => panic::resume_unwind(payload), 66 Err(payload) => panic::resume_unwind(payload),
@@ -68,6 +70,7 @@ pub trait CheckCanceled {
68 70
69impl<T: salsa::Database> CheckCanceled for T { 71impl<T: salsa::Database> CheckCanceled for T {
70 fn check_canceled(&self) { 72 fn check_canceled(&self) {
73 // profile::heartbeat();
71 if self.salsa_runtime().is_current_revision_canceled() { 74 if self.salsa_runtime().is_current_revision_canceled() {
72 Canceled::throw() 75 Canceled::throw()
73 } 76 }
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 4ee08ef21..97f162315 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -44,6 +44,7 @@ use hir_def::{
44 per_ns::PerNs, 44 per_ns::PerNs,
45 resolver::{HasResolver, Resolver}, 45 resolver::{HasResolver, Resolver},
46 src::HasSource as _, 46 src::HasSource as _,
47 type_ref::TraitRef,
47 AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, 48 AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId,
48 DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, 49 DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId,
49 LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, 50 LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId,
@@ -1573,9 +1574,9 @@ impl Impl {
1573 }; 1574 };
1574 1575
1575 let filter = |impl_def: &Impl| { 1576 let filter = |impl_def: &Impl| {
1576 let target_ty = impl_def.target_ty(db); 1577 let self_ty = impl_def.self_ty(db);
1577 let rref = target_ty.remove_ref(); 1578 let rref = self_ty.remove_ref();
1578 ty.equals_ctor(rref.as_ref().map_or(&target_ty.ty, |it| &it.ty)) 1579 ty.equals_ctor(rref.as_ref().map_or(&self_ty.ty, |it| &it.ty))
1579 }; 1580 };
1580 1581
1581 let mut all = Vec::new(); 1582 let mut all = Vec::new();
@@ -1613,16 +1614,16 @@ impl Impl {
1613 1614
1614 // FIXME: the return type is wrong. This should be a hir version of 1615 // FIXME: the return type is wrong. This should be a hir version of
1615 // `TraitRef` (ie, resolved `TypeRef`). 1616 // `TraitRef` (ie, resolved `TypeRef`).
1616 pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> { 1617 pub fn trait_(self, db: &dyn HirDatabase) -> Option<TraitRef> {
1617 db.impl_data(self.id).target_trait.clone() 1618 db.impl_data(self.id).target_trait.clone()
1618 } 1619 }
1619 1620
1620 pub fn target_ty(self, db: &dyn HirDatabase) -> Type { 1621 pub fn self_ty(self, db: &dyn HirDatabase) -> Type {
1621 let impl_data = db.impl_data(self.id); 1622 let impl_data = db.impl_data(self.id);
1622 let resolver = self.id.resolver(db.upcast()); 1623 let resolver = self.id.resolver(db.upcast());
1623 let krate = self.id.lookup(db.upcast()).container.krate(); 1624 let krate = self.id.lookup(db.upcast()).container.krate();
1624 let ctx = hir_ty::TyLoweringContext::new(db, &resolver); 1625 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
1625 let ty = ctx.lower_ty(&impl_data.target_type); 1626 let ty = ctx.lower_ty(&impl_data.self_ty);
1626 Type::new_with_resolver_inner(db, krate, &resolver, ty) 1627 Type::new_with_resolver_inner(db, krate, &resolver, ty)
1627 } 1628 }
1628 1629
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 1198e3f0b..d3caeef4e 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -445,7 +445,7 @@ impl<'db> SemanticsImpl<'db> {
445 } 445 }
446 }; 446 };
447 gpl.lifetime_params() 447 gpl.lifetime_params()
448 .find(|tp| tp.lifetime().as_ref().map(|lt| lt.text()) == Some(text)) 448 .find(|tp| tp.lifetime().as_ref().map(|lt| lt.text()).as_ref() == Some(&text))
449 })?; 449 })?;
450 let src = self.find_file(lifetime_param.syntax().clone()).with_value(lifetime_param); 450 let src = self.find_file(lifetime_param.syntax().clone()).with_value(lifetime_param);
451 ToDef::to_def(self, src) 451 ToDef::to_def(self, src)
@@ -768,7 +768,7 @@ to_def_impls![
768 (crate::TypeParam, ast::TypeParam, type_param_to_def), 768 (crate::TypeParam, ast::TypeParam, type_param_to_def),
769 (crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def), 769 (crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def),
770 (crate::ConstParam, ast::ConstParam, const_param_to_def), 770 (crate::ConstParam, ast::ConstParam, const_param_to_def),
771 (crate::MacroDef, ast::MacroRules, macro_rules_to_def), 771 (crate::MacroDef, ast::Macro, macro_to_def),
772 (crate::Local, ast::IdentPat, bind_pat_to_def), 772 (crate::Local, ast::IdentPat, bind_pat_to_def),
773 (crate::Local, ast::SelfParam, self_param_to_def), 773 (crate::Local, ast::SelfParam, self_param_to_def),
774 (crate::Label, ast::Label, label_to_def), 774 (crate::Label, ast::Label, label_to_def),
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 762809fcd..9a5a2255f 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -191,10 +191,7 @@ impl SourceToDefCtx<'_, '_> {
191 } 191 }
192 192
193 // FIXME: use DynMap as well? 193 // FIXME: use DynMap as well?
194 pub(super) fn macro_rules_to_def( 194 pub(super) fn macro_to_def(&mut self, src: InFile<ast::Macro>) -> Option<MacroDefId> {
195 &mut self,
196 src: InFile<ast::MacroRules>,
197 ) -> Option<MacroDefId> {
198 let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value); 195 let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value);
199 let ast_id = AstId::new(src.file_id, file_ast_id.upcast()); 196 let ast_id = AstId::new(src.file_id, file_ast_id.upcast());
200 let kind = MacroDefKind::Declarative(ast_id); 197 let kind = MacroDefKind::Declarative(ast_id);
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 19f5065d1..229e81dd4 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -74,6 +74,7 @@ pub(super) fn lower(
74 _c: Count::new(), 74 _c: Count::new(),
75 }, 75 },
76 expander, 76 expander,
77 statements_in_scope: Vec::new(),
77 } 78 }
78 .collect(params, body) 79 .collect(params, body)
79} 80}
@@ -83,6 +84,7 @@ struct ExprCollector<'a> {
83 expander: Expander, 84 expander: Expander,
84 body: Body, 85 body: Body,
85 source_map: BodySourceMap, 86 source_map: BodySourceMap,
87 statements_in_scope: Vec<Statement>,
86} 88}
87 89
88impl ExprCollector<'_> { 90impl ExprCollector<'_> {
@@ -533,15 +535,13 @@ impl ExprCollector<'_> {
533 ids[0] 535 ids[0]
534 } 536 }
535 ast::Expr::MacroStmts(e) => { 537 ast::Expr::MacroStmts(e) => {
536 // FIXME: these statements should be held by some hir containter 538 e.statements().for_each(|s| self.collect_stmt(s));
537 for stmt in e.statements() { 539 let tail = e
538 self.collect_stmt(stmt); 540 .expr()
539 } 541 .map(|e| self.collect_expr(e))
540 if let Some(expr) = e.expr() { 542 .unwrap_or_else(|| self.alloc_expr(Expr::Missing, syntax_ptr.clone()));
541 self.collect_expr(expr) 543
542 } else { 544 self.alloc_expr(Expr::MacroStmts { tail }, syntax_ptr)
543 self.alloc_expr(Expr::Missing, syntax_ptr)
544 }
545 } 545 }
546 }) 546 })
547 } 547 }
@@ -618,58 +618,54 @@ impl ExprCollector<'_> {
618 } 618 }
619 } 619 }
620 620
621 fn collect_stmt(&mut self, s: ast::Stmt) -> Option<Vec<Statement>> { 621 fn collect_stmt(&mut self, s: ast::Stmt) {
622 let stmt = match s { 622 match s {
623 ast::Stmt::LetStmt(stmt) => { 623 ast::Stmt::LetStmt(stmt) => {
624 self.check_cfg(&stmt)?; 624 if self.check_cfg(&stmt).is_none() {
625 625 return;
626 }
626 let pat = self.collect_pat_opt(stmt.pat()); 627 let pat = self.collect_pat_opt(stmt.pat());
627 let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it)); 628 let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
628 let initializer = stmt.initializer().map(|e| self.collect_expr(e)); 629 let initializer = stmt.initializer().map(|e| self.collect_expr(e));
629 vec![Statement::Let { pat, type_ref, initializer }] 630 self.statements_in_scope.push(Statement::Let { pat, type_ref, initializer });
630 } 631 }
631 ast::Stmt::ExprStmt(stmt) => { 632 ast::Stmt::ExprStmt(stmt) => {
632 self.check_cfg(&stmt)?; 633 if self.check_cfg(&stmt).is_none() {
634 return;
635 }
633 636
634 // Note that macro could be expended to multiple statements 637 // Note that macro could be expended to multiple statements
635 if let Some(ast::Expr::MacroCall(m)) = stmt.expr() { 638 if let Some(ast::Expr::MacroCall(m)) = stmt.expr() {
636 let syntax_ptr = AstPtr::new(&stmt.expr().unwrap()); 639 let syntax_ptr = AstPtr::new(&stmt.expr().unwrap());
637 let mut stmts = vec![];
638 640
639 self.collect_macro_call(m, syntax_ptr.clone(), false, |this, expansion| { 641 self.collect_macro_call(m, syntax_ptr.clone(), false, |this, expansion| {
640 match expansion { 642 match expansion {
641 Some(expansion) => { 643 Some(expansion) => {
642 let statements: ast::MacroStmts = expansion; 644 let statements: ast::MacroStmts = expansion;
643 645
644 statements.statements().for_each(|stmt| { 646 statements.statements().for_each(|stmt| this.collect_stmt(stmt));
645 if let Some(mut r) = this.collect_stmt(stmt) {
646 stmts.append(&mut r);
647 }
648 });
649 if let Some(expr) = statements.expr() { 647 if let Some(expr) = statements.expr() {
650 stmts.push(Statement::Expr(this.collect_expr(expr))); 648 let expr = this.collect_expr(expr);
649 this.statements_in_scope.push(Statement::Expr(expr));
651 } 650 }
652 } 651 }
653 None => { 652 None => {
654 stmts.push(Statement::Expr( 653 let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone());
655 this.alloc_expr(Expr::Missing, syntax_ptr.clone()), 654 this.statements_in_scope.push(Statement::Expr(expr));
656 ));
657 } 655 }
658 } 656 }
659 }); 657 });
660 stmts
661 } else { 658 } else {
662 vec![Statement::Expr(self.collect_expr_opt(stmt.expr()))] 659 let expr = self.collect_expr_opt(stmt.expr());
660 self.statements_in_scope.push(Statement::Expr(expr));
663 } 661 }
664 } 662 }
665 ast::Stmt::Item(item) => { 663 ast::Stmt::Item(item) => {
666 self.check_cfg(&item)?; 664 if self.check_cfg(&item).is_none() {
667 665 return;
668 return None; 666 }
669 } 667 }
670 }; 668 }
671
672 Some(stmt)
673 } 669 }
674 670
675 fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId { 671 fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId {
@@ -685,10 +681,12 @@ impl ExprCollector<'_> {
685 let module = if has_def_map { def_map.root() } else { self.expander.module }; 681 let module = if has_def_map { def_map.root() } else { self.expander.module };
686 let prev_def_map = mem::replace(&mut self.expander.def_map, def_map); 682 let prev_def_map = mem::replace(&mut self.expander.def_map, def_map);
687 let prev_local_module = mem::replace(&mut self.expander.module, module); 683 let prev_local_module = mem::replace(&mut self.expander.module, module);
684 let prev_statements = std::mem::take(&mut self.statements_in_scope);
685
686 block.statements().for_each(|s| self.collect_stmt(s));
688 687
689 let statements =
690 block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect();
691 let tail = block.tail_expr().map(|e| self.collect_expr(e)); 688 let tail = block.tail_expr().map(|e| self.collect_expr(e));
689 let statements = std::mem::replace(&mut self.statements_in_scope, prev_statements);
692 let syntax_node_ptr = AstPtr::new(&block.into()); 690 let syntax_node_ptr = AstPtr::new(&block.into());
693 let expr_id = self.alloc_expr( 691 let expr_id = self.alloc_expr(
694 Expr::Block { id: block_id, statements, tail, label: None }, 692 Expr::Block { id: block_id, statements, tail, label: None },
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs
index 0be868ba2..214bcc648 100644
--- a/crates/hir_def/src/data.rs
+++ b/crates/hir_def/src/data.rs
@@ -10,7 +10,7 @@ use crate::{
10 body::Expander, 10 body::Expander,
11 db::DefDatabase, 11 db::DefDatabase,
12 item_tree::{AssocItem, FunctionQualifier, ItemTreeId, ModItem, Param}, 12 item_tree::{AssocItem, FunctionQualifier, ItemTreeId, ModItem, Param},
13 type_ref::{TypeBound, TypeRef}, 13 type_ref::{TraitRef, TypeBound, TypeRef},
14 visibility::RawVisibility, 14 visibility::RawVisibility,
15 AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, 15 AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
16 Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc, 16 Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
@@ -156,8 +156,8 @@ impl TraitData {
156 156
157#[derive(Debug, Clone, PartialEq, Eq)] 157#[derive(Debug, Clone, PartialEq, Eq)]
158pub struct ImplData { 158pub struct ImplData {
159 pub target_trait: Option<TypeRef>, 159 pub target_trait: Option<TraitRef>,
160 pub target_type: TypeRef, 160 pub self_ty: TypeRef,
161 pub items: Vec<AssocItemId>, 161 pub items: Vec<AssocItemId>,
162 pub is_negative: bool, 162 pub is_negative: bool,
163} 163}
@@ -170,7 +170,7 @@ impl ImplData {
170 let item_tree = impl_loc.id.item_tree(db); 170 let item_tree = impl_loc.id.item_tree(db);
171 let impl_def = &item_tree[impl_loc.id.value]; 171 let impl_def = &item_tree[impl_loc.id.value];
172 let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone()); 172 let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone());
173 let target_type = item_tree[impl_def.target_type].clone(); 173 let self_ty = item_tree[impl_def.self_ty].clone();
174 let is_negative = impl_def.is_negative; 174 let is_negative = impl_def.is_negative;
175 let module_id = impl_loc.container; 175 let module_id = impl_loc.container;
176 let container = AssocContainerId::ImplId(id); 176 let container = AssocContainerId::ImplId(id);
@@ -187,7 +187,7 @@ impl ImplData {
187 ); 187 );
188 let items = items.into_iter().map(|(_, item)| item).collect(); 188 let items = items.into_iter().map(|(_, item)| item).collect();
189 189
190 Arc::new(ImplData { target_trait, target_type, items, is_negative }) 190 Arc::new(ImplData { target_trait, self_ty, items, is_negative })
191 } 191 }
192} 192}
193 193
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs
index 24be93773..6c7376fad 100644
--- a/crates/hir_def/src/expr.rs
+++ b/crates/hir_def/src/expr.rs
@@ -171,6 +171,9 @@ pub enum Expr {
171 Unsafe { 171 Unsafe {
172 body: ExprId, 172 body: ExprId,
173 }, 173 },
174 MacroStmts {
175 tail: ExprId,
176 },
174 Array(Array), 177 Array(Array),
175 Literal(Literal), 178 Literal(Literal),
176} 179}
@@ -357,6 +360,7 @@ impl Expr {
357 f(*repeat) 360 f(*repeat)
358 } 361 }
359 }, 362 },
363 Expr::MacroStmts { tail } => f(*tail),
360 Expr::Literal(_) => {} 364 Expr::Literal(_) => {}
361 } 365 }
362 } 366 }
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index ae2475b4e..5449bbf5d 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -31,7 +31,7 @@ use crate::{
31 db::DefDatabase, 31 db::DefDatabase,
32 generics::GenericParams, 32 generics::GenericParams,
33 path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind}, 33 path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind},
34 type_ref::{Mutability, TypeBound, TypeRef}, 34 type_ref::{Mutability, TraitRef, TypeBound, TypeRef},
35 visibility::RawVisibility, 35 visibility::RawVisibility,
36}; 36};
37 37
@@ -110,15 +110,6 @@ impl ItemTree {
110 // still need to collect inner items. 110 // still need to collect inner items.
111 ctx.lower_inner_items(e.syntax()) 111 ctx.lower_inner_items(e.syntax())
112 }, 112 },
113 ast::ExprStmt(stmt) => {
114 // Macros can expand to stmt. We return an empty item tree in this case, but
115 // still need to collect inner items.
116 ctx.lower_inner_items(stmt.syntax())
117 },
118 ast::Item(item) => {
119 // Macros can expand to stmt and other item, and we add it as top level item
120 ctx.lower_single_item(item)
121 },
122 _ => { 113 _ => {
123 panic!("cannot create item tree from {:?} {}", syntax, syntax); 114 panic!("cannot create item tree from {:?} {}", syntax, syntax);
124 }, 115 },
@@ -156,6 +147,7 @@ impl ItemTree {
156 vis, 147 vis,
157 generics, 148 generics,
158 type_refs, 149 type_refs,
150 trait_refs,
159 inner_items, 151 inner_items,
160 } = &mut **data; 152 } = &mut **data;
161 153
@@ -182,6 +174,7 @@ impl ItemTree {
182 generics.arena.shrink_to_fit(); 174 generics.arena.shrink_to_fit();
183 type_refs.arena.shrink_to_fit(); 175 type_refs.arena.shrink_to_fit();
184 type_refs.map.shrink_to_fit(); 176 type_refs.map.shrink_to_fit();
177 trait_refs.map.shrink_to_fit();
185 178
186 inner_items.shrink_to_fit(); 179 inner_items.shrink_to_fit();
187 } 180 }
@@ -304,6 +297,32 @@ impl TypeRefStorage {
304 } 297 }
305} 298}
306 299
300/// `TraitRef` interner.
301#[derive(Default, Debug, Eq, PartialEq)]
302struct TraitRefStorage {
303 arena: Arena<Arc<TraitRef>>,
304 map: FxHashMap<Arc<TraitRef>, Idx<Arc<TraitRef>>>,
305}
306
307impl TraitRefStorage {
308 // Note: We lie about the `Idx<TraitRef>` to hide the interner details.
309
310 fn intern(&mut self, ty: TraitRef) -> Idx<TraitRef> {
311 if let Some(id) = self.map.get(&ty) {
312 return Idx::from_raw(id.into_raw());
313 }
314
315 let ty = Arc::new(ty);
316 let idx = self.arena.alloc(ty.clone());
317 self.map.insert(ty, idx);
318 Idx::from_raw(idx.into_raw())
319 }
320
321 fn lookup(&self, id: Idx<TraitRef>) -> &TraitRef {
322 &self.arena[Idx::from_raw(id.into_raw())]
323 }
324}
325
307#[derive(Default, Debug, Eq, PartialEq)] 326#[derive(Default, Debug, Eq, PartialEq)]
308struct ItemTreeData { 327struct ItemTreeData {
309 imports: Arena<Import>, 328 imports: Arena<Import>,
@@ -328,6 +347,7 @@ struct ItemTreeData {
328 vis: ItemVisibilities, 347 vis: ItemVisibilities,
329 generics: GenericParamsStorage, 348 generics: GenericParamsStorage,
330 type_refs: TypeRefStorage, 349 type_refs: TypeRefStorage,
350 trait_refs: TraitRefStorage,
331 351
332 inner_items: FxHashMap<FileAstId<ast::BlockExpr>, SmallVec<[ModItem; 1]>>, 352 inner_items: FxHashMap<FileAstId<ast::BlockExpr>, SmallVec<[ModItem; 1]>>,
333} 353}
@@ -565,6 +585,14 @@ impl Index<Idx<TypeRef>> for ItemTree {
565 } 585 }
566} 586}
567 587
588impl Index<Idx<TraitRef>> for ItemTree {
589 type Output = TraitRef;
590
591 fn index(&self, id: Idx<TraitRef>) -> &Self::Output {
592 self.data().trait_refs.lookup(id)
593 }
594}
595
568impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree { 596impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
569 type Output = N; 597 type Output = N;
570 fn index(&self, id: FileItemTreeId<N>) -> &N { 598 fn index(&self, id: FileItemTreeId<N>) -> &N {
@@ -701,8 +729,8 @@ pub struct Trait {
701#[derive(Debug, Clone, Eq, PartialEq)] 729#[derive(Debug, Clone, Eq, PartialEq)]
702pub struct Impl { 730pub struct Impl {
703 pub generic_params: GenericParamsId, 731 pub generic_params: GenericParamsId,
704 pub target_trait: Option<Idx<TypeRef>>, 732 pub target_trait: Option<Idx<TraitRef>>,
705 pub target_type: Idx<TypeRef>, 733 pub self_ty: Idx<TypeRef>,
706 pub is_negative: bool, 734 pub is_negative: bool,
707 pub items: Box<[AssocItem]>, 735 pub items: Box<[AssocItem]>,
708 pub ast_id: FileAstId<ast::Impl>, 736 pub ast_id: FileAstId<ast::Impl>,
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index d3fe1ce1e..8d3862811 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -11,7 +11,7 @@ use syntax::{
11 11
12use crate::{ 12use crate::{
13 generics::{GenericParams, TypeParamData, TypeParamProvenance}, 13 generics::{GenericParams, TypeParamData, TypeParamProvenance},
14 type_ref::LifetimeRef, 14 type_ref::{LifetimeRef, TraitRef},
15}; 15};
16 16
17use super::*; 17use super::*;
@@ -87,14 +87,6 @@ impl Ctx {
87 self.tree 87 self.tree
88 } 88 }
89 89
90 pub(super) fn lower_single_item(mut self, item: ast::Item) -> ItemTree {
91 self.tree.top_level = self
92 .lower_mod_item(&item, false)
93 .map(|item| item.0)
94 .unwrap_or_else(|| Default::default());
95 self.tree
96 }
97
98 pub(super) fn lower_inner_items(mut self, within: &SyntaxNode) -> ItemTree { 90 pub(super) fn lower_inner_items(mut self, within: &SyntaxNode) -> ItemTree {
99 self.collect_inner_items(within); 91 self.collect_inner_items(within);
100 self.tree 92 self.tree
@@ -544,8 +536,11 @@ impl Ctx {
544 fn lower_impl(&mut self, impl_def: &ast::Impl) -> Option<FileItemTreeId<Impl>> { 536 fn lower_impl(&mut self, impl_def: &ast::Impl) -> Option<FileItemTreeId<Impl>> {
545 let generic_params = 537 let generic_params =
546 self.lower_generic_params_and_inner_items(GenericsOwner::Impl, impl_def); 538 self.lower_generic_params_and_inner_items(GenericsOwner::Impl, impl_def);
547 let target_trait = impl_def.trait_().map(|tr| self.lower_type_ref(&tr)); 539 // FIXME: If trait lowering fails, due to a non PathType for example, we treat this impl
548 let target_type = self.lower_type_ref(&impl_def.self_ty()?); 540 // as if it was an non-trait impl. Ideally we want to create a unique missing ref that only
541 // equals itself.
542 let target_trait = impl_def.trait_().and_then(|tr| self.lower_trait_ref(&tr));
543 let self_ty = self.lower_type_ref(&impl_def.self_ty()?);
549 let is_negative = impl_def.excl_token().is_some(); 544 let is_negative = impl_def.excl_token().is_some();
550 545
551 // We cannot use `assoc_items()` here as that does not include macro calls. 546 // We cannot use `assoc_items()` here as that does not include macro calls.
@@ -562,7 +557,7 @@ impl Ctx {
562 }) 557 })
563 .collect(); 558 .collect();
564 let ast_id = self.source_ast_id_map.ast_id(impl_def); 559 let ast_id = self.source_ast_id_map.ast_id(impl_def);
565 let res = Impl { generic_params, target_trait, target_type, is_negative, items, ast_id }; 560 let res = Impl { generic_params, target_trait, self_ty, is_negative, items, ast_id };
566 Some(id(self.data().impls.alloc(res))) 561 Some(id(self.data().impls.alloc(res)))
567 } 562 }
568 563
@@ -748,10 +743,16 @@ impl Ctx {
748 self.data().vis.alloc(vis) 743 self.data().vis.alloc(vis)
749 } 744 }
750 745
746 fn lower_trait_ref(&mut self, trait_ref: &ast::Type) -> Option<Idx<TraitRef>> {
747 let trait_ref = TraitRef::from_ast(&self.body_ctx, trait_ref.clone())?;
748 Some(self.data().trait_refs.intern(trait_ref))
749 }
750
751 fn lower_type_ref(&mut self, type_ref: &ast::Type) -> Idx<TypeRef> { 751 fn lower_type_ref(&mut self, type_ref: &ast::Type) -> Idx<TypeRef> {
752 let tyref = TypeRef::from_ast(&self.body_ctx, type_ref.clone()); 752 let tyref = TypeRef::from_ast(&self.body_ctx, type_ref.clone());
753 self.data().type_refs.intern(tyref) 753 self.data().type_refs.intern(tyref)
754 } 754 }
755
755 fn lower_type_ref_opt(&mut self, type_ref: Option<ast::Type>) -> Idx<TypeRef> { 756 fn lower_type_ref_opt(&mut self, type_ref: Option<ast::Type>) -> Idx<TypeRef> {
756 match type_ref.map(|ty| self.lower_type_ref(&ty)) { 757 match type_ref.map(|ty| self.lower_type_ref(&ty)) {
757 Some(it) => it, 758 Some(it) => it,
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index d8fabe49b..d58135ec9 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -25,8 +25,8 @@ use crate::{
25 derive_macro_as_call_id, 25 derive_macro_as_call_id,
26 item_scope::{ImportType, PerNsGlobImports}, 26 item_scope::{ImportType, PerNsGlobImports},
27 item_tree::{ 27 item_tree::{
28 self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroRules, Mod, ModItem, ModKind, 28 self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroDef, MacroRules, Mod, ModItem,
29 StructDefKind, 29 ModKind, StructDefKind,
30 }, 30 },
31 macro_call_as_call_id, 31 macro_call_as_call_id,
32 nameres::{ 32 nameres::{
@@ -395,7 +395,7 @@ impl DefCollector<'_> {
395 /// macro_rules! foo { () => {} } 395 /// macro_rules! foo { () => {} }
396 /// use foo as bar; 396 /// use foo as bar;
397 /// ``` 397 /// ```
398 fn define_macro( 398 fn define_macro_rules(
399 &mut self, 399 &mut self,
400 module_id: LocalModuleId, 400 module_id: LocalModuleId,
401 name: Name, 401 name: Name,
@@ -430,6 +430,21 @@ impl DefCollector<'_> {
430 self.def_map.modules[module_id].scope.define_legacy_macro(name, mac); 430 self.def_map.modules[module_id].scope.define_legacy_macro(name, mac);
431 } 431 }
432 432
433 /// Define a macro 2.0 macro
434 ///
435 /// The scoped of macro 2.0 macro is equal to normal function
436 fn define_macro_def(
437 &mut self,
438 module_id: LocalModuleId,
439 name: Name,
440 macro_: MacroDefId,
441 vis: &RawVisibility,
442 ) {
443 let vis =
444 self.def_map.resolve_visibility(self.db, module_id, vis).unwrap_or(Visibility::Public);
445 self.update(module_id, &[(Some(name), PerNs::macros(macro_, vis))], vis, ImportType::Named);
446 }
447
433 /// Define a proc macro 448 /// Define a proc macro
434 /// 449 ///
435 /// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped. 450 /// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped.
@@ -1067,40 +1082,7 @@ impl ModCollector<'_, '_> {
1067 } 1082 }
1068 ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]), 1083 ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]),
1069 ModItem::MacroRules(id) => self.collect_macro_rules(id), 1084 ModItem::MacroRules(id) => self.collect_macro_rules(id),
1070 ModItem::MacroDef(id) => { 1085 ModItem::MacroDef(id) => self.collect_macro_def(id),
1071 let mac = &self.item_tree[id];
1072 let ast_id = InFile::new(self.file_id, mac.ast_id.upcast());
1073
1074 // "Macro 2.0" is not currently supported by rust-analyzer, but libcore uses it
1075 // to define builtin macros, so we support at least that part.
1076 let attrs = self.item_tree.attrs(
1077 self.def_collector.db,
1078 krate,
1079 ModItem::from(id).into(),
1080 );
1081 if attrs.by_key("rustc_builtin_macro").exists() {
1082 let krate = self.def_collector.def_map.krate;
1083 let macro_id = find_builtin_macro(&mac.name, krate, ast_id)
1084 .or_else(|| find_builtin_derive(&mac.name, krate, ast_id));
1085 if let Some(macro_id) = macro_id {
1086 let vis = self
1087 .def_collector
1088 .def_map
1089 .resolve_visibility(
1090 self.def_collector.db,
1091 self.module_id,
1092 &self.item_tree[mac.visibility],
1093 )
1094 .unwrap_or(Visibility::Public);
1095 self.def_collector.update(
1096 self.module_id,
1097 &[(Some(mac.name.clone()), PerNs::macros(macro_id, vis))],
1098 vis,
1099 ImportType::Named,
1100 );
1101 }
1102 }
1103 }
1104 ModItem::Impl(imp) => { 1086 ModItem::Impl(imp) => {
1105 let module = self.def_collector.def_map.module_id(self.module_id); 1087 let module = self.def_collector.def_map.module_id(self.module_id);
1106 let impl_id = 1088 let impl_id =
@@ -1420,7 +1402,7 @@ impl ModCollector<'_, '_> {
1420 if attrs.by_key("rustc_builtin_macro").exists() { 1402 if attrs.by_key("rustc_builtin_macro").exists() {
1421 let krate = self.def_collector.def_map.krate; 1403 let krate = self.def_collector.def_map.krate;
1422 if let Some(macro_id) = find_builtin_macro(&mac.name, krate, ast_id) { 1404 if let Some(macro_id) = find_builtin_macro(&mac.name, krate, ast_id) {
1423 self.def_collector.define_macro( 1405 self.def_collector.define_macro_rules(
1424 self.module_id, 1406 self.module_id,
1425 mac.name.clone(), 1407 mac.name.clone(),
1426 macro_id, 1408 macro_id,
@@ -1436,7 +1418,49 @@ impl ModCollector<'_, '_> {
1436 kind: MacroDefKind::Declarative(ast_id), 1418 kind: MacroDefKind::Declarative(ast_id),
1437 local_inner: is_local_inner, 1419 local_inner: is_local_inner,
1438 }; 1420 };
1439 self.def_collector.define_macro(self.module_id, mac.name.clone(), macro_id, is_export); 1421 self.def_collector.define_macro_rules(
1422 self.module_id,
1423 mac.name.clone(),
1424 macro_id,
1425 is_export,
1426 );
1427 }
1428
1429 fn collect_macro_def(&mut self, id: FileItemTreeId<MacroDef>) {
1430 let krate = self.def_collector.def_map.krate;
1431 let mac = &self.item_tree[id];
1432 let ast_id = InFile::new(self.file_id, mac.ast_id.upcast());
1433
1434 // Case 1: bulitin macros
1435 let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into());
1436 if attrs.by_key("rustc_builtin_macro").exists() {
1437 let macro_id = find_builtin_macro(&mac.name, krate, ast_id)
1438 .or_else(|| find_builtin_derive(&mac.name, krate, ast_id));
1439
1440 if let Some(macro_id) = macro_id {
1441 self.def_collector.define_macro_def(
1442 self.module_id,
1443 mac.name.clone(),
1444 macro_id,
1445 &self.item_tree[mac.visibility],
1446 );
1447 }
1448 return;
1449 }
1450
1451 // Case 2: normal `macro`
1452 let macro_id = MacroDefId {
1453 krate: self.def_collector.def_map.krate,
1454 kind: MacroDefKind::Declarative(ast_id),
1455 local_inner: false,
1456 };
1457
1458 self.def_collector.define_macro_def(
1459 self.module_id,
1460 mac.name.clone(),
1461 macro_id,
1462 &self.item_tree[mac.visibility],
1463 );
1440 } 1464 }
1441 1465
1442 fn collect_macro_call(&mut self, mac: &MacroCall) { 1466 fn collect_macro_call(&mut self, mac: &MacroCall) {
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index 6d3cb8d7a..c37f915ab 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -837,3 +837,21 @@ fn collects_derive_helpers() {
837 _ => unreachable!(), 837 _ => unreachable!(),
838 } 838 }
839} 839}
840
841#[test]
842fn resolve_macro_def() {
843 check(
844 r#"
845//- /lib.rs
846pub macro structs($($i:ident),*) {
847 $(struct $i { field: u32 } )*
848}
849structs!(Foo);
850"#,
851 expect![[r#"
852 crate
853 Foo: t
854 structs: m
855 "#]],
856 );
857}
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs
index 505493a74..4de951fd3 100644
--- a/crates/hir_def/src/path/lower.rs
+++ b/crates/hir_def/src/path/lower.rs
@@ -74,6 +74,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
74 // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo 74 // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
75 Some(trait_ref) => { 75 Some(trait_ref) => {
76 let path = Path::from_src(trait_ref.path()?, hygiene)?; 76 let path = Path::from_src(trait_ref.path()?, hygiene)?;
77 let num_segments = path.mod_path.segments.len();
77 kind = path.mod_path.kind; 78 kind = path.mod_path.kind;
78 79
79 let mut prefix_segments = path.mod_path.segments; 80 let mut prefix_segments = path.mod_path.segments;
@@ -85,7 +86,8 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
85 generic_args.extend(prefix_args); 86 generic_args.extend(prefix_args);
86 87
87 // Insert the type reference (T in the above example) as Self parameter for the trait 88 // Insert the type reference (T in the above example) as Self parameter for the trait
88 let last_segment = generic_args.last_mut()?; 89 let last_segment =
90 generic_args.iter_mut().rev().nth(num_segments.saturating_sub(1))?;
89 if last_segment.is_none() { 91 if last_segment.is_none() {
90 *last_segment = Some(Arc::new(GenericArgs::empty())); 92 *last_segment = Some(Arc::new(GenericArgs::empty()));
91 }; 93 };
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs
index 049b2e462..4c24aae94 100644
--- a/crates/hir_def/src/type_ref.rs
+++ b/crates/hir_def/src/type_ref.rs
@@ -51,6 +51,23 @@ impl Rawness {
51 } 51 }
52} 52}
53 53
54#[derive(Clone, PartialEq, Eq, Hash, Debug)]
55pub struct TraitRef {
56 pub path: Path,
57}
58
59impl TraitRef {
60 /// Converts an `ast::PathType` to a `hir::TraitRef`.
61 pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Option<Self> {
62 // FIXME: Use `Path::from_src`
63 match node {
64 ast::Type::PathType(path) => {
65 path.path().and_then(|it| ctx.lower_path(it)).map(|path| TraitRef { path })
66 }
67 _ => None,
68 }
69 }
70}
54/// Compare ty::Ty 71/// Compare ty::Ty
55#[derive(Clone, PartialEq, Eq, Hash, Debug)] 72#[derive(Clone, PartialEq, Eq, Hash, Debug)]
56pub enum TypeRef { 73pub enum TypeRef {
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index fc73e435b..c0ab70b60 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -3,9 +3,15 @@
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use base_db::{salsa, SourceDatabase}; 5use base_db::{salsa, SourceDatabase};
6use mbe::{ExpandError, ExpandResult, MacroRules}; 6use mbe::{ExpandError, ExpandResult, MacroDef, MacroRules};
7use parser::FragmentKind; 7use parser::FragmentKind;
8use syntax::{algo::diff, ast::NameOwner, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; 8use syntax::{
9 algo::diff,
10 ast::{MacroStmts, NameOwner},
11 AstNode, GreenNode, Parse,
12 SyntaxKind::*,
13 SyntaxNode,
14};
9 15
10use crate::{ 16use crate::{
11 ast_id_map::AstIdMap, hygiene::HygieneFrame, BuiltinDeriveExpander, BuiltinFnLikeExpander, 17 ast_id_map::AstIdMap, hygiene::HygieneFrame, BuiltinDeriveExpander, BuiltinFnLikeExpander,
@@ -22,6 +28,7 @@ const TOKEN_LIMIT: usize = 524288;
22#[derive(Debug, Clone, Eq, PartialEq)] 28#[derive(Debug, Clone, Eq, PartialEq)]
23pub enum TokenExpander { 29pub enum TokenExpander {
24 MacroRules(mbe::MacroRules), 30 MacroRules(mbe::MacroRules),
31 MacroDef(mbe::MacroDef),
25 Builtin(BuiltinFnLikeExpander), 32 Builtin(BuiltinFnLikeExpander),
26 BuiltinDerive(BuiltinDeriveExpander), 33 BuiltinDerive(BuiltinDeriveExpander),
27 ProcMacro(ProcMacroExpander), 34 ProcMacro(ProcMacroExpander),
@@ -36,6 +43,7 @@ impl TokenExpander {
36 ) -> mbe::ExpandResult<tt::Subtree> { 43 ) -> mbe::ExpandResult<tt::Subtree> {
37 match self { 44 match self {
38 TokenExpander::MacroRules(it) => it.expand(tt), 45 TokenExpander::MacroRules(it) => it.expand(tt),
46 TokenExpander::MacroDef(it) => it.expand(tt),
39 TokenExpander::Builtin(it) => it.expand(db, id, tt), 47 TokenExpander::Builtin(it) => it.expand(db, id, tt),
40 // FIXME switch these to ExpandResult as well 48 // FIXME switch these to ExpandResult as well
41 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), 49 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(),
@@ -51,6 +59,7 @@ impl TokenExpander {
51 pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId { 59 pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
52 match self { 60 match self {
53 TokenExpander::MacroRules(it) => it.map_id_down(id), 61 TokenExpander::MacroRules(it) => it.map_id_down(id),
62 TokenExpander::MacroDef(it) => it.map_id_down(id),
54 TokenExpander::Builtin(..) => id, 63 TokenExpander::Builtin(..) => id,
55 TokenExpander::BuiltinDerive(..) => id, 64 TokenExpander::BuiltinDerive(..) => id,
56 TokenExpander::ProcMacro(..) => id, 65 TokenExpander::ProcMacro(..) => id,
@@ -60,6 +69,7 @@ impl TokenExpander {
60 pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) { 69 pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) {
61 match self { 70 match self {
62 TokenExpander::MacroRules(it) => it.map_id_up(id), 71 TokenExpander::MacroRules(it) => it.map_id_up(id),
72 TokenExpander::MacroDef(it) => it.map_id_up(id),
63 TokenExpander::Builtin(..) => (id, mbe::Origin::Call), 73 TokenExpander::Builtin(..) => (id, mbe::Origin::Call),
64 TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call), 74 TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call),
65 TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), 75 TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call),
@@ -130,26 +140,40 @@ fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
130 140
131fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { 141fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
132 match id.kind { 142 match id.kind {
133 MacroDefKind::Declarative(ast_id) => { 143 MacroDefKind::Declarative(ast_id) => match ast_id.to_node(db) {
134 let macro_rules = match ast_id.to_node(db) { 144 syntax::ast::Macro::MacroRules(macro_rules) => {
135 syntax::ast::Macro::MacroRules(mac) => mac, 145 let arg = macro_rules.token_tree()?;
136 syntax::ast::Macro::MacroDef(_) => return None, 146 let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
137 }; 147 log::warn!("fail on macro_rules to token tree: {:#?}", arg);
138 let arg = macro_rules.token_tree()?; 148 None
139 let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { 149 })?;
140 log::warn!("fail on macro_def to token tree: {:#?}", arg); 150 let rules = match MacroRules::parse(&tt) {
141 None 151 Ok(it) => it,
142 })?; 152 Err(err) => {
143 let rules = match MacroRules::parse(&tt) { 153 let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default();
144 Ok(it) => it, 154 log::warn!("fail on macro_def parse ({}): {:?} {:#?}", name, err, tt);
145 Err(err) => { 155 return None;
146 let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default(); 156 }
147 log::warn!("fail on macro_def parse ({}): {:?} {:#?}", name, err, tt); 157 };
148 return None; 158 Some(Arc::new((TokenExpander::MacroRules(rules), tmap)))
149 } 159 }
150 }; 160 syntax::ast::Macro::MacroDef(macro_def) => {
151 Some(Arc::new((TokenExpander::MacroRules(rules), tmap))) 161 let arg = macro_def.body()?;
152 } 162 let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
163 log::warn!("fail on macro_def to token tree: {:#?}", arg);
164 None
165 })?;
166 let rules = match MacroDef::parse(&tt) {
167 Ok(it) => it,
168 Err(err) => {
169 let name = macro_def.name().map(|n| n.to_string()).unwrap_or_default();
170 log::warn!("fail on macro_def parse ({}): {:?} {:#?}", name, err, tt);
171 return None;
172 }
173 };
174 Some(Arc::new((TokenExpander::MacroDef(rules), tmap)))
175 }
176 },
153 MacroDefKind::BuiltIn(expander, _) => { 177 MacroDefKind::BuiltIn(expander, _) => {
154 Some(Arc::new((TokenExpander::Builtin(expander), mbe::TokenMap::default()))) 178 Some(Arc::new((TokenExpander::Builtin(expander), mbe::TokenMap::default())))
155 } 179 }
@@ -340,13 +364,19 @@ fn parse_macro_with_arg(
340 None => return ExpandResult { value: None, err: result.err }, 364 None => return ExpandResult { value: None, err: result.err },
341 }; 365 };
342 366
343 log::debug!("expanded = {}", tt.as_debug_string());
344
345 let fragment_kind = to_fragment_kind(db, macro_call_id); 367 let fragment_kind = to_fragment_kind(db, macro_call_id);
346 368
369 log::debug!("expanded = {}", tt.as_debug_string());
370 log::debug!("kind = {:?}", fragment_kind);
371
347 let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) { 372 let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) {
348 Ok(it) => it, 373 Ok(it) => it,
349 Err(err) => { 374 Err(err) => {
375 log::debug!(
376 "failed to parse expanstion to {:?} = {}",
377 fragment_kind,
378 tt.as_debug_string()
379 );
350 return ExpandResult::only_err(err); 380 return ExpandResult::only_err(err);
351 } 381 }
352 }; 382 };
@@ -362,15 +392,34 @@ fn parse_macro_with_arg(
362 return ExpandResult::only_err(err); 392 return ExpandResult::only_err(err);
363 } 393 }
364 }; 394 };
365 395 if is_self_replicating(&node, &call_node.value) {
366 if !diff(&node, &call_node.value).is_empty() {
367 ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) }
368 } else {
369 return ExpandResult::only_err(err); 396 return ExpandResult::only_err(err);
397 } else {
398 ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) }
399 }
400 }
401 None => {
402 log::debug!("parse = {:?}", parse.syntax_node().kind());
403 ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: None }
404 }
405 }
406}
407
408fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool {
409 if diff(from, to).is_empty() {
410 return true;
411 }
412 if let Some(stmts) = MacroStmts::cast(from.clone()) {
413 if stmts.statements().any(|stmt| diff(stmt.syntax(), to).is_empty()) {
414 return true;
415 }
416 if let Some(expr) = stmts.expr() {
417 if diff(expr.syntax(), to).is_empty() {
418 return true;
370 } 419 }
371 } 420 }
372 None => ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: None },
373 } 421 }
422 false
374} 423}
375 424
376fn hygiene_frame(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<HygieneFrame> { 425fn hygiene_frame(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<HygieneFrame> {
@@ -390,21 +439,15 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
390 439
391 let parent = match syn.parent() { 440 let parent = match syn.parent() {
392 Some(it) => it, 441 Some(it) => it,
393 None => { 442 None => return FragmentKind::Statements,
394 // FIXME:
395 // If it is root, which means the parent HirFile
396 // MacroKindFile must be non-items
397 // return expr now.
398 return FragmentKind::Expr;
399 }
400 }; 443 };
401 444
402 match parent.kind() { 445 match parent.kind() {
403 MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, 446 MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
404 MACRO_STMTS => FragmentKind::Statement, 447 MACRO_STMTS => FragmentKind::Statements,
405 ITEM_LIST => FragmentKind::Items, 448 ITEM_LIST => FragmentKind::Items,
406 LET_STMT => { 449 LET_STMT => {
407 // FIXME: Handle Pattern 450 // FIXME: Handle LHS Pattern
408 FragmentKind::Expr 451 FragmentKind::Expr
409 } 452 }
410 EXPR_STMT => FragmentKind::Statements, 453 EXPR_STMT => FragmentKind::Statements,
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs
index 0e0f7214a..779725629 100644
--- a/crates/hir_expand/src/hygiene.rs
+++ b/crates/hir_expand/src/hygiene.rs
@@ -148,7 +148,7 @@ fn make_hygiene_info(
148 let def_offset = loc.def.ast_id().left().and_then(|id| { 148 let def_offset = loc.def.ast_id().left().and_then(|id| {
149 let def_tt = match id.to_node(db) { 149 let def_tt = match id.to_node(db) {
150 ast::Macro::MacroRules(mac) => mac.token_tree()?.syntax().text_range().start(), 150 ast::Macro::MacroRules(mac) => mac.token_tree()?.syntax().text_range().start(),
151 ast::Macro::MacroDef(_) => return None, 151 ast::Macro::MacroDef(mac) => mac.body()?.syntax().text_range().start(),
152 }; 152 };
153 Some(InFile::new(id.file_id, def_tt)) 153 Some(InFile::new(id.file_id, def_tt))
154 }); 154 });
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index b8045fda9..3e332ee47 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -151,7 +151,7 @@ impl HirFileId {
151 let def = loc.def.ast_id().left().and_then(|id| { 151 let def = loc.def.ast_id().left().and_then(|id| {
152 let def_tt = match id.to_node(db) { 152 let def_tt = match id.to_node(db) {
153 ast::Macro::MacroRules(mac) => mac.token_tree()?, 153 ast::Macro::MacroRules(mac) => mac.token_tree()?,
154 ast::Macro::MacroDef(_) => return None, 154 ast::Macro::MacroDef(mac) => mac.body()?,
155 }; 155 };
156 Some(InFile::new(id.file_id, def_tt)) 156 Some(InFile::new(id.file_id, def_tt))
157 }); 157 });
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index 0aeea48d5..203ebbe85 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -55,6 +55,15 @@ impl Name {
55 } 55 }
56 } 56 }
57 57
58 /// A fake name for things missing in the source code.
59 ///
60 /// For example, `impl Foo for {}` should be treated as a trait impl for a
61 /// type with a missing name. Similarly, `struct S { : u32 }` should have a
62 /// single field with a missing name.
63 ///
64 /// Ideally, we want a `gensym` semantics for missing names -- each missing
65 /// name is equal only to itself. It's not clear how to implement this in
66 /// salsa though, so we punt on that bit for a moment.
58 pub fn missing() -> Name { 67 pub fn missing() -> Name {
59 Name::new_text("[missing name]".into()) 68 Name::new_text("[missing name]".into())
60 } 69 }
@@ -75,14 +84,14 @@ impl AsName for ast::NameRef {
75 fn as_name(&self) -> Name { 84 fn as_name(&self) -> Name {
76 match self.as_tuple_field() { 85 match self.as_tuple_field() {
77 Some(idx) => Name::new_tuple_field(idx), 86 Some(idx) => Name::new_tuple_field(idx),
78 None => Name::resolve(self.text()), 87 None => Name::resolve(&self.text()),
79 } 88 }
80 } 89 }
81} 90}
82 91
83impl AsName for ast::Name { 92impl AsName for ast::Name {
84 fn as_name(&self) -> Name { 93 fn as_name(&self) -> Name {
85 Name::resolve(self.text()) 94 Name::resolve(&self.text())
86 } 95 }
87} 96}
88 97
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index 5a5cdcbf3..9cb472b51 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -539,7 +539,7 @@ impl Matrix {
539 if let Some(Pat::Or(pat_ids)) = row.get_head().map(|pat_id| pat_id.as_pat(cx)) { 539 if let Some(Pat::Or(pat_ids)) = row.get_head().map(|pat_id| pat_id.as_pat(cx)) {
540 // Or patterns are expanded here 540 // Or patterns are expanded here
541 for pat_id in pat_ids { 541 for pat_id in pat_ids {
542 self.0.push(PatStack::from_pattern(pat_id)); 542 self.0.push(row.replace_head_with([pat_id].iter()));
543 } 543 }
544 } else { 544 } else {
545 self.0.push(row); 545 self.0.push(row);
@@ -1085,6 +1085,20 @@ fn main() {
1085 } 1085 }
1086 1086
1087 #[test] 1087 #[test]
1088 fn or_pattern_no_diagnostic() {
1089 check_diagnostics(
1090 r#"
1091enum Either {A, B}
1092
1093fn main() {
1094 match (Either::A, Either::B) {
1095 (Either::A | Either::B, _) => (),
1096 }
1097}"#,
1098 )
1099 }
1100
1101 #[test]
1088 fn mismatched_types() { 1102 fn mismatched_types() {
1089 // Match statements with arms that don't match the 1103 // Match statements with arms that don't match the
1090 // expression pattern do not fire this diagnostic. 1104 // expression pattern do not fire this diagnostic.
@@ -1336,30 +1350,6 @@ fn bang(never: !) {
1336 } 1350 }
1337 1351
1338 #[test] 1352 #[test]
1339 fn or_pattern_panic() {
1340 check_diagnostics(
1341 r#"
1342pub enum Category { Infinity, Zero }
1343
1344fn panic(a: Category, b: Category) {
1345 match (a, b) {
1346 (Category::Zero | Category::Infinity, _) => (),
1347 (_, Category::Zero | Category::Infinity) => (),
1348 }
1349
1350 // FIXME: This is a false positive, but the code used to cause a panic in the match checker,
1351 // so this acts as a regression test for that.
1352 match (a, b) {
1353 //^^^^^^ Missing match arm
1354 (Category::Infinity, Category::Infinity) | (Category::Zero, Category::Zero) => (),
1355 (Category::Infinity | Category::Zero, _) => (),
1356 }
1357}
1358"#,
1359 );
1360 }
1361
1362 #[test]
1363 fn unknown_type() { 1353 fn unknown_type() {
1364 check_diagnostics( 1354 check_diagnostics(
1365 r#" 1355 r#"
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 3f3187ea2..6279aa572 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -119,6 +119,8 @@ impl<'a> InferenceContext<'a> {
119 } 119 }
120 120
121 fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { 121 fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
122 self.db.check_canceled();
123
122 let body = Arc::clone(&self.body); // avoid borrow checker problem 124 let body = Arc::clone(&self.body); // avoid borrow checker problem
123 let ty = match &body[tgt_expr] { 125 let ty = match &body[tgt_expr] {
124 Expr::Missing => self.err_ty(), 126 Expr::Missing => self.err_ty(),
@@ -767,6 +769,7 @@ impl<'a> InferenceContext<'a> {
767 None => self.table.new_float_var(), 769 None => self.table.new_float_var(),
768 }, 770 },
769 }, 771 },
772 Expr::MacroStmts { tail } => self.infer_expr(*tail, expected),
770 }; 773 };
771 // use a new type variable if we got unknown here 774 // use a new type variable if we got unknown here
772 let ty = self.insert_type_vars_shallow(ty); 775 let ty = self.insert_type_vars_shallow(ty);
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index c87789d45..afbfa12d5 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -15,7 +15,7 @@ use hir_def::{
15 generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget}, 15 generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget},
16 path::{GenericArg, Path, PathSegment, PathSegments}, 16 path::{GenericArg, Path, PathSegment, PathSegments},
17 resolver::{HasResolver, Resolver, TypeNs}, 17 resolver::{HasResolver, Resolver, TypeNs},
18 type_ref::{TypeBound, TypeRef}, 18 type_ref::{TraitRef as HirTraitRef, TypeBound, TypeRef},
19 AdtId, AssocContainerId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, 19 AdtId, AssocContainerId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId,
20 GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, 20 GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId,
21 TypeAliasId, TypeParamId, UnionId, VariantId, 21 TypeAliasId, TypeParamId, UnionId, VariantId,
@@ -667,14 +667,10 @@ impl<'a> TyLoweringContext<'a> {
667 667
668 fn lower_trait_ref( 668 fn lower_trait_ref(
669 &self, 669 &self,
670 type_ref: &TypeRef, 670 trait_ref: &HirTraitRef,
671 explicit_self_ty: Option<Ty>, 671 explicit_self_ty: Option<Ty>,
672 ) -> Option<TraitRef> { 672 ) -> Option<TraitRef> {
673 let path = match type_ref { 673 self.lower_trait_ref_from_path(&trait_ref.path, explicit_self_ty)
674 TypeRef::Path(path) => path,
675 _ => return None,
676 };
677 self.lower_trait_ref_from_path(path, explicit_self_ty)
678 } 674 }
679 675
680 fn trait_ref_substs_from_path( 676 fn trait_ref_substs_from_path(
@@ -1253,7 +1249,7 @@ pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binde
1253 let generics = generics(db.upcast(), impl_id.into()); 1249 let generics = generics(db.upcast(), impl_id.into());
1254 let ctx = 1250 let ctx =
1255 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); 1251 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
1256 Binders::new(generics.len(), ctx.lower_ty(&impl_data.target_type)) 1252 Binders::new(generics.len(), ctx.lower_ty(&impl_data.self_ty))
1257} 1253}
1258 1254
1259pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty { 1255pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty {
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index 7eda51866..3eb01dbd0 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -135,7 +135,88 @@ fn infer_path_qualified_macros_expanded() {
135} 135}
136 136
137#[test] 137#[test]
138fn expr_macro_expanded_in_various_places() { 138fn expr_macro_def_expanded_in_various_places() {
139 check_infer(
140 r#"
141 macro spam() {
142 1isize
143 }
144
145 fn spam() {
146 spam!();
147 (spam!());
148 spam!().spam(spam!());
149 for _ in spam!() {}
150 || spam!();
151 while spam!() {}
152 break spam!();
153 return spam!();
154 match spam!() {
155 _ if spam!() => spam!(),
156 }
157 spam!()(spam!());
158 Spam { spam: spam!() };
159 spam!()[spam!()];
160 await spam!();
161 spam!() as usize;
162 &spam!();
163 -spam!();
164 spam!()..spam!();
165 spam!() + spam!();
166 }
167 "#,
168 expect![[r#"
169 !0..6 '1isize': isize
170 !0..6 '1isize': isize
171 !0..6 '1isize': isize
172 !0..6 '1isize': isize
173 !0..6 '1isize': isize
174 !0..6 '1isize': isize
175 !0..6 '1isize': isize
176 !0..6 '1isize': isize
177 !0..6 '1isize': isize
178 !0..6 '1isize': isize
179 !0..6 '1isize': isize
180 !0..6 '1isize': isize
181 !0..6 '1isize': isize
182 !0..6 '1isize': isize
183 !0..6 '1isize': isize
184 !0..6 '1isize': isize
185 !0..6 '1isize': isize
186 !0..6 '1isize': isize
187 !0..6 '1isize': isize
188 !0..6 '1isize': isize
189 !0..6 '1isize': isize
190 !0..6 '1isize': isize
191 !0..6 '1isize': isize
192 !0..6 '1isize': isize
193 !0..6 '1isize': isize
194 39..442 '{ ...!(); }': ()
195 73..94 'spam!(...am!())': {unknown}
196 100..119 'for _ ...!() {}': ()
197 104..105 '_': {unknown}
198 117..119 '{}': ()
199 124..134 '|| spam!()': || -> isize
200 140..156 'while ...!() {}': ()
201 154..156 '{}': ()
202 161..174 'break spam!()': !
203 180..194 'return spam!()': !
204 200..254 'match ... }': isize
205 224..225 '_': isize
206 259..275 'spam!(...am!())': {unknown}
207 281..303 'Spam {...m!() }': {unknown}
208 309..325 'spam!(...am!()]': {unknown}
209 350..366 'spam!(... usize': usize
210 372..380 '&spam!()': &isize
211 386..394 '-spam!()': isize
212 400..416 'spam!(...pam!()': {unknown}
213 422..439 'spam!(...pam!()': isize
214 "#]],
215 );
216}
217
218#[test]
219fn expr_macro_rules_expanded_in_various_places() {
139 check_infer( 220 check_infer(
140 r#" 221 r#"
141 macro_rules! spam { 222 macro_rules! spam {
@@ -226,12 +307,49 @@ fn expr_macro_expanded_in_stmts() {
226 "#, 307 "#,
227 expect![[r#" 308 expect![[r#"
228 !0..8 'leta=();': () 309 !0..8 'leta=();': ()
310 !0..8 'leta=();': ()
311 !3..4 'a': ()
312 !5..7 '()': ()
229 57..84 '{ ...); } }': () 313 57..84 '{ ...); } }': ()
230 "#]], 314 "#]],
231 ); 315 );
232} 316}
233 317
234#[test] 318#[test]
319fn recurisve_macro_expanded_in_stmts() {
320 check_infer(
321 r#"
322 macro_rules! ng {
323 ([$($tts:tt)*]) => {
324 $($tts)*;
325 };
326 ([$($tts:tt)*] $head:tt $($rest:tt)*) => {
327 ng! {
328 [$($tts)* $head] $($rest)*
329 }
330 };
331 }
332 fn foo() {
333 ng!([] let a = 3);
334 let b = a;
335 }
336 "#,
337 expect![[r#"
338 !0..7 'leta=3;': {unknown}
339 !0..7 'leta=3;': {unknown}
340 !0..13 'ng!{[leta=3]}': {unknown}
341 !0..13 'ng!{[leta=]3}': {unknown}
342 !0..13 'ng!{[leta]=3}': {unknown}
343 !3..4 'a': i32
344 !5..6 '3': i32
345 196..237 '{ ...= a; }': ()
346 229..230 'b': i32
347 233..234 'a': i32
348 "#]],
349 );
350}
351
352#[test]
235fn recursive_inner_item_macro_rules() { 353fn recursive_inner_item_macro_rules() {
236 check_infer( 354 check_infer(
237 r#" 355 r#"
@@ -246,7 +364,8 @@ fn recursive_inner_item_macro_rules() {
246 "#, 364 "#,
247 expect![[r#" 365 expect![[r#"
248 !0..1 '1': i32 366 !0..1 '1': i32
249 !0..7 'mac!($)': {unknown} 367 !0..26 'macro_...>{1};}': {unknown}
368 !0..26 'macro_...>{1};}': {unknown}
250 107..143 '{ ...!(); }': () 369 107..143 '{ ...!(); }': ()
251 129..130 'a': i32 370 129..130 'a': i32
252 "#]], 371 "#]],
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 2ba97f814..65b71fdfa 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -3370,3 +3370,46 @@ fn test() {
3370 "#]], 3370 "#]],
3371 ) 3371 )
3372} 3372}
3373
3374#[test]
3375fn qualified_path_as_qualified_trait() {
3376 check_infer(
3377 r#"
3378mod foo {
3379
3380 pub trait Foo {
3381 type Target;
3382 }
3383 pub trait Bar {
3384 type Output;
3385 fn boo() -> Self::Output {
3386 loop {}
3387 }
3388 }
3389}
3390
3391struct F;
3392impl foo::Foo for F {
3393 type Target = ();
3394}
3395impl foo::Bar for F {
3396 type Output = <F as foo::Foo>::Target;
3397}
3398
3399fn foo() {
3400 use foo::Bar;
3401 let x = <F as Bar>::boo();
3402}
3403
3404 "#,
3405 expect![[r#"
3406 132..163 '{ ... }': Bar::Output<Self>
3407 146..153 'loop {}': !
3408 151..153 '{}': ()
3409 306..358 '{ ...o(); }': ()
3410 334..335 'x': ()
3411 338..353 '<F as Bar>::boo': fn boo<F>() -> <F as Bar>::Output
3412 338..355 '<F as ...:boo()': ()
3413 "#]],
3414 );
3415}
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs
index 64bc926f1..5ebe7fd0e 100644
--- a/crates/ide/src/annotations.rs
+++ b/crates/ide/src/annotations.rs
@@ -19,6 +19,8 @@ use crate::{
19// 19//
20// Provides user with annotations above items for looking up references or impl blocks 20// Provides user with annotations above items for looking up references or impl blocks
21// and running/debugging binaries. 21// and running/debugging binaries.
22//
23// image::https://user-images.githubusercontent.com/48062697/113020672-b7c34f00-917a-11eb-8f6e-858735660a0e.png[]
22#[derive(Debug)] 24#[derive(Debug)]
23pub struct Annotation { 25pub struct Annotation {
24 pub range: TextRange, 26 pub range: TextRange,
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 22697a537..dd42116a7 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -165,7 +165,6 @@ pub(crate) fn diagnostics(
165 sema.diagnostics_display_range(d.display_source()).range, 165 sema.diagnostics_display_range(d.display_source()).range,
166 d.message(), 166 d.message(),
167 ) 167 )
168 .with_unused(true)
169 .with_fix(d.fix(&sema)) 168 .with_fix(d.fix(&sema))
170 .with_code(Some(d.code())), 169 .with_code(Some(d.code())),
171 ); 170 );
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index 2f840909c..5fb3e2d91 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -210,7 +210,7 @@ fn missing_record_expr_field_fix(
210 } 210 }
211 let new_field = make::record_field( 211 let new_field = make::record_field(
212 None, 212 None,
213 make::name(record_expr_field.field_name()?.text()), 213 make::name(&record_expr_field.field_name()?.text()),
214 make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?), 214 make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?),
215 ); 215 );
216 216
diff --git a/crates/ide/src/diagnostics/unlinked_file.rs b/crates/ide/src/diagnostics/unlinked_file.rs
index 019b0b440..e174fb767 100644
--- a/crates/ide/src/diagnostics/unlinked_file.rs
+++ b/crates/ide/src/diagnostics/unlinked_file.rs
@@ -63,7 +63,7 @@ impl DiagnosticWithFix for UnlinkedFile {
63 // - `$dir.rs` in the parent folder, where `$dir` is the directory containing `self.file_id` 63 // - `$dir.rs` in the parent folder, where `$dir` is the directory containing `self.file_id`
64 let parent = our_path.parent()?; 64 let parent = our_path.parent()?;
65 let mut paths = 65 let mut paths =
66 vec![parent.join("mod.rs")?, parent.join("main.rs")?, parent.join("lib.rs")?]; 66 vec![parent.join("mod.rs")?, parent.join("lib.rs")?, parent.join("main.rs")?];
67 67
68 // `submod/bla.rs` -> `submod.rs` 68 // `submod/bla.rs` -> `submod.rs`
69 if let Some(newmod) = (|| { 69 if let Some(newmod) = (|| {
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 0cee741ac..67e2e5a1c 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -98,6 +98,29 @@ pub(crate) fn remove_links(markdown: &str) -> String {
98 out 98 out
99} 99}
100 100
101/// Retrieve a link to documentation for the given symbol.
102pub(crate) fn external_docs(
103 db: &RootDatabase,
104 position: &FilePosition,
105) -> Option<DocumentationLink> {
106 let sema = Semantics::new(db);
107 let file = sema.parse(position.file_id).syntax().clone();
108 let token = pick_best(file.token_at_offset(position.offset))?;
109 let token = sema.descend_into_macros(token);
110
111 let node = token.parent()?;
112 let definition = match_ast! {
113 match node {
114 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)),
115 ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db)),
116 _ => None,
117 }
118 };
119
120 get_doc_link(db, definition?)
121}
122
123/// Extracts all links from a given markdown text.
101pub(crate) fn extract_definitions_from_markdown( 124pub(crate) fn extract_definitions_from_markdown(
102 markdown: &str, 125 markdown: &str,
103) -> Vec<(Range<usize>, String, Option<hir::Namespace>)> { 126) -> Vec<(Range<usize>, String, Option<hir::Namespace>)> {
@@ -161,7 +184,7 @@ pub(crate) fn doc_owner_to_def(
161 ast::Variant(it) => sema.to_def(&it)?.into(), 184 ast::Variant(it) => sema.to_def(&it)?.into(),
162 ast::Trait(it) => sema.to_def(&it)?.into(), 185 ast::Trait(it) => sema.to_def(&it)?.into(),
163 ast::Impl(it) => return sema.to_def(&it).map(Definition::SelfType), 186 ast::Impl(it) => return sema.to_def(&it).map(Definition::SelfType),
164 ast::MacroRules(it) => return sema.to_def(&it).map(Definition::Macro), 187 ast::Macro(it) => return sema.to_def(&it).map(Definition::Macro),
165 ast::TupleField(it) => return sema.to_def(&it).map(Definition::Field), 188 ast::TupleField(it) => return sema.to_def(&it).map(Definition::Field),
166 ast::RecordField(it) => return sema.to_def(&it).map(Definition::Field), 189 ast::RecordField(it) => return sema.to_def(&it).map(Definition::Field),
167 _ => return None, 190 _ => return None,
@@ -178,15 +201,15 @@ pub(crate) fn resolve_doc_path_for_def(
178) -> Option<hir::ModuleDef> { 201) -> Option<hir::ModuleDef> {
179 match def { 202 match def {
180 Definition::ModuleDef(def) => match def { 203 Definition::ModuleDef(def) => match def {
181 ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns), 204 hir::ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns),
182 ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns), 205 hir::ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns),
183 ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns), 206 hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns),
184 ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns), 207 hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns),
185 ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns), 208 hir::ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns),
186 ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns), 209 hir::ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns),
187 ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns), 210 hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns),
188 ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns), 211 hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns),
189 ModuleDef::BuiltinType(_) => None, 212 hir::ModuleDef::BuiltinType(_) => None,
190 }, 213 },
191 Definition::Macro(it) => it.resolve_doc_path(db, &link, ns), 214 Definition::Macro(it) => it.resolve_doc_path(db, &link, ns),
192 Definition::Field(it) => it.resolve_doc_path(db, &link, ns), 215 Definition::Field(it) => it.resolve_doc_path(db, &link, ns),
@@ -214,7 +237,7 @@ fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> {
214 .and_then(|assoc| match assoc.container(db) { 237 .and_then(|assoc| match assoc.container(db) {
215 AssocItemContainer::Trait(t) => Some(t.into()), 238 AssocItemContainer::Trait(t) => Some(t.into()),
216 AssocItemContainer::Impl(impld) => { 239 AssocItemContainer::Impl(impld) => {
217 impld.target_ty(db).as_adt().map(|adt| adt.into()) 240 impld.self_ty(db).as_adt().map(|adt| adt.into())
218 } 241 }
219 }) 242 })
220 .unwrap_or_else(|| f.clone().into()), 243 .unwrap_or_else(|| f.clone().into()),
@@ -328,28 +351,6 @@ fn rewrite_url_link(db: &RootDatabase, def: ModuleDef, target: &str) -> Option<S
328 .map(|url| url.into_string()) 351 .map(|url| url.into_string())
329} 352}
330 353
331/// Retrieve a link to documentation for the given symbol.
332pub(crate) fn external_docs(
333 db: &RootDatabase,
334 position: &FilePosition,
335) -> Option<DocumentationLink> {
336 let sema = Semantics::new(db);
337 let file = sema.parse(position.file_id).syntax().clone();
338 let token = pick_best(file.token_at_offset(position.offset))?;
339 let token = sema.descend_into_macros(token);
340
341 let node = token.parent()?;
342 let definition = match_ast! {
343 match node {
344 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)),
345 ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db)),
346 _ => None,
347 }
348 };
349
350 get_doc_link(db, definition?)
351}
352
353/// Rewrites a markdown document, applying 'callback' to each link. 354/// Rewrites a markdown document, applying 'callback' to each link.
354fn map_links<'e>( 355fn map_links<'e>(
355 events: impl Iterator<Item = Event<'e>>, 356 events: impl Iterator<Item = Event<'e>>,
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index ffb3a6f7d..9eeabbeda 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -23,6 +23,8 @@ pub struct ExpandedMacro {
23// 23//
24// | VS Code | **Rust Analyzer: Expand macro recursively** 24// | VS Code | **Rust Analyzer: Expand macro recursively**
25// |=== 25// |===
26//
27// image::https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif[]
26pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { 28pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
27 let sema = Semantics::new(db); 29 let sema = Semantics::new(db);
28 let file = sema.parse(position.file_id); 30 let file = sema.parse(position.file_id);
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs
index 5201ce587..7032889ac 100644
--- a/crates/ide/src/extend_selection.rs
+++ b/crates/ide/src/extend_selection.rs
@@ -24,6 +24,8 @@ use crate::FileRange;
24// 24//
25// | VS Code | kbd:[Alt+Shift+→], kbd:[Alt+Shift+←] 25// | VS Code | kbd:[Alt+Shift+→], kbd:[Alt+Shift+←]
26// |=== 26// |===
27//
28// image::https://user-images.githubusercontent.com/48062697/113020651-b42fc800-917a-11eb-8a4f-cf1a07859fac.gif[]
27pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { 29pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
28 let sema = Semantics::new(db); 30 let sema = Semantics::new(db);
29 let src = sema.parse(frange.file_id); 31 let src = sema.parse(frange.file_id);
diff --git a/crates/ide/src/file_structure.rs b/crates/ide/src/file_structure.rs
index 9f879a66e..19071d6be 100644
--- a/crates/ide/src/file_structure.rs
+++ b/crates/ide/src/file_structure.rs
@@ -35,6 +35,9 @@ pub enum StructureNodeKind {
35// 35//
36// | VS Code | kbd:[Ctrl+Shift+O] 36// | VS Code | kbd:[Ctrl+Shift+O]
37// |=== 37// |===
38//
39// image::https://user-images.githubusercontent.com/48062697/113020654-b42fc800-917a-11eb-8388-e7dc4d92b02e.gif[]
40
38pub(crate) fn file_structure(file: &SourceFile) -> Vec<StructureNode> { 41pub(crate) fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
39 let mut res = Vec::new(); 42 let mut res = Vec::new();
40 let mut stack = Vec::new(); 43 let mut stack = Vec::new();
@@ -172,7 +175,7 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
172 }; 175 };
173 Some(node) 176 Some(node)
174 }, 177 },
175 ast::MacroRules(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Macro)), 178 ast::Macro(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Macro)),
176 _ => None, 179 _ => None,
177 } 180 }
178 } 181 }
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
index 4b1b24562..153726ce8 100644
--- a/crates/ide/src/folding_ranges.rs
+++ b/crates/ide/src/folding_ranges.rs
@@ -17,6 +17,8 @@ pub enum FoldKind {
17 Block, 17 Block,
18 ArgList, 18 ArgList,
19 Region, 19 Region,
20 Consts,
21 Statics,
20} 22}
21 23
22#[derive(Debug)] 24#[derive(Debug)]
@@ -30,6 +32,8 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
30 let mut visited_comments = FxHashSet::default(); 32 let mut visited_comments = FxHashSet::default();
31 let mut visited_imports = FxHashSet::default(); 33 let mut visited_imports = FxHashSet::default();
32 let mut visited_mods = FxHashSet::default(); 34 let mut visited_mods = FxHashSet::default();
35 let mut visited_consts = FxHashSet::default();
36 let mut visited_statics = FxHashSet::default();
33 // regions can be nested, here is a LIFO buffer 37 // regions can be nested, here is a LIFO buffer
34 let mut regions_starts: Vec<TextSize> = vec![]; 38 let mut regions_starts: Vec<TextSize> = vec![];
35 39
@@ -91,6 +95,19 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
91 res.push(Fold { range, kind: FoldKind::Mods }) 95 res.push(Fold { range, kind: FoldKind::Mods })
92 } 96 }
93 } 97 }
98
99 // Fold groups of consts
100 if node.kind() == CONST && !visited_consts.contains(&node) {
101 if let Some(range) = contiguous_range_for_group(&node, &mut visited_consts) {
102 res.push(Fold { range, kind: FoldKind::Consts })
103 }
104 }
105 // Fold groups of consts
106 if node.kind() == STATIC && !visited_statics.contains(&node) {
107 if let Some(range) = contiguous_range_for_group(&node, &mut visited_statics) {
108 res.push(Fold { range, kind: FoldKind::Statics })
109 }
110 }
94 } 111 }
95 } 112 }
96 } 113 }
@@ -250,6 +267,8 @@ mod tests {
250 FoldKind::Block => "block", 267 FoldKind::Block => "block",
251 FoldKind::ArgList => "arglist", 268 FoldKind::ArgList => "arglist",
252 FoldKind::Region => "region", 269 FoldKind::Region => "region",
270 FoldKind::Consts => "consts",
271 FoldKind::Statics => "statics",
253 }; 272 };
254 assert_eq!(kind, &attr.unwrap()); 273 assert_eq!(kind, &attr.unwrap());
255 } 274 }
@@ -457,4 +476,24 @@ calling_function(x,y);
457"#, 476"#,
458 ) 477 )
459 } 478 }
479
480 #[test]
481 fn fold_consecutive_const() {
482 check(
483 r#"
484<fold consts>const FIRST_CONST: &str = "first";
485const SECOND_CONST: &str = "second";</fold>
486 "#,
487 )
488 }
489
490 #[test]
491 fn fold_consecutive_static() {
492 check(
493 r#"
494<fold statics>static FIRST_STATIC: &str = "first";
495static SECOND_STATIC: &str = "second";</fold>
496 "#,
497 )
498 }
460} 499}
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index a2c97061f..8574d1e3f 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -21,6 +21,8 @@ use crate::{
21// 21//
22// | VS Code | kbd:[F12] 22// | VS Code | kbd:[F12]
23// |=== 23// |===
24//
25// image::https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif[]
24pub(crate) fn goto_definition( 26pub(crate) fn goto_definition(
25 db: &RootDatabase, 27 db: &RootDatabase,
26 position: FilePosition, 28 position: FilePosition,
@@ -918,6 +920,21 @@ fn f() -> impl Iterator<Item$0 = u8> {}
918 } 920 }
919 921
920 #[test] 922 #[test]
923 #[should_panic = "unresolved reference"]
924 fn unknown_assoc_ty() {
925 check(
926 r#"
927trait Iterator {
928 type Item;
929 //^^^^
930}
931
932fn f() -> impl Iterator<Invalid$0 = u8> {}
933 "#,
934 )
935 }
936
937 #[test]
921 fn goto_def_for_assoc_ty_in_path_multiple() { 938 fn goto_def_for_assoc_ty_in_path_multiple() {
922 check( 939 check(
923 r#" 940 r#"
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs
index f4d7c14a6..05130a237 100644
--- a/crates/ide/src/goto_implementation.rs
+++ b/crates/ide/src/goto_implementation.rs
@@ -16,6 +16,8 @@ use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};
16// 16//
17// | VS Code | kbd:[Ctrl+F12] 17// | VS Code | kbd:[Ctrl+F12]
18// |=== 18// |===
19//
20// image::https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif[]
19pub(crate) fn goto_implementation( 21pub(crate) fn goto_implementation(
20 db: &RootDatabase, 22 db: &RootDatabase,
21 position: FilePosition, 23 position: FilePosition,
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs
index 2d38cb112..9d34b109b 100644
--- a/crates/ide/src/goto_type_definition.rs
+++ b/crates/ide/src/goto_type_definition.rs
@@ -12,6 +12,8 @@ use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};
12// 12//
13// | VS Code | **Go to Type Definition* 13// | VS Code | **Go to Type Definition*
14// |=== 14// |===
15//
16// image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[]
15pub(crate) fn goto_type_definition( 17pub(crate) fn goto_type_definition(
16 db: &RootDatabase, 18 db: &RootDatabase,
17 position: FilePosition, 19 position: FilePosition,
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index c43089476..5f9edb476 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -82,6 +82,8 @@ pub struct HoverResult {
82// 82//
83// Shows additional information, like type of an expression or documentation for definition when "focusing" code. 83// Shows additional information, like type of an expression or documentation for definition when "focusing" code.
84// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut. 84// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
85//
86// image::https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif[]
85pub(crate) fn hover( 87pub(crate) fn hover(
86 db: &RootDatabase, 88 db: &RootDatabase,
87 position: FilePosition, 89 position: FilePosition,
@@ -195,7 +197,7 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov
195 let adt = match def { 197 let adt = match def {
196 Definition::ModuleDef(ModuleDef::Trait(it)) => return it.try_to_nav(db).map(to_action), 198 Definition::ModuleDef(ModuleDef::Trait(it)) => return it.try_to_nav(db).map(to_action),
197 Definition::ModuleDef(ModuleDef::Adt(it)) => Some(it), 199 Definition::ModuleDef(ModuleDef::Adt(it)) => Some(it),
198 Definition::SelfType(it) => it.target_ty(db).as_adt(), 200 Definition::SelfType(it) => it.self_ty(db).as_adt(),
199 _ => None, 201 _ => None,
200 }?; 202 }?;
201 adt.try_to_nav(db).map(to_action) 203 adt.try_to_nav(db).map(to_action)
@@ -318,7 +320,7 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
318 Definition::ModuleDef(md) => match md { 320 Definition::ModuleDef(md) => match md {
319 ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) { 321 ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) {
320 AssocItemContainer::Trait(t) => Some(t.name(db)), 322 AssocItemContainer::Trait(t) => Some(t.name(db)),
321 AssocItemContainer::Impl(i) => i.target_ty(db).as_adt().map(|adt| adt.name(db)), 323 AssocItemContainer::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)),
322 }, 324 },
323 ModuleDef::Variant(e) => Some(e.parent_enum(db).name(db)), 325 ModuleDef::Variant(e) => Some(e.parent_enum(db).name(db)),
324 _ => None, 326 _ => None,
@@ -376,7 +378,7 @@ fn hover_for_definition(
376 }, 378 },
377 Definition::Local(it) => hover_for_local(it, db), 379 Definition::Local(it) => hover_for_local(it, db),
378 Definition::SelfType(impl_def) => { 380 Definition::SelfType(impl_def) => {
379 impl_def.target_ty(db).as_adt().and_then(|adt| from_hir_fmt(db, adt, mod_path)) 381 impl_def.self_ty(db).as_adt().and_then(|adt| from_hir_fmt(db, adt, mod_path))
380 } 382 }
381 Definition::GenericParam(it) => from_hir_fmt(db, it, None), 383 Definition::GenericParam(it) => from_hir_fmt(db, it, None),
382 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))), 384 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))),
@@ -470,6 +472,7 @@ fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module>
470 472
471fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 473fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
472 return tokens.max_by_key(priority); 474 return tokens.max_by_key(priority);
475
473 fn priority(n: &SyntaxToken) -> usize { 476 fn priority(n: &SyntaxToken) -> usize {
474 match n.kind() { 477 match n.kind() {
475 IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3, 478 IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,
@@ -1235,6 +1238,37 @@ fn f() { fo$0o!(); }
1235 } 1238 }
1236 1239
1237 #[test] 1240 #[test]
1241 fn test_hover_macro2_invocation() {
1242 check(
1243 r#"
1244/// foo bar
1245///
1246/// foo bar baz
1247macro foo() {}
1248
1249fn f() { fo$0o!(); }
1250"#,
1251 expect![[r#"
1252 *foo*
1253
1254 ```rust
1255 test
1256 ```
1257
1258 ```rust
1259 macro foo
1260 ```
1261
1262 ---
1263
1264 foo bar
1265
1266 foo bar baz
1267 "#]],
1268 )
1269 }
1270
1271 #[test]
1238 fn test_hover_tuple_field() { 1272 fn test_hover_tuple_field() {
1239 check( 1273 check(
1240 r#"struct TS(String, i32$0);"#, 1274 r#"struct TS(String, i32$0);"#,
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 16c04eeee..f73edf8b6 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -52,6 +52,8 @@ pub struct InlayHint {
52// 52//
53// | VS Code | **Rust Analyzer: Toggle inlay hints* 53// | VS Code | **Rust Analyzer: Toggle inlay hints*
54// |=== 54// |===
55//
56// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[]
55pub(crate) fn inlay_hints( 57pub(crate) fn inlay_hints(
56 db: &RootDatabase, 58 db: &RootDatabase,
57 file_id: FileId, 59 file_id: FileId,
@@ -416,7 +418,7 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> {
416 match expr { 418 match expr {
417 ast::Expr::MethodCallExpr(method_call_expr) => { 419 ast::Expr::MethodCallExpr(method_call_expr) => {
418 let name_ref = method_call_expr.name_ref()?; 420 let name_ref = method_call_expr.name_ref()?;
419 match name_ref.text() { 421 match name_ref.text().as_str() {
420 "clone" => method_call_expr.receiver().map(|rec| rec.to_string()), 422 "clone" => method_call_expr.receiver().map(|rec| rec.to_string()),
421 name_ref => Some(name_ref.to_owned()), 423 name_ref => Some(name_ref.to_owned()),
422 } 424 }
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index 4b25135cd..d584190f7 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -19,6 +19,8 @@ use text_edit::{TextEdit, TextEditBuilder};
19// 19//
20// | VS Code | **Rust Analyzer: Join lines** 20// | VS Code | **Rust Analyzer: Join lines**
21// |=== 21// |===
22//
23// image::https://user-images.githubusercontent.com/48062697/113020661-b6922200-917a-11eb-87c4-b75acc028f11.gif[]
22pub(crate) fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit { 24pub(crate) fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit {
23 let range = if range.is_empty() { 25 let range = if range.is_empty() {
24 let syntax = file.syntax(); 26 let syntax = file.syntax();
diff --git a/crates/ide/src/matching_brace.rs b/crates/ide/src/matching_brace.rs
index 4241a6dac..261dcc255 100644
--- a/crates/ide/src/matching_brace.rs
+++ b/crates/ide/src/matching_brace.rs
@@ -14,6 +14,8 @@ use syntax::{
14// 14//
15// | VS Code | **Rust Analyzer: Find matching brace** 15// | VS Code | **Rust Analyzer: Find matching brace**
16// |=== 16// |===
17//
18// image::https://user-images.githubusercontent.com/48062697/113065573-04298180-91b1-11eb-8dec-d4e2a202f304.gif[]
17pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> { 19pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> {
18 const BRACES: &[SyntaxKind] = 20 const BRACES: &[SyntaxKind] =
19 &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]]; 21 &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]];
diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs
index 48690b073..8d37f4f92 100644
--- a/crates/ide/src/move_item.rs
+++ b/crates/ide/src/move_item.rs
@@ -4,10 +4,12 @@ use hir::Semantics;
4use ide_db::{base_db::FileRange, RootDatabase}; 4use ide_db::{base_db::FileRange, RootDatabase};
5use itertools::Itertools; 5use itertools::Itertools;
6use syntax::{ 6use syntax::{
7 algo, ast, match_ast, AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, 7 algo, ast, match_ast, AstNode, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange,
8 TokenAtOffset,
8}; 9};
9use text_edit::{TextEdit, TextEditBuilder}; 10use text_edit::{TextEdit, TextEditBuilder};
10 11
12#[derive(Copy, Clone, Debug)]
11pub enum Direction { 13pub enum Direction {
12 Up, 14 Up,
13 Down, 15 Down,
@@ -23,6 +25,8 @@ pub enum Direction {
23// | VS Code | **Rust Analyzer: Move item up** 25// | VS Code | **Rust Analyzer: Move item up**
24// | VS Code | **Rust Analyzer: Move item down** 26// | VS Code | **Rust Analyzer: Move item down**
25// |=== 27// |===
28//
29// image::https://user-images.githubusercontent.com/48062697/113065576-04298180-91b1-11eb-91ce-4505e99ed598.gif[]
26pub(crate) fn move_item( 30pub(crate) fn move_item(
27 db: &RootDatabase, 31 db: &RootDatabase,
28 range: FileRange, 32 range: FileRange,
@@ -31,14 +35,19 @@ pub(crate) fn move_item(
31 let sema = Semantics::new(db); 35 let sema = Semantics::new(db);
32 let file = sema.parse(range.file_id); 36 let file = sema.parse(range.file_id);
33 37
34 let item = file.syntax().covering_element(range.range); 38 let item = if range.range.is_empty() {
39 SyntaxElement::Token(pick_best(file.syntax().token_at_offset(range.range.start()))?)
40 } else {
41 file.syntax().covering_element(range.range)
42 };
43
35 find_ancestors(item, direction, range.range) 44 find_ancestors(item, direction, range.range)
36} 45}
37 46
38fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -> Option<TextEdit> { 47fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -> Option<TextEdit> {
39 let root = match item { 48 let root = match item {
40 NodeOrToken::Node(node) => node, 49 SyntaxElement::Node(node) => node,
41 NodeOrToken::Token(token) => token.parent()?, 50 SyntaxElement::Token(token) => token.parent()?,
42 }; 51 };
43 52
44 let movable = [ 53 let movable = [
@@ -51,6 +60,11 @@ fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -
51 SyntaxKind::PARAM, 60 SyntaxKind::PARAM,
52 SyntaxKind::LET_STMT, 61 SyntaxKind::LET_STMT,
53 SyntaxKind::EXPR_STMT, 62 SyntaxKind::EXPR_STMT,
63 SyntaxKind::IF_EXPR,
64 SyntaxKind::FOR_EXPR,
65 SyntaxKind::LOOP_EXPR,
66 SyntaxKind::WHILE_EXPR,
67 SyntaxKind::RETURN_EXPR,
54 SyntaxKind::MATCH_EXPR, 68 SyntaxKind::MATCH_EXPR,
55 SyntaxKind::MACRO_CALL, 69 SyntaxKind::MACRO_CALL,
56 SyntaxKind::TYPE_ALIAS, 70 SyntaxKind::TYPE_ALIAS,
@@ -66,6 +80,7 @@ fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -
66 SyntaxKind::STATIC, 80 SyntaxKind::STATIC,
67 SyntaxKind::CONST, 81 SyntaxKind::CONST,
68 SyntaxKind::MACRO_RULES, 82 SyntaxKind::MACRO_RULES,
83 SyntaxKind::MACRO_DEF,
69 ]; 84 ];
70 85
71 let ancestor = once(root.clone()) 86 let ancestor = once(root.clone())
@@ -82,11 +97,11 @@ fn move_in_direction(
82) -> Option<TextEdit> { 97) -> Option<TextEdit> {
83 match_ast! { 98 match_ast! {
84 match node { 99 match node {
85 ast::ArgList(it) => swap_sibling_in_list(it.args(), range, direction), 100 ast::ArgList(it) => swap_sibling_in_list(node, it.args(), range, direction),
86 ast::GenericParamList(it) => swap_sibling_in_list(it.generic_params(), range, direction), 101 ast::GenericParamList(it) => swap_sibling_in_list(node, it.generic_params(), range, direction),
87 ast::GenericArgList(it) => swap_sibling_in_list(it.generic_args(), range, direction), 102 ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction),
88 ast::VariantList(it) => swap_sibling_in_list(it.variants(), range, direction), 103 ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction),
89 ast::TypeBoundList(it) => swap_sibling_in_list(it.bounds(), range, direction), 104 ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction),
90 _ => Some(replace_nodes(node, &match direction { 105 _ => Some(replace_nodes(node, &match direction {
91 Direction::Up => node.prev_sibling(), 106 Direction::Up => node.prev_sibling(),
92 Direction::Down => node.next_sibling(), 107 Direction::Down => node.next_sibling(),
@@ -96,19 +111,27 @@ fn move_in_direction(
96} 111}
97 112
98fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>( 113fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>(
114 node: &SyntaxNode,
99 list: I, 115 list: I,
100 range: TextRange, 116 range: TextRange,
101 direction: Direction, 117 direction: Direction,
102) -> Option<TextEdit> { 118) -> Option<TextEdit> {
103 let (l, r) = list 119 let list_lookup = list
104 .tuple_windows() 120 .tuple_windows()
105 .filter(|(l, r)| match direction { 121 .filter(|(l, r)| match direction {
106 Direction::Up => r.syntax().text_range().contains_range(range), 122 Direction::Up => r.syntax().text_range().contains_range(range),
107 Direction::Down => l.syntax().text_range().contains_range(range), 123 Direction::Down => l.syntax().text_range().contains_range(range),
108 }) 124 })
109 .next()?; 125 .next();
110 126
111 Some(replace_nodes(l.syntax(), r.syntax())) 127 if let Some((l, r)) = list_lookup {
128 Some(replace_nodes(l.syntax(), r.syntax()))
129 } else {
130 // Cursor is beyond any movable list item (for example, on curly brace in enum).
131 // It's not necessary, that parent of list is movable (arg list's parent is not, for example),
132 // and we have to continue tree traversal to find suitable node.
133 find_ancestors(SyntaxElement::Node(node.parent()?), direction, range)
134 }
112} 135}
113 136
114fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit { 137fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit {
@@ -120,6 +143,18 @@ fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit {
120 edit.finish() 143 edit.finish()
121} 144}
122 145
146fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
147 return tokens.max_by_key(priority);
148
149 fn priority(n: &SyntaxToken) -> usize {
150 match n.kind() {
151 SyntaxKind::IDENT | SyntaxKind::LIFETIME_IDENT => 2,
152 kind if kind.is_trivia() => 0,
153 _ => 1,
154 }
155 }
156}
157
123#[cfg(test)] 158#[cfg(test)]
124mod tests { 159mod tests {
125 use crate::fixture; 160 use crate::fixture;
@@ -264,6 +299,107 @@ fn main() {
264 "#]], 299 "#]],
265 Direction::Up, 300 Direction::Up,
266 ); 301 );
302 check(
303 r#"
304fn main() {
305 println!("Hello, world");
306
307 if true {
308 println!("Test");
309 }$0$0
310}
311 "#,
312 expect![[r#"
313fn main() {
314 if true {
315 println!("Test");
316 }
317
318 println!("Hello, world");
319}
320 "#]],
321 Direction::Up,
322 );
323 check(
324 r#"
325fn main() {
326 println!("Hello, world");
327
328 for i in 0..10 {
329 println!("Test");
330 }$0$0
331}
332 "#,
333 expect![[r#"
334fn main() {
335 for i in 0..10 {
336 println!("Test");
337 }
338
339 println!("Hello, world");
340}
341 "#]],
342 Direction::Up,
343 );
344 check(
345 r#"
346fn main() {
347 println!("Hello, world");
348
349 loop {
350 println!("Test");
351 }$0$0
352}
353 "#,
354 expect![[r#"
355fn main() {
356 loop {
357 println!("Test");
358 }
359
360 println!("Hello, world");
361}
362 "#]],
363 Direction::Up,
364 );
365 check(
366 r#"
367fn main() {
368 println!("Hello, world");
369
370 while true {
371 println!("Test");
372 }$0$0
373}
374 "#,
375 expect![[r#"
376fn main() {
377 while true {
378 println!("Test");
379 }
380
381 println!("Hello, world");
382}
383 "#]],
384 Direction::Up,
385 );
386 check(
387 r#"
388fn main() {
389 println!("Hello, world");
390
391 return 123;$0$0
392}
393 "#,
394 expect![[r#"
395fn main() {
396 return 123;
397
398 println!("Hello, world");
399}
400 "#]],
401 Direction::Up,
402 );
267 } 403 }
268 404
269 #[test] 405 #[test]
@@ -614,6 +750,115 @@ fn test() {
614 } 750 }
615 751
616 #[test] 752 #[test]
753 fn test_cursor_at_item_start() {
754 check(
755 r#"
756$0$0#[derive(Debug)]
757enum FooBar {
758 Foo,
759 Bar,
760}
761
762fn main() {}
763 "#,
764 expect![[r#"
765fn main() {}
766
767#[derive(Debug)]
768enum FooBar {
769 Foo,
770 Bar,
771}
772 "#]],
773 Direction::Down,
774 );
775 check(
776 r#"
777$0$0enum FooBar {
778 Foo,
779 Bar,
780}
781
782fn main() {}
783 "#,
784 expect![[r#"
785fn main() {}
786
787enum FooBar {
788 Foo,
789 Bar,
790}
791 "#]],
792 Direction::Down,
793 );
794 check(
795 r#"
796struct Test;
797
798trait SomeTrait {}
799
800$0$0impl SomeTrait for Test {}
801
802fn main() {}
803 "#,
804 expect![[r#"
805struct Test;
806
807impl SomeTrait for Test {}
808
809trait SomeTrait {}
810
811fn main() {}
812 "#]],
813 Direction::Up,
814 );
815 }
816
817 #[test]
818 fn test_cursor_at_item_end() {
819 check(
820 r#"
821enum FooBar {
822 Foo,
823 Bar,
824}$0$0
825
826fn main() {}
827 "#,
828 expect![[r#"
829fn main() {}
830
831enum FooBar {
832 Foo,
833 Bar,
834}
835 "#]],
836 Direction::Down,
837 );
838 check(
839 r#"
840struct Test;
841
842trait SomeTrait {}
843
844impl SomeTrait for Test {}$0$0
845
846fn main() {}
847 "#,
848 expect![[r#"
849struct Test;
850
851impl SomeTrait for Test {}
852
853trait SomeTrait {}
854
855fn main() {}
856 "#]],
857 Direction::Up,
858 );
859 }
860
861 #[test]
617 fn handles_empty_file() { 862 fn handles_empty_file() {
618 check(r#"$0$0"#, expect![[r#""#]], Direction::Up); 863 check(r#"$0$0"#, expect![[r#""#]], Direction::Up);
619 } 864 }
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs
index 22b0d6ecb..99365c8a7 100644
--- a/crates/ide/src/parent_module.rs
+++ b/crates/ide/src/parent_module.rs
@@ -18,6 +18,8 @@ use crate::NavigationTarget;
18// 18//
19// | VS Code | **Rust Analyzer: Locate parent module** 19// | VS Code | **Rust Analyzer: Locate parent module**
20// |=== 20// |===
21//
22// image::https://user-images.githubusercontent.com/48062697/113065580-04c21800-91b1-11eb-9a32-00086161c0bd.gif[]
21 23
22/// This returns `Vec` because a module may be included from several places. 24/// This returns `Vec` because a module may be included from several places.
23pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { 25pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> {
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 95ed8a045..11ca7ec6b 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -48,6 +48,8 @@ pub struct Declaration {
48// 48//
49// | VS Code | kbd:[Shift+Alt+F12] 49// | VS Code | kbd:[Shift+Alt+F12]
50// |=== 50// |===
51//
52// image::https://user-images.githubusercontent.com/48062697/113020670-b7c34f00-917a-11eb-8003-370ac5f2b3cb.gif[]
51pub(crate) fn find_all_refs( 53pub(crate) fn find_all_refs(
52 sema: &Semantics<RootDatabase>, 54 sema: &Semantics<RootDatabase>,
53 position: FilePosition, 55 position: FilePosition,
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 26d6dc9c9..2408a0181 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -70,6 +70,8 @@ pub(crate) fn prepare_rename(
70// 70//
71// | VS Code | kbd:[F2] 71// | VS Code | kbd:[F2]
72// |=== 72// |===
73//
74// image::https://user-images.githubusercontent.com/48062697/113065582-055aae80-91b1-11eb-8ade-2b58e6d81883.gif[]
73pub(crate) fn rename( 75pub(crate) fn rename(
74 db: &RootDatabase, 76 db: &RootDatabase,
75 position: FilePosition, 77 position: FilePosition,
@@ -307,7 +309,7 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe
307 hir::AssocItemContainer::Impl(impl_) => impl_, 309 hir::AssocItemContainer::Impl(impl_) => impl_,
308 }; 310 };
309 let first_param_ty = first_param.ty(); 311 let first_param_ty = first_param.ty();
310 let impl_ty = impl_.target_ty(sema.db); 312 let impl_ty = impl_.self_ty(sema.db);
311 let (ty, self_param) = if impl_ty.remove_ref().is_some() { 313 let (ty, self_param) = if impl_ty.remove_ref().is_some() {
312 // if the impl is a ref to the type we can just match the `&T` with self directly 314 // if the impl is a ref to the type we can just match the `&T` with self directly
313 (first_param_ty.clone(), "self") 315 (first_param_ty.clone(), "self")
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 7e4c5a078..3eb9e27ee 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -98,6 +98,7 @@ impl Runnable {
98// 98//
99// | VS Code | **Rust Analyzer: Run** 99// | VS Code | **Rust Analyzer: Run**
100// |=== 100// |===
101// image::https://user-images.githubusercontent.com/48062697/113065583-055aae80-91b1-11eb-958f-d67efcaf6a2f.gif[]
101pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 102pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
102 let sema = Semantics::new(db); 103 let sema = Semantics::new(db);
103 104
@@ -298,7 +299,7 @@ fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Op
298 // FIXME: this also looks very wrong 299 // FIXME: this also looks very wrong
299 if let Some(assoc_def) = assoc_def { 300 if let Some(assoc_def) = assoc_def {
300 if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) { 301 if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) {
301 let ty = imp.target_ty(sema.db); 302 let ty = imp.self_ty(sema.db);
302 if let Some(adt) = ty.as_adt() { 303 if let Some(adt) = ty.as_adt() {
303 let name = adt.name(sema.db); 304 let name = adt.name(sema.db);
304 let idx = path.rfind(':').map_or(0, |idx| idx + 1); 305 let idx = path.rfind(':').map_or(0, |idx| idx + 1);
diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs
index 137c38c0d..49fde1945 100644
--- a/crates/ide/src/status.rs
+++ b/crates/ide/src/status.rs
@@ -31,6 +31,7 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
31// 31//
32// | VS Code | **Rust Analyzer: Status** 32// | VS Code | **Rust Analyzer: Status**
33// |=== 33// |===
34// image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[]
34pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String { 35pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
35 let mut buf = String::new(); 36 let mut buf = String::new();
36 format_to!(buf, "{}\n", FileTextQuery.in_db(db).entries::<FilesStats>()); 37 format_to!(buf, "{}\n", FileTextQuery.in_db(db).entries::<FilesStats>());
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index e25b698e0..67a10766b 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -5,7 +5,7 @@ mod injector;
5 5
6mod highlight; 6mod highlight;
7mod format; 7mod format;
8mod macro_rules; 8mod macro_;
9mod inject; 9mod inject;
10 10
11mod html; 11mod html;
@@ -24,8 +24,8 @@ use syntax::{
24 24
25use crate::{ 25use crate::{
26 syntax_highlighting::{ 26 syntax_highlighting::{
27 format::highlight_format_string, highlights::Highlights, 27 format::highlight_format_string, highlights::Highlights, macro_::MacroHighlighter,
28 macro_rules::MacroRulesHighlighter, tags::Highlight, 28 tags::Highlight,
29 }, 29 },
30 FileId, HlMod, HlTag, 30 FileId, HlMod, HlTag,
31}; 31};
@@ -93,8 +93,8 @@ fn traverse(
93 let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); 93 let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
94 94
95 let mut current_macro_call: Option<ast::MacroCall> = None; 95 let mut current_macro_call: Option<ast::MacroCall> = None;
96 let mut current_macro_rules: Option<ast::MacroRules> = None; 96 let mut current_macro: Option<ast::Macro> = None;
97 let mut macro_rules_highlighter = MacroRulesHighlighter::default(); 97 let mut macro_highlighter = MacroHighlighter::default();
98 let mut inside_attribute = false; 98 let mut inside_attribute = false;
99 99
100 // Walk all nodes, keeping track of whether we are inside a macro or not. 100 // Walk all nodes, keeping track of whether we are inside a macro or not.
@@ -129,16 +129,16 @@ fn traverse(
129 _ => (), 129 _ => (),
130 } 130 }
131 131
132 match event.clone().map(|it| it.into_node().and_then(ast::MacroRules::cast)) { 132 match event.clone().map(|it| it.into_node().and_then(ast::Macro::cast)) {
133 WalkEvent::Enter(Some(mac)) => { 133 WalkEvent::Enter(Some(mac)) => {
134 macro_rules_highlighter.init(); 134 macro_highlighter.init();
135 current_macro_rules = Some(mac); 135 current_macro = Some(mac);
136 continue; 136 continue;
137 } 137 }
138 WalkEvent::Leave(Some(mac)) => { 138 WalkEvent::Leave(Some(mac)) => {
139 assert_eq!(current_macro_rules, Some(mac)); 139 assert_eq!(current_macro, Some(mac));
140 current_macro_rules = None; 140 current_macro = None;
141 macro_rules_highlighter = MacroRulesHighlighter::default(); 141 macro_highlighter = MacroHighlighter::default();
142 } 142 }
143 _ => (), 143 _ => (),
144 } 144 }
@@ -164,9 +164,9 @@ fn traverse(
164 164
165 let range = element.text_range(); 165 let range = element.text_range();
166 166
167 if current_macro_rules.is_some() { 167 if current_macro.is_some() {
168 if let Some(tok) = element.as_token() { 168 if let Some(tok) = element.as_token() {
169 macro_rules_highlighter.advance(tok); 169 macro_highlighter.advance(tok);
170 } 170 }
171 } 171 }
172 172
@@ -200,7 +200,7 @@ fn traverse(
200 } 200 }
201 } 201 }
202 202
203 if let Some(_) = macro_rules_highlighter.highlight(element_to_highlight.clone()) { 203 if let Some(_) = macro_highlighter.highlight(element_to_highlight.clone()) {
204 continue; 204 continue;
205 } 205 }
206 206
diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs
index e503abc93..5bbadb0f4 100644
--- a/crates/ide/src/syntax_highlighting/format.rs
+++ b/crates/ide/src/syntax_highlighting/format.rs
@@ -31,7 +31,7 @@ fn is_format_string(string: &ast::String) -> Option<()> {
31 let parent = string.syntax().parent()?; 31 let parent = string.syntax().parent()?;
32 32
33 let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?; 33 let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?;
34 if !matches!(name.text(), "format_args" | "format_args_nl") { 34 if !matches!(name.text().as_str(), "format_args" | "format_args_nl") {
35 return None; 35 return None;
36 } 36 }
37 37
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index b0cfdd8b7..5ccb84714 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -1,6 +1,6 @@
1//! Computes color for a single element. 1//! Computes color for a single element.
2 2
3use hir::{AsAssocItem, Semantics, VariantDef}; 3use hir::{AsAssocItem, AssocItemContainer, Semantics, VariantDef};
4use ide_db::{ 4use ide_db::{
5 defs::{Definition, NameClass, NameRefClass}, 5 defs::{Definition, NameClass, NameRefClass},
6 RootDatabase, SymbolKind, 6 RootDatabase, SymbolKind,
@@ -275,12 +275,24 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
275 hir::ModuleDef::Module(_) => HlTag::Symbol(SymbolKind::Module), 275 hir::ModuleDef::Module(_) => HlTag::Symbol(SymbolKind::Module),
276 hir::ModuleDef::Function(func) => { 276 hir::ModuleDef::Function(func) => {
277 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Function)); 277 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Function));
278 if func.as_assoc_item(db).is_some() { 278 if let Some(item) = func.as_assoc_item(db) {
279 h |= HlMod::Associated; 279 h |= HlMod::Associated;
280 if func.self_param(db).is_none() { 280 if func.self_param(db).is_none() {
281 h |= HlMod::Static 281 h |= HlMod::Static
282 } 282 }
283
284 match item.container(db) {
285 AssocItemContainer::Impl(i) => {
286 if i.trait_(db).is_some() {
287 h |= HlMod::Trait;
288 }
289 }
290 AssocItemContainer::Trait(_t) => {
291 h |= HlMod::Trait;
292 }
293 }
283 } 294 }
295
284 if func.is_unsafe(db) { 296 if func.is_unsafe(db) {
285 h |= HlMod::Unsafe; 297 h |= HlMod::Unsafe;
286 } 298 }
@@ -292,9 +304,20 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
292 hir::ModuleDef::Variant(_) => HlTag::Symbol(SymbolKind::Variant), 304 hir::ModuleDef::Variant(_) => HlTag::Symbol(SymbolKind::Variant),
293 hir::ModuleDef::Const(konst) => { 305 hir::ModuleDef::Const(konst) => {
294 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const)); 306 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const));
295 if konst.as_assoc_item(db).is_some() { 307 if let Some(item) = konst.as_assoc_item(db) {
296 h |= HlMod::Associated 308 h |= HlMod::Associated;
309 match item.container(db) {
310 AssocItemContainer::Impl(i) => {
311 if i.trait_(db).is_some() {
312 h |= HlMod::Trait;
313 }
314 }
315 AssocItemContainer::Trait(_t) => {
316 h |= HlMod::Trait;
317 }
318 }
297 } 319 }
320
298 return h; 321 return h;
299 } 322 }
300 hir::ModuleDef::Trait(_) => HlTag::Symbol(SymbolKind::Trait), 323 hir::ModuleDef::Trait(_) => HlTag::Symbol(SymbolKind::Trait),
@@ -362,6 +385,10 @@ fn highlight_method_call(
362 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) { 385 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) {
363 h |= HlMod::Unsafe; 386 h |= HlMod::Unsafe;
364 } 387 }
388 if func.as_assoc_item(sema.db).and_then(|it| it.containing_trait(sema.db)).is_some() {
389 h |= HlMod::Trait
390 }
391
365 if let Some(self_param) = func.self_param(sema.db) { 392 if let Some(self_param) = func.self_param(sema.db) {
366 match self_param.access(sema.db) { 393 match self_param.access(sema.db) {
367 hir::Access::Shared => (), 394 hir::Access::Shared => (),
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 38bf49348..b62d43256 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -11,7 +11,8 @@ use syntax::{
11}; 11};
12 12
13use crate::{ 13use crate::{
14 doc_links::extract_definitions_from_markdown, Analysis, HlMod, HlRange, HlTag, RootDatabase, 14 doc_links::{extract_definitions_from_markdown, resolve_doc_path_for_def},
15 Analysis, HlMod, HlRange, HlTag, RootDatabase,
15}; 16};
16 17
17use super::{highlights::Highlights, injector::Injector}; 18use super::{highlights::Highlights, injector::Injector};
@@ -109,8 +110,7 @@ fn doc_attributes<'node>(
109 ast::Impl(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::SelfType(def))), 110 ast::Impl(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::SelfType(def))),
110 ast::RecordField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), 111 ast::RecordField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))),
111 ast::TupleField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), 112 ast::TupleField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))),
112 ast::MacroRules(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))), 113 ast::Macro(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))),
113 // ast::MacroDef(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
114 // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), 114 // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
115 _ => return None 115 _ => return None
116 } 116 }
@@ -191,7 +191,7 @@ pub(super) fn doc_comment(
191 extract_definitions_from_markdown(line) 191 extract_definitions_from_markdown(line)
192 .into_iter() 192 .into_iter()
193 .filter_map(|(range, link, ns)| { 193 .filter_map(|(range, link, ns)| {
194 Some(range).zip(validate_intra_doc_link(sema.db, &def, &link, ns)) 194 Some(range).zip(resolve_doc_path_for_def(sema.db, def, &link, ns))
195 }) 195 })
196 .map(|(Range { start, end }, def)| { 196 .map(|(Range { start, end }, def)| {
197 ( 197 (
@@ -284,33 +284,6 @@ fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::Stri
284 } 284 }
285} 285}
286 286
287fn validate_intra_doc_link(
288 db: &RootDatabase,
289 def: &Definition,
290 link: &str,
291 ns: Option<hir::Namespace>,
292) -> Option<hir::ModuleDef> {
293 match def {
294 Definition::ModuleDef(def) => match def {
295 hir::ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns),
296 hir::ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns),
297 hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns),
298 hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns),
299 hir::ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns),
300 hir::ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns),
301 hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns),
302 hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns),
303 hir::ModuleDef::BuiltinType(_) => None,
304 },
305 Definition::Macro(it) => it.resolve_doc_path(db, &link, ns),
306 Definition::Field(it) => it.resolve_doc_path(db, &link, ns),
307 Definition::SelfType(_)
308 | Definition::Local(_)
309 | Definition::GenericParam(_)
310 | Definition::Label(_) => None,
311 }
312}
313
314fn module_def_to_hl_tag(def: hir::ModuleDef) -> HlTag { 287fn module_def_to_hl_tag(def: hir::ModuleDef) -> HlTag {
315 let symbol = match def { 288 let symbol = match def {
316 hir::ModuleDef::Module(_) => SymbolKind::Module, 289 hir::ModuleDef::Module(_) => SymbolKind::Module,
diff --git a/crates/ide/src/syntax_highlighting/macro_rules.rs b/crates/ide/src/syntax_highlighting/macro_.rs
index 44620e912..819704294 100644
--- a/crates/ide/src/syntax_highlighting/macro_rules.rs
+++ b/crates/ide/src/syntax_highlighting/macro_.rs
@@ -4,18 +4,18 @@ use syntax::{SyntaxElement, SyntaxKind, SyntaxToken, TextRange, T};
4use crate::{HlRange, HlTag}; 4use crate::{HlRange, HlTag};
5 5
6#[derive(Default)] 6#[derive(Default)]
7pub(super) struct MacroRulesHighlighter { 7pub(super) struct MacroHighlighter {
8 state: Option<MacroMatcherParseState>, 8 state: Option<MacroMatcherParseState>,
9} 9}
10 10
11impl MacroRulesHighlighter { 11impl MacroHighlighter {
12 pub(super) fn init(&mut self) { 12 pub(super) fn init(&mut self) {
13 self.state = Some(MacroMatcherParseState::default()); 13 self.state = Some(MacroMatcherParseState::default());
14 } 14 }
15 15
16 pub(super) fn advance(&mut self, token: &SyntaxToken) { 16 pub(super) fn advance(&mut self, token: &SyntaxToken) {
17 if let Some(state) = self.state.as_mut() { 17 if let Some(state) = self.state.as_mut() {
18 update_macro_rules_state(state, token); 18 update_macro_state(state, token);
19 } 19 }
20 } 20 }
21 21
@@ -74,9 +74,9 @@ impl RuleState {
74 } 74 }
75} 75}
76 76
77fn update_macro_rules_state(state: &mut MacroMatcherParseState, tok: &SyntaxToken) { 77fn update_macro_state(state: &mut MacroMatcherParseState, tok: &SyntaxToken) {
78 if !state.in_invoc_body { 78 if !state.in_invoc_body {
79 if tok.kind() == T!['{'] { 79 if tok.kind() == T!['{'] || tok.kind() == T!['('] {
80 state.in_invoc_body = true; 80 state.in_invoc_body = true;
81 } 81 }
82 return; 82 return;
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index 93db79b89..1cec991aa 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -58,6 +58,8 @@ pub enum HlMod {
58 Associated, 58 Associated,
59 /// Used for intra doc links in doc injection. 59 /// Used for intra doc links in doc injection.
60 IntraDocLink, 60 IntraDocLink,
61 /// Used for items in traits and trait impls.
62 Trait,
61 63
62 /// Keep this last! 64 /// Keep this last!
63 Unsafe, 65 Unsafe,
@@ -158,6 +160,7 @@ impl HlMod {
158 HlMod::Callable, 160 HlMod::Callable,
159 HlMod::Static, 161 HlMod::Static,
160 HlMod::Associated, 162 HlMod::Associated,
163 HlMod::Trait,
161 HlMod::Unsafe, 164 HlMod::Unsafe,
162 ]; 165 ];
163 166
@@ -174,6 +177,7 @@ impl HlMod {
174 HlMod::IntraDocLink => "intra_doc_link", 177 HlMod::IntraDocLink => "intra_doc_link",
175 HlMod::Mutable => "mutable", 178 HlMod::Mutable => "mutable",
176 HlMod::Static => "static", 179 HlMod::Static => "static",
180 HlMod::Trait => "trait",
177 HlMod::Unsafe => "unsafe", 181 HlMod::Unsafe => "unsafe",
178 } 182 }
179 } 183 }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
index 4635ea927..8cde3906c 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
@@ -47,12 +47,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
47<span class="brace">}</span> 47<span class="brace">}</span>
48 48
49<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="brace">{</span> 49<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="brace">{</span>
50 <span class="keyword">fn</span> <span class="function declaration static associated">t_is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 50 <span class="keyword">fn</span> <span class="function declaration static associated trait">t_is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
51 <span class="keyword">fn</span> <span class="function declaration associated">t_is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 51 <span class="keyword">fn</span> <span class="function declaration associated trait">t_is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
52<span class="brace">}</span> 52<span class="brace">}</span>
53 53
54<span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="brace">{</span> 54<span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="brace">{</span>
55 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 55 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated trait">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
56 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 56 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated trait">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
57<span class="brace">}</span> 57<span class="brace">}</span>
58 </code></pre> \ No newline at end of file 58 </code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index 9215ddd9e..7c6694a27 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -42,7 +42,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
42<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> 42<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
43 <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span> 43 <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span>
44 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="brace">{</span> 44 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="brace">{</span>
45 <span class="keyword">fn</span> <span class="function declaration static associated">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> 45 <span class="keyword">fn</span> <span class="function declaration static associated trait">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
46 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span> 46 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span>
47 <span class="brace">}</span> 47 <span class="brace">}</span>
48 <span class="brace">}</span><span class="string_literal">"#</span> 48 <span class="brace">}</span><span class="string_literal">"#</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 6a6555208..72910421d 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -62,11 +62,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
62<span class="brace">}</span> 62<span class="brace">}</span>
63 63
64<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span> 64<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span>
65 <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span><span class="semicolon">;</span> 65 <span class="keyword">fn</span> <span class="function declaration associated trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
66<span class="brace">}</span> 66<span class="brace">}</span>
67 67
68<span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="brace">{</span> 68<span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="brace">{</span>
69 <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 69 <span class="keyword">fn</span> <span class="function declaration associated trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
70<span class="brace">}</span> 70<span class="brace">}</span>
71 71
72<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> 72<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
@@ -96,6 +96,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
96 <span class="keyword">let</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">_a</span> <span class="brace">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="semicolon">;</span> 96 <span class="keyword">let</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">_a</span> <span class="brace">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="semicolon">;</span>
97 97
98 <span class="comment">// unsafe auto ref of packed field</span> 98 <span class="comment">// unsafe auto ref of packed field</span>
99 <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="operator">.</span><span class="function associated unsafe">calls_autoref</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> 99 <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="operator">.</span><span class="function associated trait unsafe">calls_autoref</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
100 <span class="brace">}</span> 100 <span class="brace">}</span>
101<span class="brace">}</span></code></pre> \ No newline at end of file 101<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 8b2dd3b70..973173254 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -41,7 +41,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
41<span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span><span class="brace">}</span> 41<span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span><span class="brace">}</span>
42 42
43<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span> 43<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span>
44<span class="keyword">macro</span> <span class="unresolved_reference declaration">Copy</span> <span class="brace">{</span><span class="brace">}</span> 44<span class="keyword">macro</span> <span class="macro declaration">Copy</span> <span class="brace">{</span><span class="brace">}</span>
45 45
46<span class="comment">// Needed for function consuming vs normal</span> 46<span class="comment">// Needed for function consuming vs normal</span>
47<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="brace">{</span> 47<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="brace">{</span>
@@ -67,11 +67,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
67<span class="brace">}</span> 67<span class="brace">}</span>
68 68
69<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="brace">{</span> 69<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="brace">{</span>
70 <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="semicolon">;</span> 70 <span class="keyword">fn</span> <span class="function declaration associated trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="semicolon">;</span>
71<span class="brace">}</span> 71<span class="brace">}</span>
72 72
73<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="brace">{</span> 73<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="brace">{</span>
74 <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span> 74 <span class="keyword">fn</span> <span class="function declaration associated trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
75 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 75 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
76 <span class="brace">}</span> 76 <span class="brace">}</span>
77<span class="brace">}</span> 77<span class="brace">}</span>
@@ -158,6 +158,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
158 <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="colon">:</span>ty<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="parenthesis">)</span> 158 <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="colon">:</span>ty<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="parenthesis">)</span>
159<span class="brace">}</span> 159<span class="brace">}</span>
160 160
161<span class="keyword">macro</span> <span class="macro declaration">with_args</span><span class="parenthesis">(</span><span class="punctuation">$</span>i<span class="colon">:</span>ident<span class="parenthesis">)</span> <span class="brace">{</span>
162 <span class="punctuation">$</span>i
163<span class="brace">}</span>
164
165<span class="keyword">macro</span> <span class="macro declaration">without_args</span> <span class="brace">{</span>
166 <span class="parenthesis">(</span><span class="punctuation">$</span>i<span class="colon">:</span>ident<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
167 <span class="punctuation">$</span>i
168 <span class="brace">}</span>
169<span class="brace">}</span>
170
161<span class="comment">// comment</span> 171<span class="comment">// comment</span>
162<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> 172<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
163 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, {}!"</span><span class="comma">,</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span> 173 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, {}!"</span><span class="comma">,</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 7b2922b0d..369ae0972 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -129,6 +129,16 @@ macro_rules! keyword_frag {
129 ($type:ty) => ($type) 129 ($type:ty) => ($type)
130} 130}
131 131
132macro with_args($i:ident) {
133 $i
134}
135
136macro without_args {
137 ($i:ident) => {
138 $i
139 }
140}
141
132// comment 142// comment
133fn main() { 143fn main() {
134 println!("Hello, {}!", 92); 144 println!("Hello, {}!", 92);
diff --git a/crates/ide/src/syntax_tree.rs b/crates/ide/src/syntax_tree.rs
index 8979de528..633878d1c 100644
--- a/crates/ide/src/syntax_tree.rs
+++ b/crates/ide/src/syntax_tree.rs
@@ -14,6 +14,7 @@ use syntax::{
14// 14//
15// | VS Code | **Rust Analyzer: Show Syntax Tree** 15// | VS Code | **Rust Analyzer: Show Syntax Tree**
16// |=== 16// |===
17// image::https://user-images.githubusercontent.com/48062697/113065586-068bdb80-91b1-11eb-9507-fee67f9f45a0.gif[]
17pub(crate) fn syntax_tree( 18pub(crate) fn syntax_tree(
18 db: &RootDatabase, 19 db: &RootDatabase,
19 file_id: FileId, 20 file_id: FileId,
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs
index 978c479de..9144681bf 100644
--- a/crates/ide/src/typing/on_enter.rs
+++ b/crates/ide/src/typing/on_enter.rs
@@ -32,6 +32,8 @@ use text_edit::TextEdit;
32// "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust" 32// "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust"
33// } 33// }
34// ---- 34// ----
35//
36// image::https://user-images.githubusercontent.com/48062697/113065578-04c21800-91b1-11eb-82b8-22b8c481e645.gif[]
35pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> { 37pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> {
36 let parse = db.parse(position.file_id); 38 let parse = db.parse(position.file_id);
37 let file = parse.tree(); 39 let file = parse.tree();
diff --git a/crates/ide/src/view_hir.rs b/crates/ide/src/view_hir.rs
index f8f3fae3d..7312afe53 100644
--- a/crates/ide/src/view_hir.rs
+++ b/crates/ide/src/view_hir.rs
@@ -10,6 +10,7 @@ use syntax::{algo::find_node_at_offset, ast, AstNode};
10// 10//
11// | VS Code | **Rust Analyzer: View Hir** 11// | VS Code | **Rust Analyzer: View Hir**
12// |=== 12// |===
13// image::https://user-images.githubusercontent.com/48062697/113065588-068bdb80-91b1-11eb-9a78-0b4ef1e972fb.gif[]
13pub(crate) fn view_hir(db: &RootDatabase, position: FilePosition) -> String { 14pub(crate) fn view_hir(db: &RootDatabase, position: FilePosition) -> String {
14 body_hir(db, position).unwrap_or_else(|| "Not inside a function body".to_string()) 15 body_hir(db, position).unwrap_or_else(|| "Not inside a function body".to_string())
15} 16}
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
index 7019039b9..5ccd7f7a2 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -61,6 +61,8 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
61// - `plain`: This setting does not impose any restrictions in imports. 61// - `plain`: This setting does not impose any restrictions in imports.
62// 62//
63// In `VS Code` the configuration for this is `rust-analyzer.assist.importPrefix`. 63// In `VS Code` the configuration for this is `rust-analyzer.assist.importPrefix`.
64//
65// image::https://user-images.githubusercontent.com/48062697/113020673-b85be580-917a-11eb-9022-59585f35d4f8.gif[]
64 66
65// Assist: auto_import 67// Assist: auto_import
66// 68//
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
index 596c536a7..a8d6355bd 100644
--- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -195,7 +195,7 @@ fn extract_struct_def(
195 195
196fn update_variant(rewriter: &mut SyntaxRewriter, variant: &ast::Variant) -> Option<()> { 196fn update_variant(rewriter: &mut SyntaxRewriter, variant: &ast::Variant) -> Option<()> {
197 let name = variant.name()?; 197 let name = variant.name()?;
198 let tuple_field = make::tuple_field(None, make::ty(name.text())); 198 let tuple_field = make::tuple_field(None, make::ty(&name.text()));
199 let replacement = make::variant( 199 let replacement = make::variant(
200 name, 200 name,
201 Some(ast::FieldList::TupleFieldList(make::tuple_field_list(iter::once(tuple_field)))), 201 Some(ast::FieldList::TupleFieldList(make::tuple_field_list(iter::once(tuple_field)))),
diff --git a/crates/ide_assists/src/handlers/generate_default_from_new.rs b/crates/ide_assists/src/handlers/generate_default_from_new.rs
index 81c54ba3e..dc14552d6 100644
--- a/crates/ide_assists/src/handlers/generate_default_from_new.rs
+++ b/crates/ide_assists/src/handlers/generate_default_from_new.rs
@@ -92,7 +92,7 @@ fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool {
92 None => return false, 92 None => return false,
93 }; 93 };
94 94
95 let ty = impl_def.target_ty(db); 95 let ty = impl_def.self_ty(db);
96 let krate = impl_def.module(db).krate(); 96 let krate = impl_def.module(db).krate();
97 let default = FamousDefs(&ctx.sema, Some(krate)).core_default_Default(); 97 let default = FamousDefs(&ctx.sema, Some(krate)).core_default_Default();
98 let default_trait = match default { 98 let default_trait = match default {
diff --git a/crates/ide_assists/src/handlers/generate_enum_is_method.rs b/crates/ide_assists/src/handlers/generate_enum_is_method.rs
index 7e181a480..a9f71a703 100644
--- a/crates/ide_assists/src/handlers/generate_enum_is_method.rs
+++ b/crates/ide_assists/src/handlers/generate_enum_is_method.rs
@@ -44,7 +44,7 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) ->
44 }; 44 };
45 45
46 let enum_lowercase_name = to_lower_snake_case(&parent_enum.name()?.to_string()); 46 let enum_lowercase_name = to_lower_snake_case(&parent_enum.name()?.to_string());
47 let fn_name = format!("is_{}", &to_lower_snake_case(variant_name.text())); 47 let fn_name = format!("is_{}", &to_lower_snake_case(&variant_name.text()));
48 48
49 // Return early if we've found an existing new fn 49 // Return early if we've found an existing new fn
50 let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; 50 let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?;
diff --git a/crates/ide_assists/src/handlers/generate_enum_projection_method.rs b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs
index 871bcab50..e2f572ba3 100644
--- a/crates/ide_assists/src/handlers/generate_enum_projection_method.rs
+++ b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs
@@ -132,7 +132,8 @@ fn generate_enum_projection_method(
132 ast::StructKind::Unit => return None, 132 ast::StructKind::Unit => return None,
133 }; 133 };
134 134
135 let fn_name = format!("{}_{}", props.fn_name_prefix, &to_lower_snake_case(variant_name.text())); 135 let fn_name =
136 format!("{}_{}", props.fn_name_prefix, &to_lower_snake_case(&variant_name.text()));
136 137
137 // Return early if we've found an existing new fn 138 // Return early if we've found an existing new fn
138 let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; 139 let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?;
diff --git a/crates/ide_assists/src/handlers/generate_impl.rs b/crates/ide_assists/src/handlers/generate_impl.rs
index a8e3c4fc2..fd2e250bc 100644
--- a/crates/ide_assists/src/handlers/generate_impl.rs
+++ b/crates/ide_assists/src/handlers/generate_impl.rs
@@ -72,6 +72,17 @@ mod tests {
72 check_assist( 72 check_assist(
73 generate_impl, 73 generate_impl,
74 r#" 74 r#"
75 struct MyOwnArray<T, const S: usize> {}$0"#,
76 r#"
77 struct MyOwnArray<T, const S: usize> {}
78
79 impl<T, const S: usize> MyOwnArray<T, S> {
80 $0
81 }"#,
82 );
83 check_assist(
84 generate_impl,
85 r#"
75 #[cfg(feature = "foo")] 86 #[cfg(feature = "foo")]
76 struct Foo<'a, T: Foo<'a>> {$0}"#, 87 struct Foo<'a, T: Foo<'a>> {$0}"#,
77 r#" 88 r#"
@@ -114,11 +125,11 @@ mod tests {
114 check_assist( 125 check_assist(
115 generate_impl, 126 generate_impl,
116 r#" 127 r#"
117 struct Defaulted<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String> {}$0"#, 128 struct Defaulted<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String, const S: usize> {}$0"#,
118 r#" 129 r#"
119 struct Defaulted<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String> {} 130 struct Defaulted<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String, const S: usize> {}
120 131
121 impl<'a, 'b: 'a, T: Debug + Clone + 'a + 'b> Defaulted<'a, 'b, T> { 132 impl<'a, 'b: 'a, T: Debug + Clone + 'a + 'b, const S: usize> Defaulted<'a, 'b, T, S> {
122 $0 133 $0
123 }"#, 134 }"#,
124 ); 135 );
diff --git a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
index b8834d283..910010a04 100644
--- a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
+++ b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
@@ -91,7 +91,7 @@ fn get_impl_method(
91 91
92 let scope = ctx.sema.scope(impl_.syntax()); 92 let scope = ctx.sema.scope(impl_.syntax());
93 let krate = impl_def.module(db).krate(); 93 let krate = impl_def.module(db).krate();
94 let ty = impl_def.target_ty(db); 94 let ty = impl_def.self_ty(db);
95 let traits_in_scope = scope.traits_in_scope(); 95 let traits_in_scope = scope.traits_in_scope();
96 ty.iterate_method_candidates(db, krate, &traits_in_scope, Some(fn_name), |_, func| Some(func)) 96 ty.iterate_method_candidates(db, krate, &traits_in_scope, Some(fn_name), |_, func| Some(func))
97} 97}
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
index 4f0ef52ca..f872d20c8 100644
--- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -165,7 +165,7 @@ fn impl_def_from_trait(
165 } 165 }
166 let impl_def = make::impl_trait( 166 let impl_def = make::impl_trait(
167 trait_path.clone(), 167 trait_path.clone(),
168 make::path_unqualified(make::path_segment(make::name_ref(annotated_name.text()))), 168 make::path_unqualified(make::path_segment(make::name_ref(&annotated_name.text()))),
169 ); 169 );
170 let (impl_def, first_assoc_item) = 170 let (impl_def, first_assoc_item) =
171 add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope); 171 add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope);
@@ -178,12 +178,13 @@ fn update_attribute(
178 trait_name: &ast::NameRef, 178 trait_name: &ast::NameRef,
179 attr: &ast::Attr, 179 attr: &ast::Attr,
180) { 180) {
181 let trait_name = trait_name.text();
181 let new_attr_input = input 182 let new_attr_input = input
182 .syntax() 183 .syntax()
183 .descendants_with_tokens() 184 .descendants_with_tokens()
184 .filter(|t| t.kind() == IDENT) 185 .filter(|t| t.kind() == IDENT)
185 .filter_map(|t| t.into_token().map(|t| t.text().to_string())) 186 .filter_map(|t| t.into_token().map(|t| t.text().to_string()))
186 .filter(|t| t != trait_name.text()) 187 .filter(|t| t != &trait_name)
187 .collect::<Vec<_>>(); 188 .collect::<Vec<_>>();
188 let has_more_derives = !new_attr_input.is_empty(); 189 let has_more_derives = !new_attr_input.is_empty();
189 190
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index 62f959082..d67524937 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -246,7 +246,7 @@ fn invert_special_case(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Opti
246 let method = mce.name_ref()?; 246 let method = mce.name_ref()?;
247 let arg_list = mce.arg_list()?; 247 let arg_list = mce.arg_list()?;
248 248
249 let method = match method.text() { 249 let method = match method.text().as_str() {
250 "is_some" => "is_none", 250 "is_some" => "is_none",
251 "is_none" => "is_some", 251 "is_none" => "is_some",
252 "is_ok" => "is_err", 252 "is_ok" => "is_err",
@@ -338,11 +338,11 @@ pub(crate) fn find_struct_impl(
338 // (we currently use the wrong type parameter) 338 // (we currently use the wrong type parameter)
339 // also we wouldn't want to use e.g. `impl S<u32>` 339 // also we wouldn't want to use e.g. `impl S<u32>`
340 340
341 let same_ty = match blk.target_ty(db).as_adt() { 341 let same_ty = match blk.self_ty(db).as_adt() {
342 Some(def) => def == struct_def, 342 Some(def) => def == struct_def,
343 None => false, 343 None => false,
344 }; 344 };
345 let not_trait_impl = blk.target_trait(db).is_none(); 345 let not_trait_impl = blk.trait_(db).is_none();
346 346
347 if !(same_ty && not_trait_impl) { 347 if !(same_ty && not_trait_impl) {
348 None 348 None
@@ -434,7 +434,8 @@ fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str
434 } 434 }
435 buf 435 buf
436 }); 436 });
437 let generics = lifetimes.chain(type_params).format(", "); 437 let const_params = generic_params.const_params().map(|t| t.syntax().to_string());
438 let generics = lifetimes.chain(type_params).chain(const_params).format(", ");
438 format_to!(buf, "<{}>", generics); 439 format_to!(buf, "<{}>", generics);
439 } 440 }
440 buf.push(' '); 441 buf.push(' ');
@@ -442,7 +443,7 @@ fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str
442 buf.push_str(trait_text); 443 buf.push_str(trait_text);
443 buf.push_str(" for "); 444 buf.push_str(" for ");
444 } 445 }
445 buf.push_str(adt.name().unwrap().text()); 446 buf.push_str(&adt.name().unwrap().text());
446 if let Some(generic_params) = generic_params { 447 if let Some(generic_params) = generic_params {
447 let lifetime_params = generic_params 448 let lifetime_params = generic_params
448 .lifetime_params() 449 .lifetime_params()
@@ -452,7 +453,11 @@ fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str
452 .type_params() 453 .type_params()
453 .filter_map(|it| it.name()) 454 .filter_map(|it| it.name())
454 .map(|it| SmolStr::from(it.text())); 455 .map(|it| SmolStr::from(it.text()));
455 format_to!(buf, "<{}>", lifetime_params.chain(type_params).format(", ")) 456 let const_params = generic_params
457 .const_params()
458 .filter_map(|it| it.name())
459 .map(|it| SmolStr::from(it.text()));
460 format_to!(buf, "<{}>", lifetime_params.chain(type_params).chain(const_params).format(", "))
456 } 461 }
457 462
458 match adt.where_clause() { 463 match adt.where_clause() {
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index 6d572a836..e2994eed4 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -220,7 +220,7 @@ fn complete_enum_variants(
220 }; 220 };
221 221
222 if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) { 222 if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) {
223 if impl_.target_ty(ctx.db) == *ty { 223 if impl_.self_ty(ctx.db) == *ty {
224 for &variant in &variants { 224 for &variant in &variants {
225 let self_path = hir::ModPath::from_segments( 225 let self_path = hir::ModPath::from_segments(
226 hir::PathKind::Plain, 226 hir::PathKind::Plain,
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index b06498e6d..808d7ff7e 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -40,7 +40,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
40 _ => false, 40 _ => false,
41 }, 41 },
42 hir::ScopeDef::MacroDef(_) => true, 42 hir::ScopeDef::MacroDef(_) => true,
43 hir::ScopeDef::ImplSelfType(impl_) => match impl_.target_ty(ctx.db).as_adt() { 43 hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() {
44 Some(hir::Adt::Struct(strukt)) => { 44 Some(hir::Adt::Struct(strukt)) => {
45 acc.add_struct_pat(ctx, strukt, Some(name.clone())); 45 acc.add_struct_pat(ctx, strukt, Some(name.clone()));
46 true 46 true
diff --git a/crates/ide_completion/src/completions/postfix/format_like.rs b/crates/ide_completion/src/completions/postfix/format_like.rs
index 3f1c6730b..e86ffa8f8 100644
--- a/crates/ide_completion/src/completions/postfix/format_like.rs
+++ b/crates/ide_completion/src/completions/postfix/format_like.rs
@@ -13,6 +13,8 @@
13// + `logi` -> `log::info!(...)` 13// + `logi` -> `log::info!(...)`
14// + `logw` -> `log::warn!(...)` 14// + `logw` -> `log::warn!(...)`
15// + `loge` -> `log::error!(...)` 15// + `loge` -> `log::error!(...)`
16//
17// image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[]
16 18
17use ide_db::helpers::SnippetCap; 19use ide_db::helpers::SnippetCap;
18use syntax::ast::{self, AstToken}; 20use syntax::ast::{self, AstToken};
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index 105ff6013..1891eb5b3 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -117,7 +117,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
117 if let Some(krate) = ctx.krate { 117 if let Some(krate) = ctx.krate {
118 let ty = match resolution { 118 let ty = match resolution {
119 PathResolution::TypeParam(param) => param.ty(ctx.db), 119 PathResolution::TypeParam(param) => param.ty(ctx.db),
120 PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db), 120 PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
121 _ => return, 121 _ => return,
122 }; 122 };
123 123
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 5ac1cb48d..831d543bb 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -81,6 +81,8 @@ pub use crate::{
81// And the auto import completions, enabled with the `rust-analyzer.completion.autoimport.enable` setting and the corresponding LSP client capabilities. 81// And the auto import completions, enabled with the `rust-analyzer.completion.autoimport.enable` setting and the corresponding LSP client capabilities.
82// Those are the additional completion options with automatic `use` import and options from all project importable items, 82// Those are the additional completion options with automatic `use` import and options from all project importable items,
83// fuzzy matched agains the completion imput. 83// fuzzy matched agains the completion imput.
84//
85// image::https://user-images.githubusercontent.com/48062697/113020667-b72ab880-917a-11eb-8778-716cf26a0eb3.gif[]
84 86
85/// Main entry point for completion. We run completion as a two-phase process. 87/// Main entry point for completion. We run completion as a two-phase process.
86/// 88///
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs
index 047a9b6bc..111e9325a 100644
--- a/crates/ide_db/src/apply_change.rs
+++ b/crates/ide_db/src/apply_change.rs
@@ -101,6 +101,7 @@ impl RootDatabase {
101 // 101 //
102 // | VS Code | **Rust Analyzer: Memory Usage (Clears Database)** 102 // | VS Code | **Rust Analyzer: Memory Usage (Clears Database)**
103 // |=== 103 // |===
104 // image::https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif[]
104 pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> { 105 pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> {
105 let mut acc: Vec<(String, Bytes)> = vec![]; 106 let mut acc: Vec<(String, Bytes)> = vec![];
106 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); 107 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs
index 75ab3eb6e..281a081a3 100644
--- a/crates/ide_db/src/call_info/tests.rs
+++ b/crates/ide_db/src/call_info/tests.rs
@@ -1,8 +1,9 @@
1use crate::RootDatabase;
2use base_db::{fixture::ChangeFixture, FilePosition}; 1use base_db::{fixture::ChangeFixture, FilePosition};
3use expect_test::{expect, Expect}; 2use expect_test::{expect, Expect};
4use test_utils::RangeOrOffset; 3use test_utils::RangeOrOffset;
5 4
5use crate::RootDatabase;
6
6/// Creates analysis from a multi-file fixture, returns positions marked with $0. 7/// Creates analysis from a multi-file fixture, returns positions marked with $0.
7pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { 8pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
8 let change_fixture = ChangeFixture::parse(ra_fixture); 9 let change_fixture = ChangeFixture::parse(ra_fixture);
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index 75167ff39..de0dc2a40 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -227,7 +227,7 @@ impl NameClass {
227 let def: hir::TypeAlias = sema.to_def(&it)?; 227 let def: hir::TypeAlias = sema.to_def(&it)?;
228 Some(NameClass::Definition(Definition::ModuleDef(def.into()))) 228 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
229 }, 229 },
230 ast::MacroRules(it) => { 230 ast::Macro(it) => {
231 let def = sema.to_def(&it)?; 231 let def = sema.to_def(&it)?;
232 Some(NameClass::Definition(Definition::Macro(def))) 232 Some(NameClass::Definition(Definition::Macro(def)))
233 }, 233 },
@@ -330,25 +330,30 @@ impl NameRefClass {
330 } 330 }
331 } 331 }
332 332
333 if ast::AssocTypeArg::cast(parent.clone()).is_some() { 333 if let Some(assoc_type_arg) = ast::AssocTypeArg::cast(parent.clone()) {
334 // `Trait<Assoc = Ty>` 334 if assoc_type_arg.name_ref().as_ref() == Some(name_ref) {
335 // ^^^^^ 335 // `Trait<Assoc = Ty>`
336 let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; 336 // ^^^^^
337 let resolved = sema.resolve_path(&path)?; 337 let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
338 if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved { 338 let resolved = sema.resolve_path(&path)?;
339 if let Some(ty) = tr 339 if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
340 .items(sema.db) 340 // FIXME: resolve in supertraits
341 .iter() 341 if let Some(ty) = tr
342 .filter_map(|assoc| match assoc { 342 .items(sema.db)
343 hir::AssocItem::TypeAlias(it) => Some(*it), 343 .iter()
344 _ => None, 344 .filter_map(|assoc| match assoc {
345 }) 345 hir::AssocItem::TypeAlias(it) => Some(*it),
346 .find(|alias| &alias.name(sema.db).to_string() == name_ref.text()) 346 _ => None,
347 { 347 })
348 return Some(NameRefClass::Definition(Definition::ModuleDef( 348 .find(|alias| &alias.name(sema.db).to_string() == &name_ref.text())
349 ModuleDef::TypeAlias(ty), 349 {
350 ))); 350 return Some(NameRefClass::Definition(Definition::ModuleDef(
351 ModuleDef::TypeAlias(ty),
352 )));
353 }
351 } 354 }
355
356 return None;
352 } 357 }
353 } 358 }
354 359
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index 1881c746f..8ce648367 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -288,7 +288,7 @@ fn path_applicable_imports(
288 import_for_item( 288 import_for_item(
289 sema.db, 289 sema.db,
290 mod_path, 290 mod_path,
291 unresolved_first_segment, 291 &unresolved_first_segment,
292 &unresolved_qualifier, 292 &unresolved_qualifier,
293 item, 293 item,
294 ) 294 )
@@ -361,7 +361,7 @@ fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> {
361 Some(assoc_item) => match assoc_item.container(db) { 361 Some(assoc_item) => match assoc_item.container(db) {
362 AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)), 362 AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
363 AssocItemContainer::Impl(impl_) => { 363 AssocItemContainer::Impl(impl_) => {
364 ItemInNs::from(ModuleDef::from(impl_.target_ty(db).as_adt()?)) 364 ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?))
365 } 365 }
366 }, 366 },
367 None => item, 367 None => item,
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index 20c195f82..be3a22725 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -1,7 +1,6 @@
1//! Handle syntactic aspects of inserting a new `use`. 1//! Handle syntactic aspects of inserting a new `use`.
2use std::{cmp::Ordering, iter::successors}; 2use std::{cmp::Ordering, iter::successors};
3 3
4use crate::RootDatabase;
5use hir::Semantics; 4use hir::Semantics;
6use itertools::{EitherOrBoth, Itertools}; 5use itertools::{EitherOrBoth, Itertools};
7use syntax::{ 6use syntax::{
@@ -14,6 +13,8 @@ use syntax::{
14 AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, 13 AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
15}; 14};
16 15
16use crate::RootDatabase;
17
17pub use hir::PrefixKind; 18pub use hir::PrefixKind;
18 19
19#[derive(Clone, Copy, Debug, PartialEq, Eq)] 20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -509,7 +510,7 @@ impl ImportGroup {
509 PathSegmentKind::SelfKw => ImportGroup::ThisModule, 510 PathSegmentKind::SelfKw => ImportGroup::ThisModule,
510 PathSegmentKind::SuperKw => ImportGroup::SuperModule, 511 PathSegmentKind::SuperKw => ImportGroup::SuperModule,
511 PathSegmentKind::CrateKw => ImportGroup::ThisCrate, 512 PathSegmentKind::CrateKw => ImportGroup::ThisCrate,
512 PathSegmentKind::Name(name) => match name.text() { 513 PathSegmentKind::Name(name) => match name.text().as_str() {
513 "std" => ImportGroup::Std, 514 "std" => ImportGroup::Std,
514 "core" => ImportGroup::Std, 515 "core" => ImportGroup::Std,
515 _ => ImportGroup::ExternCrate, 516 _ => ImportGroup::ExternCrate,
diff --git a/crates/ide_db/src/label.rs b/crates/ide_db/src/label.rs
index c0e89e72f..1f1e715c9 100644
--- a/crates/ide_db/src/label.rs
+++ b/crates/ide_db/src/label.rs
@@ -1,4 +1,4 @@
1//! See `Label` 1//! See [`Label`]
2use std::fmt; 2use std::fmt;
3 3
4/// A type to specify UI label, like an entry in the list of assists. Enforces 4/// A type to specify UI label, like an entry in the list of assists. Enforces
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index 3634b2b26..b55e3851e 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -12,9 +12,8 @@ use once_cell::unsync::Lazy;
12use rustc_hash::FxHashMap; 12use rustc_hash::FxHashMap;
13use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; 13use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
14 14
15use crate::defs::NameClass;
16use crate::{ 15use crate::{
17 defs::{Definition, NameRefClass}, 16 defs::{Definition, NameClass, NameRefClass},
18 RootDatabase, 17 RootDatabase,
19}; 18};
20 19
diff --git a/crates/ide_db/src/source_change.rs b/crates/ide_db/src/source_change.rs
index b36455d49..846530f78 100644
--- a/crates/ide_db/src/source_change.rs
+++ b/crates/ide_db/src/source_change.rs
@@ -37,6 +37,8 @@ impl SourceChange {
37 } 37 }
38 } 38 }
39 39
40 /// Inserts a [`TextEdit`] for the given [`FileId`]. This properly handles merging existing
41 /// edits for a file if some already exist.
40 pub fn insert_source_edit(&mut self, file_id: FileId, edit: TextEdit) { 42 pub fn insert_source_edit(&mut self, file_id: FileId, edit: TextEdit) {
41 match self.source_file_edits.entry(file_id) { 43 match self.source_file_edits.entry(file_id) {
42 Entry::Occupied(mut entry) => { 44 Entry::Occupied(mut entry) => {
diff --git a/crates/ide_db/src/symbol_index.rs b/crates/ide_db/src/symbol_index.rs
index 35e382b5c..da427d686 100644
--- a/crates/ide_db/src/symbol_index.rs
+++ b/crates/ide_db/src/symbol_index.rs
@@ -438,7 +438,7 @@ fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
438 ast::TypeAlias(it) => decl(it), 438 ast::TypeAlias(it) => decl(it),
439 ast::Const(it) => decl(it), 439 ast::Const(it) => decl(it),
440 ast::Static(it) => decl(it), 440 ast::Static(it) => decl(it),
441 ast::MacroRules(it) => decl(it), 441 ast::Macro(it) => decl(it),
442 ast::Union(it) => decl(it), 442 ast::Union(it) => decl(it),
443 _ => None, 443 _ => None,
444 } 444 }
@@ -458,6 +458,7 @@ fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> {
458 CONST => FileSymbolKind::Const, 458 CONST => FileSymbolKind::Const,
459 STATIC => FileSymbolKind::Static, 459 STATIC => FileSymbolKind::Static,
460 MACRO_RULES => FileSymbolKind::Macro, 460 MACRO_RULES => FileSymbolKind::Macro,
461 MACRO_DEF => FileSymbolKind::Macro,
461 UNION => FileSymbolKind::Union, 462 UNION => FileSymbolKind::Union,
462 kind => unreachable!("{:?}", kind), 463 kind => unreachable!("{:?}", kind),
463 }, 464 },
diff --git a/crates/ide_db/src/traits.rs b/crates/ide_db/src/traits.rs
index 78a43f587..66ae81c73 100644
--- a/crates/ide_db/src/traits.rs
+++ b/crates/ide_db/src/traits.rs
@@ -61,7 +61,7 @@ pub fn get_missing_assoc_items(
61 resolve_target_trait(sema, impl_def).map_or(vec![], |target_trait| { 61 resolve_target_trait(sema, impl_def).map_or(vec![], |target_trait| {
62 target_trait 62 target_trait
63 .items(sema.db) 63 .items(sema.db)
64 .iter() 64 .into_iter()
65 .filter(|i| match i { 65 .filter(|i| match i {
66 hir::AssocItem::Function(f) => { 66 hir::AssocItem::Function(f) => {
67 !impl_fns_consts.contains(&f.name(sema.db).to_string()) 67 !impl_fns_consts.contains(&f.name(sema.db).to_string())
@@ -72,7 +72,6 @@ pub fn get_missing_assoc_items(
72 .map(|n| !impl_fns_consts.contains(&n.to_string())) 72 .map(|n| !impl_fns_consts.contains(&n.to_string()))
73 .unwrap_or_default(), 73 .unwrap_or_default(),
74 }) 74 })
75 .cloned()
76 .collect() 75 .collect()
77 }) 76 })
78} 77}
diff --git a/crates/ide_db/src/traits/tests.rs b/crates/ide_db/src/traits/tests.rs
index 84bb25505..2a5482024 100644
--- a/crates/ide_db/src/traits/tests.rs
+++ b/crates/ide_db/src/traits/tests.rs
@@ -1,10 +1,11 @@
1use crate::RootDatabase;
2use base_db::{fixture::ChangeFixture, FilePosition}; 1use base_db::{fixture::ChangeFixture, FilePosition};
3use expect_test::{expect, Expect}; 2use expect_test::{expect, Expect};
4use hir::Semantics; 3use hir::Semantics;
5use syntax::ast::{self, AstNode}; 4use syntax::ast::{self, AstNode};
6use test_utils::RangeOrOffset; 5use test_utils::RangeOrOffset;
7 6
7use crate::RootDatabase;
8
8/// Creates analysis from a multi-file fixture, returns positions marked with $0. 9/// Creates analysis from a multi-file fixture, returns positions marked with $0.
9pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { 10pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
10 let change_fixture = ChangeFixture::parse(ra_fixture); 11 let change_fixture = ChangeFixture::parse(ra_fixture);
diff --git a/crates/ide_db/src/ty_filter.rs b/crates/ide_db/src/ty_filter.rs
index f8406851b..988ecd060 100644
--- a/crates/ide_db/src/ty_filter.rs
+++ b/crates/ide_db/src/ty_filter.rs
@@ -2,11 +2,13 @@
2//! Use case for structures in this module is, for example, situation when you need to process 2//! Use case for structures in this module is, for example, situation when you need to process
3//! only certain `Enum`s. 3//! only certain `Enum`s.
4 4
5use crate::RootDatabase;
6use hir::{Adt, Semantics, Type};
7use std::iter; 5use std::iter;
6
7use hir::{Adt, Semantics, Type};
8use syntax::ast::{self, make}; 8use syntax::ast::{self, make};
9 9
10use crate::RootDatabase;
11
10/// Enum types that implement `std::ops::Try` trait. 12/// Enum types that implement `std::ops::Try` trait.
11#[derive(Clone, Copy)] 13#[derive(Clone, Copy)]
12pub enum TryEnum { 14pub enum TryEnum {
diff --git a/crates/ide_ssr/src/resolving.rs b/crates/ide_ssr/src/resolving.rs
index dc7835473..541da4122 100644
--- a/crates/ide_ssr/src/resolving.rs
+++ b/crates/ide_ssr/src/resolving.rs
@@ -150,7 +150,7 @@ impl Resolver<'_, '_> {
150 fn path_contains_placeholder(&self, path: &ast::Path) -> bool { 150 fn path_contains_placeholder(&self, path: &ast::Path) -> bool {
151 if let Some(segment) = path.segment() { 151 if let Some(segment) = path.segment() {
152 if let Some(name_ref) = segment.name_ref() { 152 if let Some(name_ref) = segment.name_ref() {
153 if self.placeholders_by_stand_in.contains_key(name_ref.text()) { 153 if self.placeholders_by_stand_in.contains_key(name_ref.text().as_str()) {
154 return true; 154 return true;
155 } 155 }
156 } 156 }
diff --git a/crates/proc_macro_srv/src/rustc_server.rs b/crates/proc_macro_srv/src/rustc_server.rs
index c147484c0..5d765f6e2 100644
--- a/crates/proc_macro_srv/src/rustc_server.rs
+++ b/crates/proc_macro_srv/src/rustc_server.rs
@@ -534,8 +534,12 @@ impl server::Literal for Rustc {
534 } 534 }
535 535
536 fn integer(&mut self, n: &str) -> Self::Literal { 536 fn integer(&mut self, n: &str) -> Self::Literal {
537 let n: i128 = n.parse().unwrap(); 537 let n = if let Ok(n) = n.parse::<i128>() {
538 Literal { text: n.to_string().into(), id: tt::TokenId::unspecified() } 538 n.to_string()
539 } else {
540 n.parse::<u128>().unwrap().to_string()
541 };
542 return Literal { text: n.into(), id: tt::TokenId::unspecified() };
539 } 543 }
540 544
541 fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal { 545 fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal {
@@ -757,6 +761,17 @@ mod tests {
757 assert_eq!(srv.string("hello_world").text, "\"hello_world\""); 761 assert_eq!(srv.string("hello_world").text, "\"hello_world\"");
758 assert_eq!(srv.character('c').text, "'c'"); 762 assert_eq!(srv.character('c').text, "'c'");
759 assert_eq!(srv.byte_string(b"1234586\x88").text, "b\"1234586\\x88\""); 763 assert_eq!(srv.byte_string(b"1234586\x88").text, "b\"1234586\\x88\"");
764
765 // u128::max
766 assert_eq!(
767 srv.integer("340282366920938463463374607431768211455").text,
768 "340282366920938463463374607431768211455"
769 );
770 // i128::min
771 assert_eq!(
772 srv.integer("-170141183460469231731687303715884105728").text,
773 "-170141183460469231731687303715884105728"
774 );
760 } 775 }
761 776
762 #[test] 777 #[test]
diff --git a/crates/profile/src/google_cpu_profiler.rs b/crates/profile/src/google_cpu_profiler.rs
index db865c65b..cae6caeaa 100644
--- a/crates/profile/src/google_cpu_profiler.rs
+++ b/crates/profile/src/google_cpu_profiler.rs
@@ -14,26 +14,31 @@ extern "C" {
14 fn ProfilerStop(); 14 fn ProfilerStop();
15} 15}
16 16
17static PROFILER_STATE: AtomicUsize = AtomicUsize::new(OFF);
18const OFF: usize = 0; 17const OFF: usize = 0;
19const ON: usize = 1; 18const ON: usize = 1;
20const PENDING: usize = 2; 19const PENDING: usize = 2;
21 20
22pub fn start(path: &Path) { 21fn transition(current: usize, new: usize) -> bool {
23 if PROFILER_STATE.compare_and_swap(OFF, PENDING, Ordering::SeqCst) != OFF { 22 static STATE: AtomicUsize = AtomicUsize::new(OFF);
23
24 STATE.compare_exchange(current, new, Ordering::SeqCst, Ordering::SeqCst).is_ok()
25}
26
27pub(crate) fn start(path: &Path) {
28 if !transition(OFF, PENDING) {
24 panic!("profiler already started"); 29 panic!("profiler already started");
25 } 30 }
26 let path = CString::new(path.display().to_string()).unwrap(); 31 let path = CString::new(path.display().to_string()).unwrap();
27 if unsafe { ProfilerStart(path.as_ptr()) } == 0 { 32 if unsafe { ProfilerStart(path.as_ptr()) } == 0 {
28 panic!("profiler failed to start") 33 panic!("profiler failed to start")
29 } 34 }
30 assert!(PROFILER_STATE.compare_and_swap(PENDING, ON, Ordering::SeqCst) == PENDING); 35 assert!(transition(PENDING, ON));
31} 36}
32 37
33pub fn stop() { 38pub(crate) fn stop() {
34 if PROFILER_STATE.compare_and_swap(ON, PENDING, Ordering::SeqCst) != ON { 39 if !transition(ON, PENDING) {
35 panic!("profiler is not started") 40 panic!("profiler is not started")
36 } 41 }
37 unsafe { ProfilerStop() }; 42 unsafe { ProfilerStop() };
38 assert!(PROFILER_STATE.compare_and_swap(PENDING, OFF, Ordering::SeqCst) == PENDING); 43 assert!(transition(PENDING, OFF));
39} 44}
diff --git a/crates/profile/src/hprof.rs b/crates/profile/src/hprof.rs
index 29d2ed518..5fdb37206 100644
--- a/crates/profile/src/hprof.rs
+++ b/crates/profile/src/hprof.rs
@@ -1,5 +1,4 @@
1//! Simple hierarchical profiler 1//! Simple hierarchical profiler
2use once_cell::sync::Lazy;
3use std::{ 2use std::{
4 cell::RefCell, 3 cell::RefCell,
5 collections::{BTreeMap, HashSet}, 4 collections::{BTreeMap, HashSet},
@@ -12,6 +11,8 @@ use std::{
12 time::{Duration, Instant}, 11 time::{Duration, Instant},
13}; 12};
14 13
14use once_cell::sync::Lazy;
15
15use crate::tree::{Idx, Tree}; 16use crate::tree::{Idx, Tree};
16 17
17/// Filtering syntax 18/// Filtering syntax
@@ -56,18 +57,32 @@ type Label = &'static str;
56/// 0ms - profile 57/// 0ms - profile
57/// 0ms - profile2 58/// 0ms - profile2
58/// ``` 59/// ```
60#[inline]
59pub fn span(label: Label) -> ProfileSpan { 61pub fn span(label: Label) -> ProfileSpan {
60 assert!(!label.is_empty()); 62 debug_assert!(!label.is_empty());
61 63
62 if PROFILING_ENABLED.load(Ordering::Relaxed) 64 let enabled = PROFILING_ENABLED.load(Ordering::Relaxed);
63 && PROFILE_STACK.with(|stack| stack.borrow_mut().push(label)) 65 if enabled && with_profile_stack(|stack| stack.push(label)) {
64 {
65 ProfileSpan(Some(ProfilerImpl { label, detail: None })) 66 ProfileSpan(Some(ProfilerImpl { label, detail: None }))
66 } else { 67 } else {
67 ProfileSpan(None) 68 ProfileSpan(None)
68 } 69 }
69} 70}
70 71
72#[inline]
73pub fn heartbeat_span() -> HeartbeatSpan {
74 let enabled = PROFILING_ENABLED.load(Ordering::Relaxed);
75 HeartbeatSpan::new(enabled)
76}
77
78#[inline]
79pub fn heartbeat() {
80 let enabled = PROFILING_ENABLED.load(Ordering::Relaxed);
81 if enabled {
82 with_profile_stack(|it| it.heartbeat(1));
83 }
84}
85
71pub struct ProfileSpan(Option<ProfilerImpl>); 86pub struct ProfileSpan(Option<ProfilerImpl>);
72 87
73struct ProfilerImpl { 88struct ProfilerImpl {
@@ -85,20 +100,48 @@ impl ProfileSpan {
85} 100}
86 101
87impl Drop for ProfilerImpl { 102impl Drop for ProfilerImpl {
103 #[inline]
104 fn drop(&mut self) {
105 with_profile_stack(|it| it.pop(self.label, self.detail.take()));
106 }
107}
108
109pub struct HeartbeatSpan {
110 enabled: bool,
111}
112
113impl HeartbeatSpan {
114 #[inline]
115 pub fn new(enabled: bool) -> Self {
116 if enabled {
117 with_profile_stack(|it| it.heartbeats(true))
118 }
119 Self { enabled }
120 }
121}
122
123impl Drop for HeartbeatSpan {
88 fn drop(&mut self) { 124 fn drop(&mut self) {
89 PROFILE_STACK.with(|it| it.borrow_mut().pop(self.label, self.detail.take())); 125 if self.enabled {
126 with_profile_stack(|it| it.heartbeats(false))
127 }
90 } 128 }
91} 129}
92 130
93static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false); 131static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);
94static FILTER: Lazy<RwLock<Filter>> = Lazy::new(Default::default); 132static FILTER: Lazy<RwLock<Filter>> = Lazy::new(Default::default);
95thread_local!(static PROFILE_STACK: RefCell<ProfileStack> = RefCell::new(ProfileStack::new())); 133
134fn with_profile_stack<T>(f: impl FnOnce(&mut ProfileStack) -> T) -> T {
135 thread_local!(static STACK: RefCell<ProfileStack> = RefCell::new(ProfileStack::new()));
136 STACK.with(|it| f(&mut *it.borrow_mut()))
137}
96 138
97#[derive(Default, Clone, Debug)] 139#[derive(Default, Clone, Debug)]
98struct Filter { 140struct Filter {
99 depth: usize, 141 depth: usize,
100 allowed: HashSet<String>, 142 allowed: HashSet<String>,
101 longer_than: Duration, 143 longer_than: Duration,
144 heartbeat_longer_than: Duration,
102 version: usize, 145 version: usize,
103} 146}
104 147
@@ -115,6 +158,7 @@ impl Filter {
115 } else { 158 } else {
116 Duration::new(0, 0) 159 Duration::new(0, 0)
117 }; 160 };
161 let heartbeat_longer_than = longer_than;
118 162
119 let depth = if let Some(idx) = spec.rfind('@') { 163 let depth = if let Some(idx) = spec.rfind('@') {
120 let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth"); 164 let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth");
@@ -125,7 +169,7 @@ impl Filter {
125 }; 169 };
126 let allowed = 170 let allowed =
127 if spec == "*" { HashSet::new() } else { spec.split('|').map(String::from).collect() }; 171 if spec == "*" { HashSet::new() } else { spec.split('|').map(String::from).collect() };
128 Filter { depth, allowed, longer_than, version: 0 } 172 Filter { depth, allowed, longer_than, heartbeat_longer_than, version: 0 }
129 } 173 }
130 174
131 fn install(mut self) { 175 fn install(mut self) {
@@ -137,9 +181,15 @@ impl Filter {
137} 181}
138 182
139struct ProfileStack { 183struct ProfileStack {
140 starts: Vec<Instant>, 184 frames: Vec<Frame>,
141 filter: Filter, 185 filter: Filter,
142 messages: Tree<Message>, 186 messages: Tree<Message>,
187 heartbeats: bool,
188}
189
190struct Frame {
191 t: Instant,
192 heartbeats: u32,
143} 193}
144 194
145#[derive(Default)] 195#[derive(Default)]
@@ -151,35 +201,49 @@ struct Message {
151 201
152impl ProfileStack { 202impl ProfileStack {
153 fn new() -> ProfileStack { 203 fn new() -> ProfileStack {
154 ProfileStack { starts: Vec::new(), messages: Tree::default(), filter: Default::default() } 204 ProfileStack {
205 frames: Vec::new(),
206 messages: Tree::default(),
207 filter: Default::default(),
208 heartbeats: false,
209 }
155 } 210 }
156 211
157 fn push(&mut self, label: Label) -> bool { 212 fn push(&mut self, label: Label) -> bool {
158 if self.starts.is_empty() { 213 if self.frames.is_empty() {
159 if let Ok(f) = FILTER.try_read() { 214 if let Ok(f) = FILTER.try_read() {
160 if f.version > self.filter.version { 215 if f.version > self.filter.version {
161 self.filter = f.clone(); 216 self.filter = f.clone();
162 } 217 }
163 }; 218 };
164 } 219 }
165 if self.starts.len() > self.filter.depth { 220 if self.frames.len() > self.filter.depth {
166 return false; 221 return false;
167 } 222 }
168 let allowed = &self.filter.allowed; 223 let allowed = &self.filter.allowed;
169 if self.starts.is_empty() && !allowed.is_empty() && !allowed.contains(label) { 224 if self.frames.is_empty() && !allowed.is_empty() && !allowed.contains(label) {
170 return false; 225 return false;
171 } 226 }
172 227
173 self.starts.push(Instant::now()); 228 self.frames.push(Frame { t: Instant::now(), heartbeats: 0 });
174 self.messages.start(); 229 self.messages.start();
175 true 230 true
176 } 231 }
177 232
178 fn pop(&mut self, label: Label, detail: Option<String>) { 233 fn pop(&mut self, label: Label, detail: Option<String>) {
179 let start = self.starts.pop().unwrap(); 234 let frame = self.frames.pop().unwrap();
180 let duration = start.elapsed(); 235 let duration = frame.t.elapsed();
236
237 if self.heartbeats {
238 self.heartbeat(frame.heartbeats);
239 let avg_span = duration / (frame.heartbeats + 1);
240 if avg_span > self.filter.heartbeat_longer_than {
241 eprintln!("Too few heartbeats {} ({}/{:?})?", label, frame.heartbeats, duration)
242 }
243 }
244
181 self.messages.finish(Message { duration, label, detail }); 245 self.messages.finish(Message { duration, label, detail });
182 if self.starts.is_empty() { 246 if self.frames.is_empty() {
183 let longer_than = self.filter.longer_than; 247 let longer_than = self.filter.longer_than;
184 // Convert to millis for comparison to avoid problems with rounding 248 // Convert to millis for comparison to avoid problems with rounding
185 // (otherwise we could print `0ms` despite user's `>0` filter when 249 // (otherwise we could print `0ms` despite user's `>0` filter when
@@ -192,6 +256,15 @@ impl ProfileStack {
192 self.messages.clear(); 256 self.messages.clear();
193 } 257 }
194 } 258 }
259
260 fn heartbeats(&mut self, yes: bool) {
261 self.heartbeats = yes;
262 }
263 fn heartbeat(&mut self, n: u32) {
264 if let Some(frame) = self.frames.last_mut() {
265 frame.heartbeats += n;
266 }
267 }
195} 268}
196 269
197fn print( 270fn print(
diff --git a/crates/profile/src/lib.rs b/crates/profile/src/lib.rs
index 79dba47d5..a31fb8f43 100644
--- a/crates/profile/src/lib.rs
+++ b/crates/profile/src/lib.rs
@@ -10,7 +10,7 @@ mod tree;
10use std::cell::RefCell; 10use std::cell::RefCell;
11 11
12pub use crate::{ 12pub use crate::{
13 hprof::{init, init_from, span}, 13 hprof::{heartbeat, heartbeat_span, init, init_from, span},
14 memory_usage::{Bytes, MemoryUsage}, 14 memory_usage::{Bytes, MemoryUsage},
15 stop_watch::{StopWatch, StopWatchSpan}, 15 stop_watch::{StopWatch, StopWatchSpan},
16}; 16};
@@ -52,7 +52,7 @@ impl Drop for Scope {
52/// Usage: 52/// Usage:
53/// 1. Install gpref_tools (https://github.com/gperftools/gperftools), probably packaged with your Linux distro. 53/// 1. Install gpref_tools (https://github.com/gperftools/gperftools), probably packaged with your Linux distro.
54/// 2. Build with `cpu_profiler` feature. 54/// 2. Build with `cpu_profiler` feature.
55/// 3. Tun the code, the *raw* output would be in the `./out.profile` file. 55/// 3. Run the code, the *raw* output would be in the `./out.profile` file.
56/// 4. Install pprof for visualization (https://github.com/google/pprof). 56/// 4. Install pprof for visualization (https://github.com/google/pprof).
57/// 5. Bump sampling frequency to once per ms: `export CPUPROFILE_FREQUENCY=1000` 57/// 5. Bump sampling frequency to once per ms: `export CPUPROFILE_FREQUENCY=1000`
58/// 6. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results. 58/// 6. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results.
@@ -60,8 +60,17 @@ impl Drop for Scope {
60/// For example, here's how I run profiling on NixOS: 60/// For example, here's how I run profiling on NixOS:
61/// 61///
62/// ```bash 62/// ```bash
63/// $ nix-shell -p gperftools --run \ 63/// $ bat -p shell.nix
64/// 'cargo run --release -p rust-analyzer -- parse < ~/projects/rustbench/parser.rs > /dev/null' 64/// with import <nixpkgs> {};
65/// mkShell {
66/// buildInputs = [ gperftools ];
67/// shellHook = ''
68/// export LD_LIBRARY_PATH="${gperftools}/lib:"
69/// '';
70/// }
71/// $ set -x CPUPROFILE_FREQUENCY 1000
72/// $ nix-shell --run 'cargo test --release --package rust-analyzer --lib -- benchmarks::benchmark_integrated_highlighting --exact --nocapture'
73/// $ pprof -svg target/release/deps/rust_analyzer-8739592dc93d63cb crates/rust-analyzer/out.profile > profile.svg
65/// ``` 74/// ```
66/// 75///
67/// See this diff for how to profile completions: 76/// See this diff for how to profile completions:
@@ -81,7 +90,9 @@ pub fn cpu_span() -> CpuSpan {
81 90
82 #[cfg(not(feature = "cpu_profiler"))] 91 #[cfg(not(feature = "cpu_profiler"))]
83 { 92 {
84 eprintln!("cpu_profiler feature is disabled") 93 eprintln!(
94 r#"cpu profiling is disabled, uncomment `default = [ "cpu_profiler" ]` in Cargo.toml to enable."#
95 )
85 } 96 }
86 97
87 CpuSpan { _private: () } 98 CpuSpan { _private: () }
@@ -91,7 +102,23 @@ impl Drop for CpuSpan {
91 fn drop(&mut self) { 102 fn drop(&mut self) {
92 #[cfg(feature = "cpu_profiler")] 103 #[cfg(feature = "cpu_profiler")]
93 { 104 {
94 google_cpu_profiler::stop() 105 google_cpu_profiler::stop();
106 let profile_data = std::env::current_dir().unwrap().join("out.profile");
107 eprintln!("Profile data saved to:\n\n {}\n", profile_data.display());
108 let mut cmd = std::process::Command::new("pprof");
109 cmd.arg("-svg").arg(std::env::current_exe().unwrap()).arg(&profile_data);
110 let out = cmd.output();
111
112 match out {
113 Ok(out) if out.status.success() => {
114 let svg = profile_data.with_extension("svg");
115 std::fs::write(&svg, &out.stdout).unwrap();
116 eprintln!("Profile rendered to:\n\n {}\n", svg.display());
117 }
118 _ => {
119 eprintln!("Failed to run:\n\n {:?}\n", cmd);
120 }
121 }
95 } 122 }
96 } 123 }
97} 124}
diff --git a/crates/rust-analyzer/src/benchmarks.rs b/crates/rust-analyzer/src/benchmarks.rs
new file mode 100644
index 000000000..bf569b40b
--- /dev/null
+++ b/crates/rust-analyzer/src/benchmarks.rs
@@ -0,0 +1,71 @@
1//! Fully integrated benchmarks for rust-analyzer, which load real cargo
2//! projects.
3//!
4//! The benchmark here is used to debug specific performance regressions. If you
5//! notice that, eg, completion is slow in some specific case, you can modify
6//! code here exercise this specific completion, and thus have a fast
7//! edit/compile/test cycle.
8//!
9//! Note that "Rust Analyzer: Run" action does not allow running a single test
10//! in release mode in VS Code. There's however "Rust Analyzer: Copy Run Command Line"
11//! which you can use to paste the command in terminal and add `--release` manually.
12
13use std::sync::Arc;
14
15use ide::Change;
16use test_utils::project_root;
17use vfs::{AbsPathBuf, VfsPath};
18
19use crate::cli::load_cargo::{load_workspace_at, LoadCargoConfig};
20
21#[test]
22fn benchmark_integrated_highlighting() {
23 // Don't run slow benchmark by default
24 if true {
25 return;
26 }
27
28 // Load rust-analyzer itself.
29 let workspace_to_load = project_root();
30 let file = "./crates/ide_db/src/apply_change.rs";
31
32 let cargo_config = Default::default();
33 let load_cargo_config =
34 LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro: false };
35
36 let (mut host, vfs, _proc_macro) = {
37 let _it = stdx::timeit("workspace loading");
38 load_workspace_at(&workspace_to_load, &cargo_config, &load_cargo_config, &|_| {}).unwrap()
39 };
40
41 let file_id = {
42 let file = workspace_to_load.join(file);
43 let path = VfsPath::from(AbsPathBuf::assert(file));
44 vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {}", path))
45 };
46
47 {
48 let _it = stdx::timeit("initial");
49 let analysis = host.analysis();
50 analysis.highlight_as_html(file_id, false).unwrap();
51 }
52
53 profile::init_from("*>100");
54 // let _s = profile::heartbeat_span();
55
56 {
57 let _it = stdx::timeit("change");
58 let mut text = host.analysis().file_text(file_id).unwrap().to_string();
59 text.push_str("\npub fn _dummy() {}\n");
60 let mut change = Change::new();
61 change.change_file(file_id, Some(Arc::new(text)));
62 host.apply_change(change);
63 }
64
65 {
66 let _it = stdx::timeit("after change");
67 let _span = profile::cpu_span();
68 let analysis = host.analysis();
69 analysis.highlight_as_html(file_id, false).unwrap();
70 }
71}
diff --git a/crates/rust-analyzer/src/bin/flags.rs b/crates/rust-analyzer/src/bin/flags.rs
index d8987633d..b05fc00b9 100644
--- a/crates/rust-analyzer/src/bin/flags.rs
+++ b/crates/rust-analyzer/src/bin/flags.rs
@@ -1,10 +1,9 @@
1//! Grammar for the command-line arguments. 1//! Grammar for the command-line arguments.
2#![allow(unreachable_pub)] 2#![allow(unreachable_pub)]
3use std::{env, path::PathBuf}; 3use std::path::PathBuf;
4 4
5use ide_ssr::{SsrPattern, SsrRule}; 5use ide_ssr::{SsrPattern, SsrRule};
6use rust_analyzer::cli::{BenchWhat, Position, Verbosity}; 6use rust_analyzer::cli::Verbosity;
7use vfs::AbsPathBuf;
8 7
9xflags::xflags! { 8xflags::xflags! {
10 src "./src/bin/flags.rs" 9 src "./src/bin/flags.rs"
@@ -74,27 +73,6 @@ xflags::xflags! {
74 optional --with-proc-macro 73 optional --with-proc-macro
75 } 74 }
76 75
77 /// Benchmark specific analysis operation
78 cmd analysis-bench
79 /// Directory with Cargo.toml.
80 required path: PathBuf
81 {
82 /// Collect memory usage statistics.
83 optional --memory-usage
84
85 /// Compute syntax highlighting for this file
86 optional --highlight path: PathBuf
87 /// Compute completions at file:line:column location.
88 optional --complete location: Position
89 /// Compute goto definition at file:line:column location.
90 optional --goto-def location: Position
91
92 /// Load OUT_DIR values by running `cargo check` before analysis.
93 optional --load-output-dirs
94 /// Use proc-macro-srv for proc-macro expanding.
95 optional --with-proc-macro
96 }
97
98 cmd diagnostics 76 cmd diagnostics
99 /// Directory with Cargo.toml. 77 /// Directory with Cargo.toml.
100 required path: PathBuf 78 required path: PathBuf
@@ -142,7 +120,6 @@ pub enum RustAnalyzerCmd {
142 Symbols(Symbols), 120 Symbols(Symbols),
143 Highlight(Highlight), 121 Highlight(Highlight),
144 AnalysisStats(AnalysisStats), 122 AnalysisStats(AnalysisStats),
145 AnalysisBench(AnalysisBench),
146 Diagnostics(Diagnostics), 123 Diagnostics(Diagnostics),
147 Ssr(Ssr), 124 Ssr(Ssr),
148 Search(Search), 125 Search(Search),
@@ -184,18 +161,6 @@ pub struct AnalysisStats {
184} 161}
185 162
186#[derive(Debug)] 163#[derive(Debug)]
187pub struct AnalysisBench {
188 pub path: PathBuf,
189
190 pub memory_usage: bool,
191 pub highlight: Option<PathBuf>,
192 pub complete: Option<Position>,
193 pub goto_def: Option<Position>,
194 pub load_output_dirs: bool,
195 pub with_proc_macro: bool,
196}
197
198#[derive(Debug)]
199pub struct Diagnostics { 164pub struct Diagnostics {
200 pub path: PathBuf, 165 pub path: PathBuf,
201 166
@@ -239,17 +204,3 @@ impl RustAnalyzer {
239 } 204 }
240 } 205 }
241} 206}
242
243impl AnalysisBench {
244 pub(crate) fn what(&self) -> BenchWhat {
245 match (&self.highlight, &self.complete, &self.goto_def) {
246 (Some(path), None, None) => {
247 let path = env::current_dir().unwrap().join(path);
248 BenchWhat::Highlight { path: AbsPathBuf::assert(path) }
249 }
250 (None, Some(position), None) => BenchWhat::Complete(position.clone()),
251 (None, None, Some(position)) => BenchWhat::GotoDef(position.clone()),
252 _ => panic!("exactly one of `--highlight`, `--complete` or `--goto-def` must be set"),
253 }
254 }
255}
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index a0b611bff..ae99eefe3 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -9,7 +9,7 @@ use std::{convert::TryFrom, env, fs, path::Path, process};
9use lsp_server::Connection; 9use lsp_server::Connection;
10use project_model::ProjectManifest; 10use project_model::ProjectManifest;
11use rust_analyzer::{ 11use rust_analyzer::{
12 cli::{self, AnalysisStatsCmd, BenchCmd}, 12 cli::{self, AnalysisStatsCmd},
13 config::Config, 13 config::Config,
14 from_json, 14 from_json,
15 lsp_ext::supports_utf8, 15 lsp_ext::supports_utf8,
@@ -80,17 +80,6 @@ fn try_main() -> Result<()> {
80 with_proc_macro: cmd.with_proc_macro, 80 with_proc_macro: cmd.with_proc_macro,
81 } 81 }
82 .run(verbosity)?, 82 .run(verbosity)?,
83 flags::RustAnalyzerCmd::AnalysisBench(cmd) => {
84 let what = cmd.what();
85 BenchCmd {
86 memory_usage: cmd.memory_usage,
87 path: cmd.path,
88 load_output_dirs: cmd.load_output_dirs,
89 with_proc_macro: cmd.with_proc_macro,
90 what,
91 }
92 .run(verbosity)?
93 }
94 83
95 flags::RustAnalyzerCmd::Diagnostics(cmd) => { 84 flags::RustAnalyzerCmd::Diagnostics(cmd) => {
96 cli::diagnostics(&cmd.path, cmd.load_output_dirs, cmd.with_proc_macro)? 85 cli::diagnostics(&cmd.path, cmd.load_output_dirs, cmd.with_proc_macro)?
diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs
index ed732eb38..76b666dc2 100644
--- a/crates/rust-analyzer/src/cli.rs
+++ b/crates/rust-analyzer/src/cli.rs
@@ -1,8 +1,7 @@
1//! Various batch processing tasks, intended primarily for debugging. 1//! Various batch processing tasks, intended primarily for debugging.
2 2
3mod load_cargo; 3pub(crate) mod load_cargo;
4mod analysis_stats; 4mod analysis_stats;
5mod analysis_bench;
6mod diagnostics; 5mod diagnostics;
7mod progress_report; 6mod progress_report;
8mod ssr; 7mod ssr;
@@ -15,7 +14,6 @@ use syntax::{AstNode, SourceFile};
15use vfs::Vfs; 14use vfs::Vfs;
16 15
17pub use self::{ 16pub use self::{
18 analysis_bench::{BenchCmd, BenchWhat, Position},
19 analysis_stats::AnalysisStatsCmd, 17 analysis_stats::AnalysisStatsCmd,
20 diagnostics::diagnostics, 18 diagnostics::diagnostics,
21 load_cargo::{load_workspace, load_workspace_at, LoadCargoConfig}, 19 load_cargo::{load_workspace, load_workspace_at, LoadCargoConfig},
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
deleted file mode 100644
index 49994824f..000000000
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ /dev/null
@@ -1,196 +0,0 @@
1//! Benchmark operations like highlighting or goto definition.
2
3use std::{env, path::PathBuf, str::FromStr, sync::Arc, time::Instant};
4
5use anyhow::{bail, format_err, Result};
6use hir::PrefixKind;
7use ide::{
8 Analysis, AnalysisHost, Change, CompletionConfig, DiagnosticsConfig, FilePosition, LineCol,
9};
10use ide_db::{
11 base_db::{
12 salsa::{Database, Durability},
13 FileId,
14 },
15 helpers::{insert_use::InsertUseConfig, SnippetCap},
16};
17use vfs::AbsPathBuf;
18
19use crate::cli::{
20 load_cargo::{load_workspace_at, LoadCargoConfig},
21 print_memory_usage, Verbosity,
22};
23
24pub struct BenchCmd {
25 pub path: PathBuf,
26 pub what: BenchWhat,
27 pub memory_usage: bool,
28 pub load_output_dirs: bool,
29 pub with_proc_macro: bool,
30}
31
32pub enum BenchWhat {
33 Highlight { path: AbsPathBuf },
34 Complete(Position),
35 GotoDef(Position),
36}
37
38#[derive(Debug, Clone)]
39pub struct Position {
40 pub path: AbsPathBuf,
41 pub line: u32,
42 pub column: u32,
43}
44
45impl FromStr for Position {
46 type Err = anyhow::Error;
47 fn from_str(s: &str) -> Result<Self> {
48 let mut split = s.rsplitn(3, ':');
49 match (split.next(), split.next(), split.next()) {
50 (Some(column), Some(line), Some(path)) => {
51 let path = env::current_dir().unwrap().join(path);
52 let path = AbsPathBuf::assert(path);
53 Ok(Position { path, line: line.parse()?, column: column.parse()? })
54 }
55 _ => bail!("position should be in file:line:column format: {:?}", s),
56 }
57 }
58}
59
60impl BenchCmd {
61 pub fn run(self, verbosity: Verbosity) -> Result<()> {
62 profile::init();
63
64 let start = Instant::now();
65 eprint!("loading: ");
66
67 let cargo_config = Default::default();
68 let load_cargo_config = LoadCargoConfig {
69 load_out_dirs_from_check: self.load_output_dirs,
70 with_proc_macro: self.with_proc_macro,
71 };
72 let (mut host, vfs, _proc_macro) =
73 load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?;
74 eprintln!("{:?}\n", start.elapsed());
75
76 let file_id = {
77 let path = match &self.what {
78 BenchWhat::Highlight { path } => path,
79 BenchWhat::Complete(pos) | BenchWhat::GotoDef(pos) => &pos.path,
80 };
81 let path = path.clone().into();
82 vfs.file_id(&path).ok_or_else(|| format_err!("Can't find {}", path))?
83 };
84
85 match &self.what {
86 BenchWhat::Highlight { .. } => {
87 let res = do_work(&mut host, file_id, |analysis| {
88 analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap();
89 analysis.highlight_as_html(file_id, false).unwrap()
90 });
91 if verbosity.is_verbose() {
92 println!("\n{}", res);
93 }
94 }
95 BenchWhat::Complete(pos) | BenchWhat::GotoDef(pos) => {
96 let is_completion = matches!(self.what, BenchWhat::Complete(..));
97
98 let offset = host
99 .analysis()
100 .file_line_index(file_id)?
101 .offset(LineCol { line: pos.line - 1, col: pos.column });
102 let file_position = FilePosition { file_id, offset };
103
104 if is_completion {
105 let options = CompletionConfig {
106 enable_postfix_completions: true,
107 enable_imports_on_the_fly: true,
108 add_call_parenthesis: true,
109 add_call_argument_snippets: true,
110 snippet_cap: SnippetCap::new(true),
111 insert_use: InsertUseConfig {
112 merge: None,
113 prefix_kind: PrefixKind::Plain,
114 group: true,
115 },
116 };
117 let res = do_work(&mut host, file_id, |analysis| {
118 analysis.completions(&options, file_position)
119 });
120 if verbosity.is_verbose() {
121 println!("\n{:#?}", res);
122 }
123 } else {
124 let res = do_work(&mut host, file_id, |analysis| {
125 analysis.goto_definition(file_position)
126 });
127 if verbosity.is_verbose() {
128 println!("\n{:#?}", res);
129 }
130 }
131 }
132 }
133
134 if self.memory_usage {
135 print_memory_usage(host, vfs);
136 }
137
138 Ok(())
139 }
140}
141
142fn do_work<F: Fn(&Analysis) -> T, T>(host: &mut AnalysisHost, file_id: FileId, work: F) -> T {
143 {
144 let start = Instant::now();
145 eprint!("from scratch: ");
146 work(&host.analysis());
147 eprintln!("{:?}", start.elapsed());
148 }
149 {
150 let start = Instant::now();
151 eprint!("no change: ");
152 work(&host.analysis());
153 eprintln!("{:?}", start.elapsed());
154 }
155 {
156 let start = Instant::now();
157 eprint!("trivial change: ");
158 host.raw_database_mut().salsa_runtime_mut().synthetic_write(Durability::LOW);
159 work(&host.analysis());
160 eprintln!("{:?}", start.elapsed());
161 }
162 {
163 let start = Instant::now();
164 eprint!("comment change: ");
165 {
166 let mut text = host.analysis().file_text(file_id).unwrap().to_string();
167 text.push_str("\n/* Hello world */\n");
168 let mut change = Change::new();
169 change.change_file(file_id, Some(Arc::new(text)));
170 host.apply_change(change);
171 }
172 work(&host.analysis());
173 eprintln!("{:?}", start.elapsed());
174 }
175 {
176 let start = Instant::now();
177 eprint!("item change: ");
178 {
179 let mut text = host.analysis().file_text(file_id).unwrap().to_string();
180 text.push_str("\npub fn _dummy() {}\n");
181 let mut change = Change::new();
182 change.change_file(file_id, Some(Arc::new(text)));
183 host.apply_change(change);
184 }
185 work(&host.analysis());
186 eprintln!("{:?}", start.elapsed());
187 }
188 {
189 let start = Instant::now();
190 eprint!("const change: ");
191 host.raw_database_mut().salsa_runtime_mut().synthetic_write(Durability::HIGH);
192 let res = work(&host.analysis());
193 eprintln!("{:?}", start.elapsed());
194 res
195 }
196}
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index 8b874239c..d9a5030a0 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -39,6 +39,9 @@ mod op_queue;
39pub mod lsp_ext; 39pub mod lsp_ext;
40pub mod config; 40pub mod config;
41 41
42#[cfg(test)]
43mod benchmarks;
44
42use serde::de::DeserializeOwned; 45use serde::de::DeserializeOwned;
43use std::fmt; 46use std::fmt;
44 47
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index a3c5e9ccf..2dc8a42f1 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -88,6 +88,7 @@ define_semantic_token_modifiers![
88 (CONSUMING, "consuming"), 88 (CONSUMING, "consuming"),
89 (UNSAFE, "unsafe"), 89 (UNSAFE, "unsafe"),
90 (ATTRIBUTE_MODIFIER, "attribute"), 90 (ATTRIBUTE_MODIFIER, "attribute"),
91 (TRAIT_MODIFIER, "trait"),
91 (CALLABLE, "callable"), 92 (CALLABLE, "callable"),
92 (INTRA_DOC_LINK, "intraDocLink"), 93 (INTRA_DOC_LINK, "intraDocLink"),
93]; 94];
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 530c8a5a4..c3820944b 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -474,6 +474,7 @@ fn semantic_token_type_and_modifiers(
474 HlMod::Callable => semantic_tokens::CALLABLE, 474 HlMod::Callable => semantic_tokens::CALLABLE,
475 HlMod::Static => lsp_types::SemanticTokenModifier::STATIC, 475 HlMod::Static => lsp_types::SemanticTokenModifier::STATIC,
476 HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK, 476 HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK,
477 HlMod::Trait => semantic_tokens::TRAIT_MODIFIER,
477 HlMod::Associated => continue, 478 HlMod::Associated => continue,
478 }; 479 };
479 mods |= modifier; 480 mods |= modifier;
@@ -492,7 +493,11 @@ pub(crate) fn folding_range(
492 FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), 493 FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment),
493 FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), 494 FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports),
494 FoldKind::Region => Some(lsp_types::FoldingRangeKind::Region), 495 FoldKind::Region => Some(lsp_types::FoldingRangeKind::Region),
495 FoldKind::Mods | FoldKind::Block | FoldKind::ArgList => None, 496 FoldKind::Mods
497 | FoldKind::Block
498 | FoldKind::ArgList
499 | FoldKind::Consts
500 | FoldKind::Statics => None,
496 }; 501 };
497 502
498 let range = range(line_index, fold.range); 503 let range = range(line_index, fold.range);
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index c08f2c14f..c6a7b99b7 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -268,14 +268,14 @@ pub fn arg_list(args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList {
268} 268}
269 269
270pub fn ident_pat(name: ast::Name) -> ast::IdentPat { 270pub fn ident_pat(name: ast::Name) -> ast::IdentPat {
271 return from_text(name.text()); 271 return from_text(&name.text());
272 272
273 fn from_text(text: &str) -> ast::IdentPat { 273 fn from_text(text: &str) -> ast::IdentPat {
274 ast_from_text(&format!("fn f({}: ())", text)) 274 ast_from_text(&format!("fn f({}: ())", text))
275 } 275 }
276} 276}
277pub fn ident_mut_pat(name: ast::Name) -> ast::IdentPat { 277pub fn ident_mut_pat(name: ast::Name) -> ast::IdentPat {
278 return from_text(name.text()); 278 return from_text(&name.text());
279 279
280 fn from_text(text: &str) -> ast::IdentPat { 280 fn from_text(text: &str) -> ast::IdentPat {
281 ast_from_text(&format!("fn f(mut {}: ())", text)) 281 ast_from_text(&format!("fn f(mut {}: ())", text))
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index bdf907a21..ae98dbd26 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -8,23 +8,23 @@ use parser::SyntaxKind;
8 8
9use crate::{ 9use crate::{
10 ast::{self, support, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode}, 10 ast::{self, support, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode},
11 SmolStr, SyntaxElement, SyntaxToken, T, 11 SmolStr, SyntaxElement, SyntaxToken, TokenText, T,
12}; 12};
13 13
14impl ast::Lifetime { 14impl ast::Lifetime {
15 pub fn text(&self) -> &str { 15 pub fn text(&self) -> TokenText {
16 text_of_first_token(self.syntax()) 16 text_of_first_token(self.syntax())
17 } 17 }
18} 18}
19 19
20impl ast::Name { 20impl ast::Name {
21 pub fn text(&self) -> &str { 21 pub fn text(&self) -> TokenText {
22 text_of_first_token(self.syntax()) 22 text_of_first_token(self.syntax())
23 } 23 }
24} 24}
25 25
26impl ast::NameRef { 26impl ast::NameRef {
27 pub fn text(&self) -> &str { 27 pub fn text(&self) -> TokenText {
28 text_of_first_token(self.syntax()) 28 text_of_first_token(self.syntax())
29 } 29 }
30 30
@@ -33,12 +33,14 @@ impl ast::NameRef {
33 } 33 }
34} 34}
35 35
36fn text_of_first_token(node: &SyntaxNode) -> &str { 36fn text_of_first_token(node: &SyntaxNode) -> TokenText {
37 let t = 37 let first_token =
38 node.green().children().next().and_then(|it| it.into_token()).unwrap().text().to_string(); 38 node.green().children().next().and_then(|it| it.into_token()).unwrap().to_owned();
39 Box::leak(Box::new(t)) 39
40 TokenText(first_token)
40} 41}
41 42
43#[derive(Debug, PartialEq, Eq, Clone)]
42pub enum Macro { 44pub enum Macro {
43 MacroRules(ast::MacroRules), 45 MacroRules(ast::MacroRules),
44 MacroDef(ast::MacroDef), 46 MacroDef(ast::MacroDef),
@@ -378,7 +380,7 @@ impl fmt::Display for NameOrNameRef {
378} 380}
379 381
380impl NameOrNameRef { 382impl NameOrNameRef {
381 pub fn text(&self) -> &str { 383 pub fn text(&self) -> TokenText {
382 match self { 384 match self {
383 NameOrNameRef::Name(name) => name.text(), 385 NameOrNameRef::Name(name) => name.text(),
384 NameOrNameRef::NameRef(name_ref) => name_ref.text(), 386 NameOrNameRef::NameRef(name_ref) => name_ref.text(),
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs
index 2a5c61171..90de6bef6 100644
--- a/crates/syntax/src/lib.rs
+++ b/crates/syntax/src/lib.rs
@@ -29,6 +29,7 @@ mod syntax_error;
29mod parsing; 29mod parsing;
30mod validation; 30mod validation;
31mod ptr; 31mod ptr;
32mod token_text;
32#[cfg(test)] 33#[cfg(test)]
33mod tests; 34mod tests;
34 35
@@ -55,6 +56,7 @@ pub use crate::{
55 SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren, SyntaxToken, 56 SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren, SyntaxToken,
56 SyntaxTreeBuilder, 57 SyntaxTreeBuilder,
57 }, 58 },
59 token_text::TokenText,
58}; 60};
59pub use parser::{SyntaxKind, T}; 61pub use parser::{SyntaxKind, T};
60pub use rowan::{ 62pub use rowan::{
diff --git a/crates/syntax/src/parsing/text_tree_sink.rs b/crates/syntax/src/parsing/text_tree_sink.rs
index bb10f20c9..1934204ea 100644
--- a/crates/syntax/src/parsing/text_tree_sink.rs
+++ b/crates/syntax/src/parsing/text_tree_sink.rs
@@ -147,8 +147,8 @@ fn n_attached_trivias<'a>(
147 trivias: impl Iterator<Item = (SyntaxKind, &'a str)>, 147 trivias: impl Iterator<Item = (SyntaxKind, &'a str)>,
148) -> usize { 148) -> usize {
149 match kind { 149 match kind {
150 MACRO_CALL | MACRO_RULES | CONST | TYPE_ALIAS | STRUCT | UNION | ENUM | VARIANT | FN 150 MACRO_CALL | MACRO_RULES | MACRO_DEF | CONST | TYPE_ALIAS | STRUCT | UNION | ENUM
151 | TRAIT | MODULE | RECORD_FIELD | STATIC | USE => { 151 | VARIANT | FN | TRAIT | MODULE | RECORD_FIELD | STATIC | USE => {
152 let mut res = 0; 152 let mut res = 0;
153 let mut trivias = trivias.enumerate().peekable(); 153 let mut trivias = trivias.enumerate().peekable();
154 154
diff --git a/crates/syntax/src/token_text.rs b/crates/syntax/src/token_text.rs
new file mode 100644
index 000000000..d2ed0a12a
--- /dev/null
+++ b/crates/syntax/src/token_text.rs
@@ -0,0 +1,77 @@
1//! Yet another version of owned string, backed by a syntax tree token.
2
3use std::{cmp::Ordering, fmt, ops};
4
5pub struct TokenText(pub(crate) rowan::GreenToken);
6
7impl TokenText {
8 pub fn as_str(&self) -> &str {
9 self.0.text()
10 }
11}
12
13impl ops::Deref for TokenText {
14 type Target = str;
15
16 fn deref(&self) -> &str {
17 self.as_str()
18 }
19}
20impl AsRef<str> for TokenText {
21 fn as_ref(&self) -> &str {
22 self.as_str()
23 }
24}
25
26impl From<TokenText> for String {
27 fn from(token_text: TokenText) -> Self {
28 token_text.as_str().into()
29 }
30}
31
32impl PartialEq<&'_ str> for TokenText {
33 fn eq(&self, other: &&str) -> bool {
34 self.as_str() == *other
35 }
36}
37impl PartialEq<TokenText> for &'_ str {
38 fn eq(&self, other: &TokenText) -> bool {
39 other == self
40 }
41}
42impl PartialEq<String> for TokenText {
43 fn eq(&self, other: &String) -> bool {
44 self.as_str() == other.as_str()
45 }
46}
47impl PartialEq<TokenText> for String {
48 fn eq(&self, other: &TokenText) -> bool {
49 other == self
50 }
51}
52impl PartialEq for TokenText {
53 fn eq(&self, other: &TokenText) -> bool {
54 self.as_str() == other.as_str()
55 }
56}
57impl Eq for TokenText {}
58impl Ord for TokenText {
59 fn cmp(&self, other: &Self) -> Ordering {
60 self.as_str().cmp(other.as_str())
61 }
62}
63impl PartialOrd for TokenText {
64 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
65 Some(self.cmp(other))
66 }
67}
68impl fmt::Display for TokenText {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 fmt::Display::fmt(self.as_str(), f)
71 }
72}
73impl fmt::Debug for TokenText {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 fmt::Debug::fmt(self.as_str(), f)
76 }
77}
diff --git a/docs/dev/README.md b/docs/dev/README.md
index 57162a47d..eab21a765 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -1,7 +1,7 @@
1# Contributing Quick Start 1# Contributing Quick Start
2 2
3Rust Analyzer is an ordinary Rust project, which is organized as a Cargo 3Rust Analyzer is an ordinary Rust project, which is organized as a Cargo workspace, builds on stable and doesn't depend on C libraries.
4workspace, builds on stable and doesn't depend on C libraries. So, just 4So, just
5 5
6``` 6```
7$ cargo test 7$ cargo test
@@ -13,9 +13,8 @@ To learn more about how rust-analyzer works, see [./architecture.md](./architect
13It also explains the high-level layout of the source code. 13It also explains the high-level layout of the source code.
14Do skim through that document. 14Do skim through that document.
15 15
16We also publish rustdoc docs to pages: 16We also publish rustdoc docs to pages: https://rust-analyzer.github.io/rust-analyzer/ide/.
17 17Note though, that internal documentation is very incomplete.
18https://rust-analyzer.github.io/rust-analyzer/ide/
19 18
20Various organizational and process issues are discussed in this document. 19Various organizational and process issues are discussed in this document.
21 20
@@ -49,21 +48,28 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0
49 Also a kind of fun. 48 Also a kind of fun.
50 These issues should generally include a link to a Zulip discussion thread. 49 These issues should generally include a link to a Zulip discussion thread.
51 50
52# CI 51# Code Style & Review Process
52
53Do see [./style.md](./style.md).
53 54
54We use GitHub Actions for CI. Most of the things, including formatting, are checked by 55# Cookbook
55`cargo test` so, if `cargo test` passes locally, that's a good sign that CI will 56
56be green as well. The only exception is that some long-running tests are skipped locally by default. 57## CI
58
59We use GitHub Actions for CI.
60Most of the things, including formatting, are checked by `cargo test`.
61If `cargo test` passes locally, that's a good sign that CI will be green as well.
62The only exception is that some long-running tests are skipped locally by default.
57Use `env RUN_SLOW_TESTS=1 cargo test` to run the full suite. 63Use `env RUN_SLOW_TESTS=1 cargo test` to run the full suite.
58 64
59We use bors-ng to enforce the [not rocket science](https://graydon2.dreamwidth.org/1597.html) rule. 65We use bors-ng to enforce the [not rocket science](https://graydon2.dreamwidth.org/1597.html) rule.
60 66
61# Launching rust-analyzer 67## Launching rust-analyzer
62 68
63Debugging the language server can be tricky. 69Debugging the language server can be tricky.
64LSP is rather chatty, so driving it from the command line is not really feasible, driving it via VS Code requires interacting with two processes. 70LSP is rather chatty, so driving it from the command line is not really feasible, driving it via VS Code requires interacting with two processes.
65 71
66For this reason, the best way to see how rust-analyzer works is to find a relevant test and execute it. 72For this reason, the best way to see how rust-analyzer works is to **find a relevant test and execute it**.
67VS Code & Emacs include an action for running a single test. 73VS Code & Emacs include an action for running a single test.
68 74
69Launching a VS Code instance with a locally built language server is also possible. 75Launching a VS Code instance with a locally built language server is also possible.
@@ -107,12 +113,7 @@ cd editors/code
107npm ci 113npm ci
108npm run lint 114npm run lint
109``` 115```
110 116## How to ...
111# Code Style & Review Process
112
113Do see [./style.md](./style.md).
114
115# How to ...
116 117
117* ... add an assist? [#7535](https://github.com/rust-analyzer/rust-analyzer/pull/7535) 118* ... add an assist? [#7535](https://github.com/rust-analyzer/rust-analyzer/pull/7535)
118* ... add a new protocol extension? [#4569](https://github.com/rust-analyzer/rust-analyzer/pull/4569) 119* ... add a new protocol extension? [#4569](https://github.com/rust-analyzer/rust-analyzer/pull/4569)
@@ -120,33 +121,30 @@ Do see [./style.md](./style.md).
120* ... add a new completion? [#6964](https://github.com/rust-analyzer/rust-analyzer/pull/6964) 121* ... add a new completion? [#6964](https://github.com/rust-analyzer/rust-analyzer/pull/6964)
121* ... allow new syntax in the parser? [#7338](https://github.com/rust-analyzer/rust-analyzer/pull/7338) 122* ... allow new syntax in the parser? [#7338](https://github.com/rust-analyzer/rust-analyzer/pull/7338)
122 123
123# Logging 124## Logging
124 125
125Logging is done by both rust-analyzer and VS Code, so it might be tricky to 126Logging is done by both rust-analyzer and VS Code, so it might be tricky to figure out where logs go.
126figure out where logs go.
127 127
128Inside rust-analyzer, we use the standard `log` crate for logging, and 128Inside rust-analyzer, we use the standard `log` crate for logging, and `env_logger` for logging frontend.
129`env_logger` for logging frontend. By default, log goes to stderr, but the 129By default, log goes to stderr, but the stderr itself is processed by VS Code.
130stderr itself is processed by VS Code. 130`--log-file <PATH>` CLI argument allows logging to file.
131 131
132To see stderr in the running VS Code instance, go to the "Output" tab of the 132To see stderr in the running VS Code instance, go to the "Output" tab of the panel and select `rust-analyzer`.
133panel and select `rust-analyzer`. This shows `eprintln!` as well. Note that 133This shows `eprintln!` as well.
134`stdout` is used for the actual protocol, so `println!` will break things. 134Note that `stdout` is used for the actual protocol, so `println!` will break things.
135 135
136To log all communication between the server and the client, there are two choices: 136To log all communication between the server and the client, there are two choices:
137 137
138* you can log on the server side, by running something like 138* You can log on the server side, by running something like
139 ``` 139 ```
140 env RA_LOG=lsp_server=debug code . 140 env RA_LOG=lsp_server=debug code .
141 ``` 141 ```
142 142* You can log on the client side, by enabling `"rust-analyzer.trace.server": "verbose"` workspace setting.
143* you can log on the client side, by enabling `"rust-analyzer.trace.server": 143 These logs are shown in a separate tab in the output and could be used with LSP inspector.
144 "verbose"` workspace setting. These logs are shown in a separate tab in the 144 Kudos to [@DJMcNab](https://github.com/DJMcNab) for setting this awesome infra up!
145 output and could be used with LSP inspector. Kudos to
146 [@DJMcNab](https://github.com/DJMcNab) for setting this awesome infra up!
147 145
148 146
149There are also two VS Code commands which might be of interest: 147There are also several VS Code commands which might be of interest:
150 148
151* `Rust Analyzer: Status` shows some memory-usage statistics. 149* `Rust Analyzer: Status` shows some memory-usage statistics.
152 150
@@ -164,7 +162,7 @@ There are also two VS Code commands which might be of interest:
164 162
165 ![demo](https://user-images.githubusercontent.com/36276403/78225773-6636a480-74d3-11ea-9d9f-1c9d42da03b0.png) 163 ![demo](https://user-images.githubusercontent.com/36276403/78225773-6636a480-74d3-11ea-9d9f-1c9d42da03b0.png)
166 164
167# Profiling 165## Profiling
168 166
169We have a built-in hierarchical profiler, you can enable it by using `RA_PROFILE` env-var: 167We have a built-in hierarchical profiler, you can enable it by using `RA_PROFILE` env-var:
170 168
@@ -192,7 +190,9 @@ $ cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --highlight .
192$ cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --complete ../chalk/chalk-engine/src/logic.rs:94:0 190$ cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --complete ../chalk/chalk-engine/src/logic.rs:94:0
193``` 191```
194 192
195# Release Process 193Look for `fn benchmark_xxx` tests for a quick way to reproduce performance problems.
194
195## Release Process
196 196
197Release process is handled by `release`, `dist` and `promote` xtasks, `release` being the main one. 197Release process is handled by `release`, `dist` and `promote` xtasks, `release` being the main one.
198 198
@@ -229,7 +229,7 @@ Make sure to remove the new changelog post created when running `cargo xtask rel
229We release "nightly" every night automatically and promote the latest nightly to "stable" manually, every week. 229We release "nightly" every night automatically and promote the latest nightly to "stable" manually, every week.
230We don't do "patch" releases, unless something truly egregious comes up. 230We don't do "patch" releases, unless something truly egregious comes up.
231 231
232# Permissions 232## Permissions
233 233
234There are three sets of people with extra permissions: 234There are three sets of people with extra permissions:
235 235
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 36a86e78f..e74b287fb 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -178,6 +178,15 @@ $ cargo xtask install --server
178If your editor can't find the binary even though the binary is on your `$PATH`, the likely explanation is that it doesn't see the same `$PATH` as the shell, see https://github.com/rust-analyzer/rust-analyzer/issues/1811[this issue]. 178If your editor can't find the binary even though the binary is on your `$PATH`, the likely explanation is that it doesn't see the same `$PATH` as the shell, see https://github.com/rust-analyzer/rust-analyzer/issues/1811[this issue].
179On Unix, running the editor from a shell or changing the `.desktop` file to set the environment should help. 179On Unix, running the editor from a shell or changing the `.desktop` file to set the environment should help.
180 180
181==== `rustup`
182
183`rust-analyzer` is available in `rustup`, but only in the nightly toolchain:
184
185[source,bash]
186---
187$ rustup +nightly component add rust-analyzer-preview
188---
189
181==== Arch Linux 190==== Arch Linux
182 191
183The `rust-analyzer` binary can be installed from the repos or AUR (Arch User Repository): 192The `rust-analyzer` binary can be installed from the repos or AUR (Arch User Repository):
@@ -245,23 +254,10 @@ let g:LanguageClient_serverCommands = {
245 254
246==== YouCompleteMe 255==== YouCompleteMe
247 256
2481. Install YouCompleteMe by following the instructions 257Install YouCompleteMe by following the instructions
249 https://github.com/ycm-core/lsp-examples#rust-rust-analyzer[here] 258 https://github.com/ycm-core/YouCompleteMe#installation[here].
250 259
2512. Configure by adding this to your vim/neovim config file (replacing the existing Rust-specific line if it exists): 260rust-analyzer is the default in ycm, it should work out of the box.
252+
253[source,vim]
254----
255let g:ycm_language_server =
256\ [
257\ {
258\ 'name': 'rust',
259\ 'cmdline': ['rust-analyzer'],
260\ 'filetypes': ['rust'],
261\ 'project_root_files': ['Cargo.toml']
262\ }
263\ ]
264----
265 261
266==== ALE 262==== ALE
267 263