aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/base_db/src/lib.rs3
-rw-r--r--crates/hir/src/lib.rs13
-rw-r--r--crates/hir_def/src/data.rs10
-rw-r--r--crates/hir_def/src/item_tree.rs43
-rw-r--r--crates/hir_def/src/item_tree/lower.rs17
-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_ty/src/diagnostics/match_check.rs40
-rw-r--r--crates/hir_ty/src/infer/expr.rs2
-rw-r--r--crates/hir_ty/src/lower.rs12
-rw-r--r--crates/hir_ty/src/tests/traits.rs43
-rw-r--r--crates/ide/src/doc_links.rs65
-rw-r--r--crates/ide/src/goto_definition.rs15
-rw-r--r--crates/ide/src/hover.rs7
-rw-r--r--crates/ide/src/move_item.rs268
-rw-r--r--crates/ide/src/references/rename.rs2
-rw-r--r--crates/ide/src/runnables.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs35
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs32
-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.html4
-rw-r--r--crates/ide_assists/src/handlers/generate_default_from_new.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_is_empty_from_len.rs2
-rw-r--r--crates/ide_assists/src/utils.rs4
-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/qualified_path.rs2
-rw-r--r--crates/ide_db/src/defs.rs41
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs2
-rw-r--r--crates/profile/src/google_cpu_profiler.rs19
-rw-r--r--crates/profile/src/hprof.rs87
-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.rs1
-rw-r--r--docs/dev/README.md73
-rw-r--r--docs/user/manual.adoc19
45 files changed, 780 insertions, 510 deletions
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_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/item_tree.rs b/crates/hir_def/src/item_tree.rs
index ca0048b16..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
@@ -147,6 +147,7 @@ impl ItemTree {
147 vis, 147 vis,
148 generics, 148 generics,
149 type_refs, 149 type_refs,
150 trait_refs,
150 inner_items, 151 inner_items,
151 } = &mut **data; 152 } = &mut **data;
152 153
@@ -173,6 +174,7 @@ impl ItemTree {
173 generics.arena.shrink_to_fit(); 174 generics.arena.shrink_to_fit();
174 type_refs.arena.shrink_to_fit(); 175 type_refs.arena.shrink_to_fit();
175 type_refs.map.shrink_to_fit(); 176 type_refs.map.shrink_to_fit();
177 trait_refs.map.shrink_to_fit();
176 178
177 inner_items.shrink_to_fit(); 179 inner_items.shrink_to_fit();
178 } 180 }
@@ -295,6 +297,32 @@ impl TypeRefStorage {
295 } 297 }
296} 298}
297 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
298#[derive(Default, Debug, Eq, PartialEq)] 326#[derive(Default, Debug, Eq, PartialEq)]
299struct ItemTreeData { 327struct ItemTreeData {
300 imports: Arena<Import>, 328 imports: Arena<Import>,
@@ -319,6 +347,7 @@ struct ItemTreeData {
319 vis: ItemVisibilities, 347 vis: ItemVisibilities,
320 generics: GenericParamsStorage, 348 generics: GenericParamsStorage,
321 type_refs: TypeRefStorage, 349 type_refs: TypeRefStorage,
350 trait_refs: TraitRefStorage,
322 351
323 inner_items: FxHashMap<FileAstId<ast::BlockExpr>, SmallVec<[ModItem; 1]>>, 352 inner_items: FxHashMap<FileAstId<ast::BlockExpr>, SmallVec<[ModItem; 1]>>,
324} 353}
@@ -556,6 +585,14 @@ impl Index<Idx<TypeRef>> for ItemTree {
556 } 585 }
557} 586}
558 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
559impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree { 596impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
560 type Output = N; 597 type Output = N;
561 fn index(&self, id: FileItemTreeId<N>) -> &N { 598 fn index(&self, id: FileItemTreeId<N>) -> &N {
@@ -692,8 +729,8 @@ pub struct Trait {
692#[derive(Debug, Clone, Eq, PartialEq)] 729#[derive(Debug, Clone, Eq, PartialEq)]
693pub struct Impl { 730pub struct Impl {
694 pub generic_params: GenericParamsId, 731 pub generic_params: GenericParamsId,
695 pub target_trait: Option<Idx<TypeRef>>, 732 pub target_trait: Option<Idx<TraitRef>>,
696 pub target_type: Idx<TypeRef>, 733 pub self_ty: Idx<TypeRef>,
697 pub is_negative: bool, 734 pub is_negative: bool,
698 pub items: Box<[AssocItem]>, 735 pub items: Box<[AssocItem]>,
699 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 3f558edd8..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::*;
@@ -536,8 +536,11 @@ impl Ctx {
536 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>> {
537 let generic_params = 537 let generic_params =
538 self.lower_generic_params_and_inner_items(GenericsOwner::Impl, impl_def); 538 self.lower_generic_params_and_inner_items(GenericsOwner::Impl, impl_def);
539 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
540 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()?);
541 let is_negative = impl_def.excl_token().is_some(); 544 let is_negative = impl_def.excl_token().is_some();
542 545
543 // 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.
@@ -554,7 +557,7 @@ impl Ctx {
554 }) 557 })
555 .collect(); 558 .collect();
556 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);
557 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 };
558 Some(id(self.data().impls.alloc(res))) 561 Some(id(self.data().impls.alloc(res)))
559 } 562 }
560 563
@@ -740,10 +743,16 @@ impl Ctx {
740 self.data().vis.alloc(vis) 743 self.data().vis.alloc(vis)
741 } 744 }
742 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
743 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> {
744 let tyref = TypeRef::from_ast(&self.body_ctx, type_ref.clone()); 752 let tyref = TypeRef::from_ast(&self.body_ctx, type_ref.clone());
745 self.data().type_refs.intern(tyref) 753 self.data().type_refs.intern(tyref)
746 } 754 }
755
747 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> {
748 match type_ref.map(|ty| self.lower_type_ref(&ty)) { 757 match type_ref.map(|ty| self.lower_type_ref(&ty)) {
749 Some(it) => it, 758 Some(it) => it,
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_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 e6ede05ca..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(),
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/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/doc_links.rs b/crates/ide/src/doc_links.rs
index 9301cdeff..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>)> {
@@ -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/goto_definition.rs b/crates/ide/src/goto_definition.rs
index d66981fcb..8574d1e3f 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -920,6 +920,21 @@ fn f() -> impl Iterator<Item$0 = u8> {}
920 } 920 }
921 921
922 #[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]
923 fn goto_def_for_assoc_ty_in_path_multiple() { 938 fn goto_def_for_assoc_ty_in_path_multiple() {
924 check( 939 check(
925 r#" 940 r#"
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 2e477fd0a..5f9edb476 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -197,7 +197,7 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov
197 let adt = match def { 197 let adt = match def {
198 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),
199 Definition::ModuleDef(ModuleDef::Adt(it)) => Some(it), 199 Definition::ModuleDef(ModuleDef::Adt(it)) => Some(it),
200 Definition::SelfType(it) => it.target_ty(db).as_adt(), 200 Definition::SelfType(it) => it.self_ty(db).as_adt(),
201 _ => None, 201 _ => None,
202 }?; 202 }?;
203 adt.try_to_nav(db).map(to_action) 203 adt.try_to_nav(db).map(to_action)
@@ -320,7 +320,7 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
320 Definition::ModuleDef(md) => match md { 320 Definition::ModuleDef(md) => match md {
321 ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) { 321 ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) {
322 AssocItemContainer::Trait(t) => Some(t.name(db)), 322 AssocItemContainer::Trait(t) => Some(t.name(db)),
323 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)),
324 }, 324 },
325 ModuleDef::Variant(e) => Some(e.parent_enum(db).name(db)), 325 ModuleDef::Variant(e) => Some(e.parent_enum(db).name(db)),
326 _ => None, 326 _ => None,
@@ -378,7 +378,7 @@ fn hover_for_definition(
378 }, 378 },
379 Definition::Local(it) => hover_for_local(it, db), 379 Definition::Local(it) => hover_for_local(it, db),
380 Definition::SelfType(impl_def) => { 380 Definition::SelfType(impl_def) => {
381 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))
382 } 382 }
383 Definition::GenericParam(it) => from_hir_fmt(db, it, None), 383 Definition::GenericParam(it) => from_hir_fmt(db, it, None),
384 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))), 384 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))),
@@ -472,6 +472,7 @@ fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module>
472 472
473fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 473fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
474 return tokens.max_by_key(priority); 474 return tokens.max_by_key(priority);
475
475 fn priority(n: &SyntaxToken) -> usize { 476 fn priority(n: &SyntaxToken) -> usize {
476 match n.kind() { 477 match n.kind() {
477 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,
diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs
index eab2687e3..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,
@@ -33,14 +35,19 @@ pub(crate) fn move_item(
33 let sema = Semantics::new(db); 35 let sema = Semantics::new(db);
34 let file = sema.parse(range.file_id); 36 let file = sema.parse(range.file_id);
35 37
36 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
37 find_ancestors(item, direction, range.range) 44 find_ancestors(item, direction, range.range)
38} 45}
39 46
40fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -> Option<TextEdit> { 47fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -> Option<TextEdit> {
41 let root = match item { 48 let root = match item {
42 NodeOrToken::Node(node) => node, 49 SyntaxElement::Node(node) => node,
43 NodeOrToken::Token(token) => token.parent()?, 50 SyntaxElement::Token(token) => token.parent()?,
44 }; 51 };
45 52
46 let movable = [ 53 let movable = [
@@ -53,6 +60,11 @@ fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -
53 SyntaxKind::PARAM, 60 SyntaxKind::PARAM,
54 SyntaxKind::LET_STMT, 61 SyntaxKind::LET_STMT,
55 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,
56 SyntaxKind::MATCH_EXPR, 68 SyntaxKind::MATCH_EXPR,
57 SyntaxKind::MACRO_CALL, 69 SyntaxKind::MACRO_CALL,
58 SyntaxKind::TYPE_ALIAS, 70 SyntaxKind::TYPE_ALIAS,
@@ -85,11 +97,11 @@ fn move_in_direction(
85) -> Option<TextEdit> { 97) -> Option<TextEdit> {
86 match_ast! { 98 match_ast! {
87 match node { 99 match node {
88 ast::ArgList(it) => swap_sibling_in_list(it.args(), range, direction), 100 ast::ArgList(it) => swap_sibling_in_list(node, it.args(), range, direction),
89 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),
90 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),
91 ast::VariantList(it) => swap_sibling_in_list(it.variants(), range, direction), 103 ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction),
92 ast::TypeBoundList(it) => swap_sibling_in_list(it.bounds(), range, direction), 104 ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction),
93 _ => Some(replace_nodes(node, &match direction { 105 _ => Some(replace_nodes(node, &match direction {
94 Direction::Up => node.prev_sibling(), 106 Direction::Up => node.prev_sibling(),
95 Direction::Down => node.next_sibling(), 107 Direction::Down => node.next_sibling(),
@@ -99,19 +111,27 @@ fn move_in_direction(
99} 111}
100 112
101fn 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,
102 list: I, 115 list: I,
103 range: TextRange, 116 range: TextRange,
104 direction: Direction, 117 direction: Direction,
105) -> Option<TextEdit> { 118) -> Option<TextEdit> {
106 let (l, r) = list 119 let list_lookup = list
107 .tuple_windows() 120 .tuple_windows()
108 .filter(|(l, r)| match direction { 121 .filter(|(l, r)| match direction {
109 Direction::Up => r.syntax().text_range().contains_range(range), 122 Direction::Up => r.syntax().text_range().contains_range(range),
110 Direction::Down => l.syntax().text_range().contains_range(range), 123 Direction::Down => l.syntax().text_range().contains_range(range),
111 }) 124 })
112 .next()?; 125 .next();
113 126
114 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 }
115} 135}
116 136
117fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit { 137fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit {
@@ -123,6 +143,18 @@ fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit {
123 edit.finish() 143 edit.finish()
124} 144}
125 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
126#[cfg(test)] 158#[cfg(test)]
127mod tests { 159mod tests {
128 use crate::fixture; 160 use crate::fixture;
@@ -267,6 +299,107 @@ fn main() {
267 "#]], 299 "#]],
268 Direction::Up, 300 Direction::Up,
269 ); 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 );
270 } 403 }
271 404
272 #[test] 405 #[test]
@@ -617,6 +750,115 @@ fn test() {
617 } 750 }
618 751
619 #[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]
620 fn handles_empty_file() { 862 fn handles_empty_file() {
621 check(r#"$0$0"#, expect![[r#""#]], Direction::Up); 863 check(r#"$0$0"#, expect![[r#""#]], Direction::Up);
622 } 864 }
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index e6f7fa066..2408a0181 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -309,7 +309,7 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe
309 hir::AssocItemContainer::Impl(impl_) => impl_, 309 hir::AssocItemContainer::Impl(impl_) => impl_,
310 }; 310 };
311 let first_param_ty = first_param.ty(); 311 let first_param_ty = first_param.ty();
312 let impl_ty = impl_.target_ty(sema.db); 312 let impl_ty = impl_.self_ty(sema.db);
313 let (ty, self_param) = if impl_ty.remove_ref().is_some() { 313 let (ty, self_param) = if impl_ty.remove_ref().is_some() {
314 // 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
315 (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 9435dfc06..3eb9e27ee 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -299,7 +299,7 @@ fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Op
299 // FIXME: this also looks very wrong 299 // FIXME: this also looks very wrong
300 if let Some(assoc_def) = assoc_def { 300 if let Some(assoc_def) = assoc_def {
301 if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) { 301 if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) {
302 let ty = imp.target_ty(sema.db); 302 let ty = imp.self_ty(sema.db);
303 if let Some(adt) = ty.as_adt() { 303 if let Some(adt) = ty.as_adt() {
304 let name = adt.name(sema.db); 304 let name = adt.name(sema.db);
305 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/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 963c3fb59..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};
@@ -190,7 +191,7 @@ pub(super) fn doc_comment(
190 extract_definitions_from_markdown(line) 191 extract_definitions_from_markdown(line)
191 .into_iter() 192 .into_iter()
192 .filter_map(|(range, link, ns)| { 193 .filter_map(|(range, link, ns)| {
193 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))
194 }) 195 })
195 .map(|(Range { start, end }, def)| { 196 .map(|(Range { start, end }, def)| {
196 ( 197 (
@@ -283,33 +284,6 @@ fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::Stri
283 } 284 }
284} 285}
285 286
286fn validate_intra_doc_link(
287 db: &RootDatabase,
288 def: &Definition,
289 link: &str,
290 ns: Option<hir::Namespace>,
291) -> Option<hir::ModuleDef> {
292 match def {
293 Definition::ModuleDef(def) => match def {
294 hir::ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns),
295 hir::ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns),
296 hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns),
297 hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns),
298 hir::ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns),
299 hir::ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns),
300 hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns),
301 hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns),
302 hir::ModuleDef::BuiltinType(_) => None,
303 },
304 Definition::Macro(it) => it.resolve_doc_path(db, &link, ns),
305 Definition::Field(it) => it.resolve_doc_path(db, &link, ns),
306 Definition::SelfType(_)
307 | Definition::Local(_)
308 | Definition::GenericParam(_)
309 | Definition::Label(_) => None,
310 }
311}
312
313fn module_def_to_hl_tag(def: hir::ModuleDef) -> HlTag { 287fn module_def_to_hl_tag(def: hir::ModuleDef) -> HlTag {
314 let symbol = match def { 288 let symbol = match def {
315 hir::ModuleDef::Module(_) => SymbolKind::Module, 289 hir::ModuleDef::Module(_) => SymbolKind::Module,
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 1eaa7b75b..973173254 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -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>
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_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/utils.rs b/crates/ide_assists/src/utils.rs
index 5f630ec75..d67524937 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -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
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/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_db/src/defs.rs b/crates/ide_db/src/defs.rs
index 0d9808d24..de0dc2a40 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -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 3deb0d159..8ce648367 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -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/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 014e906e6..5fdb37206 100644
--- a/crates/profile/src/hprof.rs
+++ b/crates/profile/src/hprof.rs
@@ -69,6 +69,20 @@ pub fn span(label: Label) -> ProfileSpan {
69 } 69 }
70} 70}
71 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
72pub struct ProfileSpan(Option<ProfilerImpl>); 86pub struct ProfileSpan(Option<ProfilerImpl>);
73 87
74struct ProfilerImpl { 88struct ProfilerImpl {
@@ -92,6 +106,28 @@ impl Drop for ProfilerImpl {
92 } 106 }
93} 107}
94 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 {
124 fn drop(&mut self) {
125 if self.enabled {
126 with_profile_stack(|it| it.heartbeats(false))
127 }
128 }
129}
130
95static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false); 131static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);
96static FILTER: Lazy<RwLock<Filter>> = Lazy::new(Default::default); 132static FILTER: Lazy<RwLock<Filter>> = Lazy::new(Default::default);
97 133
@@ -105,6 +141,7 @@ struct Filter {
105 depth: usize, 141 depth: usize,
106 allowed: HashSet<String>, 142 allowed: HashSet<String>,
107 longer_than: Duration, 143 longer_than: Duration,
144 heartbeat_longer_than: Duration,
108 version: usize, 145 version: usize,
109} 146}
110 147
@@ -121,6 +158,7 @@ impl Filter {
121 } else { 158 } else {
122 Duration::new(0, 0) 159 Duration::new(0, 0)
123 }; 160 };
161 let heartbeat_longer_than = longer_than;
124 162
125 let depth = if let Some(idx) = spec.rfind('@') { 163 let depth = if let Some(idx) = spec.rfind('@') {
126 let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth"); 164 let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth");
@@ -131,7 +169,7 @@ impl Filter {
131 }; 169 };
132 let allowed = 170 let allowed =
133 if spec == "*" { HashSet::new() } else { spec.split('|').map(String::from).collect() }; 171 if spec == "*" { HashSet::new() } else { spec.split('|').map(String::from).collect() };
134 Filter { depth, allowed, longer_than, version: 0 } 172 Filter { depth, allowed, longer_than, heartbeat_longer_than, version: 0 }
135 } 173 }
136 174
137 fn install(mut self) { 175 fn install(mut self) {
@@ -143,9 +181,15 @@ impl Filter {
143} 181}
144 182
145struct ProfileStack { 183struct ProfileStack {
146 starts: Vec<Instant>, 184 frames: Vec<Frame>,
147 filter: Filter, 185 filter: Filter,
148 messages: Tree<Message>, 186 messages: Tree<Message>,
187 heartbeats: bool,
188}
189
190struct Frame {
191 t: Instant,
192 heartbeats: u32,
149} 193}
150 194
151#[derive(Default)] 195#[derive(Default)]
@@ -157,35 +201,49 @@ struct Message {
157 201
158impl ProfileStack { 202impl ProfileStack {
159 fn new() -> ProfileStack { 203 fn new() -> ProfileStack {
160 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 }
161 } 210 }
162 211
163 fn push(&mut self, label: Label) -> bool { 212 fn push(&mut self, label: Label) -> bool {
164 if self.starts.is_empty() { 213 if self.frames.is_empty() {
165 if let Ok(f) = FILTER.try_read() { 214 if let Ok(f) = FILTER.try_read() {
166 if f.version > self.filter.version { 215 if f.version > self.filter.version {
167 self.filter = f.clone(); 216 self.filter = f.clone();
168 } 217 }
169 }; 218 };
170 } 219 }
171 if self.starts.len() > self.filter.depth { 220 if self.frames.len() > self.filter.depth {
172 return false; 221 return false;
173 } 222 }
174 let allowed = &self.filter.allowed; 223 let allowed = &self.filter.allowed;
175 if self.starts.is_empty() && !allowed.is_empty() && !allowed.contains(label) { 224 if self.frames.is_empty() && !allowed.is_empty() && !allowed.contains(label) {
176 return false; 225 return false;
177 } 226 }
178 227
179 self.starts.push(Instant::now()); 228 self.frames.push(Frame { t: Instant::now(), heartbeats: 0 });
180 self.messages.start(); 229 self.messages.start();
181 true 230 true
182 } 231 }
183 232
184 fn pop(&mut self, label: Label, detail: Option<String>) { 233 fn pop(&mut self, label: Label, detail: Option<String>) {
185 let start = self.starts.pop().unwrap(); 234 let frame = self.frames.pop().unwrap();
186 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
187 self.messages.finish(Message { duration, label, detail }); 245 self.messages.finish(Message { duration, label, detail });
188 if self.starts.is_empty() { 246 if self.frames.is_empty() {
189 let longer_than = self.filter.longer_than; 247 let longer_than = self.filter.longer_than;
190 // Convert to millis for comparison to avoid problems with rounding 248 // Convert to millis for comparison to avoid problems with rounding
191 // (otherwise we could print `0ms` despite user's `>0` filter when 249 // (otherwise we could print `0ms` despite user's `>0` filter when
@@ -198,6 +256,15 @@ impl ProfileStack {
198 self.messages.clear(); 256 self.messages.clear();
199 } 257 }
200 } 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 }
201} 268}
202 269
203fn 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 e297a72e6..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;
diff --git a/docs/dev/README.md b/docs/dev/README.md
index dcbab1a1d..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,18 +121,17 @@ 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
@@ -139,17 +139,12 @@ To log all communication between the server and the client, there are two choice
139 ``` 139 ```
140 env RA_LOG=lsp_server=debug code . 140 env RA_LOG=lsp_server=debug code .
141 ``` 141 ```
142* You can log on the client side, by enabling `"rust-analyzer.trace.server": "verbose"` workspace setting.
143 These logs are shown in a separate tab in the output and could be used with LSP inspector.
144 Kudos to [@DJMcNab](https://github.com/DJMcNab) for setting this awesome infra up!
142 145
143 By default, logs go to stderr, `--log-file <PATH>` CLI argument overrides
144 that.
145 146
146* You can log on the client side, by enabling `"rust-analyzer.trace.server": 147There are also several VS Code commands which might be of interest:
147 "verbose"` workspace setting. These logs are shown in a separate tab in the
148 output and could be used with LSP inspector. Kudos to
149 [@DJMcNab](https://github.com/DJMcNab) for setting this awesome infra up!
150
151
152There are also two VS Code commands which might be of interest:
153 148
154* `Rust Analyzer: Status` shows some memory-usage statistics. 149* `Rust Analyzer: Status` shows some memory-usage statistics.
155 150
@@ -167,7 +162,7 @@ There are also two VS Code commands which might be of interest:
167 162
168 ![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)
169 164
170# Profiling 165## Profiling
171 166
172We 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:
173 168
@@ -195,7 +190,9 @@ $ cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --highlight .
195$ 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
196``` 191```
197 192
198# Release Process 193Look for `fn benchmark_xxx` tests for a quick way to reproduce performance problems.
194
195## Release Process
199 196
200Release 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.
201 198
@@ -232,7 +229,7 @@ Make sure to remove the new changelog post created when running `cargo xtask rel
232We 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.
233We don't do "patch" releases, unless something truly egregious comes up. 230We don't do "patch" releases, unless something truly egregious comes up.
234 231
235# Permissions 232## Permissions
236 233
237There are three sets of people with extra permissions: 234There are three sets of people with extra permissions:
238 235
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index b1beeb883..e74b287fb 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -254,23 +254,10 @@ let g:LanguageClient_serverCommands = {
254 254
255==== YouCompleteMe 255==== YouCompleteMe
256 256
2571. Install YouCompleteMe by following the instructions 257Install YouCompleteMe by following the instructions
258 https://github.com/ycm-core/lsp-examples#rust-rust-analyzer[here] 258 https://github.com/ycm-core/YouCompleteMe#installation[here].
259 259
2602. 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.
261+
262[source,vim]
263----
264let g:ycm_language_server =
265\ [
266\ {
267\ 'name': 'rust',
268\ 'cmdline': ['rust-analyzer'],
269\ 'filetypes': ['rust'],
270\ 'project_root_files': ['Cargo.toml']
271\ }
272\ ]
273----
274 261
275==== ALE 262==== ALE
276 263