aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock4
-rw-r--r--crates/ra_db/src/input.rs35
-rw-r--r--crates/ra_hir/src/db.rs2
-rw-r--r--crates/ra_hir_def/src/lib.rs2
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs2
-rw-r--r--crates/ra_hir_ty/src/db.rs15
-rw-r--r--crates/ra_hir_ty/src/infer/expr.rs81
-rw-r--r--crates/ra_hir_ty/src/lower.rs3
-rw-r--r--crates/ra_hir_ty/src/method_resolution.rs69
-rw-r--r--crates/ra_hir_ty/src/tests/regression.rs91
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs223
-rw-r--r--crates/ra_hir_ty/src/traits.rs43
-rw-r--r--crates/ra_hir_ty/src/traits/builtin.rs20
-rw-r--r--crates/ra_hir_ty/src/traits/chalk.rs28
-rw-r--r--crates/ra_ide_db/src/change.rs2
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/global_state.rs8
-rw-r--r--crates/rust-analyzer/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/main_loop.rs153
-rw-r--r--crates/rust-analyzer/src/main_loop/pending_requests.rs75
-rw-r--r--crates/rust-analyzer/src/main_loop/request_metrics.rs37
-rw-r--r--editors/code/src/main.ts16
-rw-r--r--editors/code/src/net.ts55
23 files changed, 695 insertions, 273 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f6d5b900f..e5cf487a5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -640,9 +640,9 @@ dependencies = [
640 640
641[[package]] 641[[package]]
642name = "lsp-server" 642name = "lsp-server"
643version = "0.3.2" 643version = "0.3.3"
644source = "registry+https://github.com/rust-lang/crates.io-index" 644source = "registry+https://github.com/rust-lang/crates.io-index"
645checksum = "dccec31bfd027ac0dd288a78e19005fd89624d9099456e284b5241316a6c3072" 645checksum = "53b4ace8ebe5d2aff3687ce0ed507f6020d6a47a7de2b0d3d664ea237ffb0c62"
646dependencies = [ 646dependencies = [
647 "crossbeam-channel", 647 "crossbeam-channel",
648 "log", 648 "log",
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index bf26048f2..e6af99035 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -254,12 +254,12 @@ impl CrateGraph {
254 return false; 254 return false;
255 } 255 }
256 256
257 if target == from {
258 return true;
259 }
260
257 for dep in &self[from].dependencies { 261 for dep in &self[from].dependencies {
258 let crate_id = dep.crate_id; 262 let crate_id = dep.crate_id;
259 if crate_id == target {
260 return true;
261 }
262
263 if self.dfs_find(target, crate_id, visited) { 263 if self.dfs_find(target, crate_id, visited) {
264 return true; 264 return true;
265 } 265 }
@@ -369,7 +369,7 @@ mod tests {
369 use super::{CfgOptions, CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId}; 369 use super::{CfgOptions, CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId};
370 370
371 #[test] 371 #[test]
372 fn it_should_panic_because_of_cycle_dependencies() { 372 fn detect_cyclic_dependency_indirect() {
373 let mut graph = CrateGraph::default(); 373 let mut graph = CrateGraph::default();
374 let crate1 = graph.add_crate_root( 374 let crate1 = graph.add_crate_root(
375 FileId(1u32), 375 FileId(1u32),
@@ -404,6 +404,31 @@ mod tests {
404 } 404 }
405 405
406 #[test] 406 #[test]
407 fn detect_cyclic_dependency_direct() {
408 let mut graph = CrateGraph::default();
409 let crate1 = graph.add_crate_root(
410 FileId(1u32),
411 Edition2018,
412 None,
413 CfgOptions::default(),
414 Env::default(),
415 Default::default(),
416 Default::default(),
417 );
418 let crate2 = graph.add_crate_root(
419 FileId(2u32),
420 Edition2018,
421 None,
422 CfgOptions::default(),
423 Env::default(),
424 Default::default(),
425 Default::default(),
426 );
427 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
428 assert!(graph.add_dep(crate2, CrateName::new("crate2").unwrap(), crate2).is_err());
429 }
430
431 #[test]
407 fn it_works() { 432 fn it_works() {
408 let mut graph = CrateGraph::default(); 433 let mut graph = CrateGraph::default();
409 let crate1 = graph.add_crate_root( 434 let crate1 = graph.add_crate_root(
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index b6b665de1..b25dac28e 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -16,7 +16,7 @@ pub use hir_expand::db::{
16pub use hir_ty::db::{ 16pub use hir_ty::db::{
17 AssociatedTyDataQuery, AssociatedTyValueQuery, CallableItemSignatureQuery, FieldTypesQuery, 17 AssociatedTyDataQuery, AssociatedTyValueQuery, CallableItemSignatureQuery, FieldTypesQuery,
18 GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, 18 GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase,
19 HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, ImplsForTraitQuery, 19 HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, ImplsFromDepsQuery,
20 ImplsInCrateQuery, InferQueryQuery, InternAssocTyValueQuery, InternChalkImplQuery, 20 ImplsInCrateQuery, InferQueryQuery, InternAssocTyValueQuery, InternChalkImplQuery,
21 InternTypeCtorQuery, InternTypeParamIdQuery, ReturnTypeImplTraitsQuery, StructDatumQuery, 21 InternTypeCtorQuery, InternTypeParamIdQuery, ReturnTypeImplTraitsQuery, StructDatumQuery,
22 TraitDatumQuery, TraitSolveQuery, TyQuery, ValueTyQuery, 22 TraitDatumQuery, TraitSolveQuery, TyQuery, ValueTyQuery,
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index edc59e5a8..af2a717c9 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -159,7 +159,7 @@ pub struct TypeAliasId(salsa::InternId);
159type TypeAliasLoc = AssocItemLoc<ast::TypeAliasDef>; 159type TypeAliasLoc = AssocItemLoc<ast::TypeAliasDef>;
160impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias); 160impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias);
161 161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 162#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
163pub struct ImplId(salsa::InternId); 163pub struct ImplId(salsa::InternId);
164type ImplLoc = ItemLoc<ast::ImplDef>; 164type ImplLoc = ItemLoc<ast::ImplDef>;
165impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl); 165impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl);
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index 77baa4c69..b8f6aac8f 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -36,8 +36,8 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> Cr
36 36
37 // populate external prelude 37 // populate external prelude
38 for dep in &crate_graph[def_map.krate].dependencies { 38 for dep in &crate_graph[def_map.krate].dependencies {
39 let dep_def_map = db.crate_def_map(dep.crate_id);
40 log::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id); 39 log::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id);
40 let dep_def_map = db.crate_def_map(dep.crate_id);
41 def_map.extern_prelude.insert( 41 def_map.extern_prelude.insert(
42 dep.as_name(), 42 dep.as_name(),
43 ModuleId { krate: dep.crate_id, local_id: dep_def_map.root }.into(), 43 ModuleId { krate: dep.crate_id, local_id: dep_def_map.root }.into(),
diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs
index bf71d38d6..7889b8d2c 100644
--- a/crates/ra_hir_ty/src/db.rs
+++ b/crates/ra_hir_ty/src/db.rs
@@ -3,15 +3,15 @@
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use hir_def::{ 5use hir_def::{
6 db::DefDatabase, DefWithBodyId, FunctionId, GenericDefId, ImplId, LocalFieldId, TraitId, 6 db::DefDatabase, DefWithBodyId, FunctionId, GenericDefId, ImplId, LocalFieldId, TypeParamId,
7 TypeParamId, VariantId, 7 VariantId,
8}; 8};
9use ra_arena::map::ArenaMap; 9use ra_arena::map::ArenaMap;
10use ra_db::{impl_intern_key, salsa, CrateId, Upcast}; 10use ra_db::{impl_intern_key, salsa, CrateId, Upcast};
11use ra_prof::profile; 11use ra_prof::profile;
12 12
13use crate::{ 13use crate::{
14 method_resolution::{CrateImplDefs, TyFingerprint}, 14 method_resolution::CrateImplDefs,
15 traits::{chalk, AssocTyValue, Impl}, 15 traits::{chalk, AssocTyValue, Impl},
16 Binders, CallableDef, GenericPredicate, InferenceResult, OpaqueTyId, PolyFnSig, 16 Binders, CallableDef, GenericPredicate, InferenceResult, OpaqueTyId, PolyFnSig,
17 ReturnTypeImplTraits, Substs, TraitRef, Ty, TyDefId, TypeCtor, ValueTyDefId, 17 ReturnTypeImplTraits, Substs, TraitRef, Ty, TyDefId, TypeCtor, ValueTyDefId,
@@ -70,13 +70,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
70 #[salsa::invoke(crate::method_resolution::CrateImplDefs::impls_in_crate_query)] 70 #[salsa::invoke(crate::method_resolution::CrateImplDefs::impls_in_crate_query)]
71 fn impls_in_crate(&self, krate: CrateId) -> Arc<CrateImplDefs>; 71 fn impls_in_crate(&self, krate: CrateId) -> Arc<CrateImplDefs>;
72 72
73 #[salsa::invoke(crate::traits::impls_for_trait_query)] 73 #[salsa::invoke(crate::method_resolution::CrateImplDefs::impls_from_deps_query)]
74 fn impls_for_trait( 74 fn impls_from_deps(&self, krate: CrateId) -> Arc<CrateImplDefs>;
75 &self,
76 krate: CrateId,
77 trait_: TraitId,
78 self_ty_fp: Option<TyFingerprint>,
79 ) -> Arc<[ImplId]>;
80 75
81 // Interned IDs for Chalk integration 76 // Interned IDs for Chalk integration
82 #[salsa::interned] 77 #[salsa::interned]
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 9fd310f69..a9565a58d 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -10,12 +10,12 @@ use hir_def::{
10 resolver::resolver_for_expr, 10 resolver::resolver_for_expr,
11 AdtId, AssocContainerId, FieldId, Lookup, 11 AdtId, AssocContainerId, FieldId, Lookup,
12}; 12};
13use hir_expand::name::Name; 13use hir_expand::name::{name, Name};
14use ra_syntax::ast::RangeOp; 14use ra_syntax::ast::RangeOp;
15 15
16use crate::{ 16use crate::{
17 autoderef, method_resolution, op, 17 autoderef, method_resolution, op,
18 traits::InEnvironment, 18 traits::{FnTrait, InEnvironment},
19 utils::{generics, variant_data, Generics}, 19 utils::{generics, variant_data, Generics},
20 ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs, 20 ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs,
21 TraitRef, Ty, TypeCtor, 21 TraitRef, Ty, TypeCtor,
@@ -63,6 +63,58 @@ impl<'a> InferenceContext<'a> {
63 self.resolve_ty_as_possible(ty) 63 self.resolve_ty_as_possible(ty)
64 } 64 }
65 65
66 fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> {
67 let krate = self.resolver.krate()?;
68 let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
69 let output_assoc_type =
70 self.db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
71 let generic_params = generics(self.db.upcast(), fn_once_trait.into());
72 if generic_params.len() != 2 {
73 return None;
74 }
75
76 let mut param_builder = Substs::builder(num_args);
77 let mut arg_tys = vec![];
78 for _ in 0..num_args {
79 let arg = self.table.new_type_var();
80 param_builder = param_builder.push(arg.clone());
81 arg_tys.push(arg);
82 }
83 let parameters = param_builder.build();
84 let arg_ty = Ty::Apply(ApplicationTy {
85 ctor: TypeCtor::Tuple { cardinality: num_args as u16 },
86 parameters,
87 });
88 let substs = Substs::build_for_generics(&generic_params)
89 .push(ty.clone())
90 .push(arg_ty.clone())
91 .build();
92
93 let trait_env = Arc::clone(&self.trait_env);
94 let implements_fn_trait =
95 Obligation::Trait(TraitRef { trait_: fn_once_trait, substs: substs.clone() });
96 let goal = self.canonicalizer().canonicalize_obligation(InEnvironment {
97 value: implements_fn_trait.clone(),
98 environment: trait_env,
99 });
100 if self.db.trait_solve(krate, goal.value).is_some() {
101 self.obligations.push(implements_fn_trait);
102 let output_proj_ty =
103 crate::ProjectionTy { associated_ty: output_assoc_type, parameters: substs };
104 let return_ty = self.normalize_projection_ty(output_proj_ty);
105 Some((arg_tys, return_ty))
106 } else {
107 None
108 }
109 }
110
111 pub fn callable_sig(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> {
112 match ty.callable_sig(self.db) {
113 Some(sig) => Some((sig.params().to_vec(), sig.ret().clone())),
114 None => self.callable_sig_from_fn_trait(ty, num_args),
115 }
116 }
117
66 fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { 118 fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
67 let body = Arc::clone(&self.body); // avoid borrow checker problem 119 let body = Arc::clone(&self.body); // avoid borrow checker problem
68 let ty = match &body[tgt_expr] { 120 let ty = match &body[tgt_expr] {
@@ -198,14 +250,23 @@ impl<'a> InferenceContext<'a> {
198 } 250 }
199 Expr::Call { callee, args } => { 251 Expr::Call { callee, args } => {
200 let callee_ty = self.infer_expr(*callee, &Expectation::none()); 252 let callee_ty = self.infer_expr(*callee, &Expectation::none());
201 let (param_tys, ret_ty) = match callee_ty.callable_sig(self.db) { 253 let canonicalized = self.canonicalizer().canonicalize_ty(callee_ty.clone());
202 Some(sig) => (sig.params().to_vec(), sig.ret().clone()), 254 let mut derefs = autoderef(
203 None => { 255 self.db,
204 // Not callable 256 self.resolver.krate(),
205 // FIXME: report an error 257 InEnvironment {
206 (Vec::new(), Ty::Unknown) 258 value: canonicalized.value.clone(),
207 } 259 environment: self.trait_env.clone(),
208 }; 260 },
261 );
262 let (param_tys, ret_ty): (Vec<Ty>, Ty) = derefs
263 .find_map(|callee_deref_ty| {
264 self.callable_sig(
265 &canonicalized.decanonicalize_ty(callee_deref_ty.value),
266 args.len(),
267 )
268 })
269 .unwrap_or((Vec::new(), Ty::Unknown));
209 self.register_obligations_for_call(&callee_ty); 270 self.register_obligations_for_call(&callee_ty);
210 self.check_call_arguments(args, &param_tys); 271 self.check_call_arguments(args, &param_tys);
211 self.normalize_associated_types_in(ret_ty) 272 self.normalize_associated_types_in(ret_ty)
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs
index 7a7fcb0ab..d5154f436 100644
--- a/crates/ra_hir_ty/src/lower.rs
+++ b/crates/ra_hir_ty/src/lower.rs
@@ -467,6 +467,9 @@ impl Ty {
467 } 467 }
468 TypeParamLoweringMode::Variable => t.substs.clone(), 468 TypeParamLoweringMode::Variable => t.substs.clone(),
469 }; 469 };
470 // We need to shift in the bound vars, since
471 // associated_type_shorthand_candidates does not do that
472 let substs = substs.shift_bound_vars(ctx.in_binders);
470 // FIXME handle type parameters on the segment 473 // FIXME handle type parameters on the segment
471 return Some(Ty::Projection(ProjectionTy { 474 return Some(Ty::Projection(ProjectionTy {
472 associated_ty, 475 associated_ty,
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
index e83b39456..ed638c195 100644
--- a/crates/ra_hir_ty/src/method_resolution.rs
+++ b/crates/ra_hir_ty/src/method_resolution.rs
@@ -38,18 +38,53 @@ impl TyFingerprint {
38 } 38 }
39} 39}
40 40
41/// A queryable and mergeable collection of impls.
41#[derive(Debug, PartialEq, Eq)] 42#[derive(Debug, PartialEq, Eq)]
42pub struct CrateImplDefs { 43pub struct CrateImplDefs {
43 impls: FxHashMap<TyFingerprint, Vec<ImplId>>, 44 inherent_impls: FxHashMap<TyFingerprint, Vec<ImplId>>,
44 impls_by_trait: FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Vec<ImplId>>>, 45 impls_by_trait: FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Vec<ImplId>>>,
45} 46}
46 47
47impl CrateImplDefs { 48impl CrateImplDefs {
48 pub(crate) fn impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<CrateImplDefs> { 49 pub(crate) fn impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<CrateImplDefs> {
49 let _p = profile("impls_in_crate_query"); 50 let _p = profile("impls_in_crate_query");
50 let mut res = 51 let mut res = CrateImplDefs {
51 CrateImplDefs { impls: FxHashMap::default(), impls_by_trait: FxHashMap::default() }; 52 inherent_impls: FxHashMap::default(),
53 impls_by_trait: FxHashMap::default(),
54 };
55 res.fill(db, krate);
56
57 Arc::new(res)
58 }
59
60 /// Collects all impls from transitive dependencies of `krate` that may be used by `krate`.
61 ///
62 /// The full set of impls that can be used by `krate` is the returned map plus all the impls
63 /// from `krate` itself.
64 pub(crate) fn impls_from_deps_query(
65 db: &dyn HirDatabase,
66 krate: CrateId,
67 ) -> Arc<CrateImplDefs> {
68 let _p = profile("impls_from_deps_query");
69 let crate_graph = db.crate_graph();
70 let mut res = CrateImplDefs {
71 inherent_impls: FxHashMap::default(),
72 impls_by_trait: FxHashMap::default(),
73 };
52 74
75 // For each dependency, calculate `impls_from_deps` recursively, then add its own
76 // `impls_in_crate`.
77 // As we might visit crates multiple times, `merge` has to deduplicate impls to avoid
78 // wasting memory.
79 for dep in &crate_graph[krate].dependencies {
80 res.merge(&db.impls_from_deps(dep.crate_id));
81 res.merge(&db.impls_in_crate(dep.crate_id));
82 }
83
84 Arc::new(res)
85 }
86
87 fn fill(&mut self, db: &dyn HirDatabase, krate: CrateId) {
53 let crate_def_map = db.crate_def_map(krate); 88 let crate_def_map = db.crate_def_map(krate);
54 for (_module_id, module_data) in crate_def_map.modules.iter() { 89 for (_module_id, module_data) in crate_def_map.modules.iter() {
55 for impl_id in module_data.scope.impls() { 90 for impl_id in module_data.scope.impls() {
@@ -57,7 +92,7 @@ impl CrateImplDefs {
57 Some(tr) => { 92 Some(tr) => {
58 let self_ty = db.impl_self_ty(impl_id); 93 let self_ty = db.impl_self_ty(impl_id);
59 let self_ty_fp = TyFingerprint::for_impl(&self_ty.value); 94 let self_ty_fp = TyFingerprint::for_impl(&self_ty.value);
60 res.impls_by_trait 95 self.impls_by_trait
61 .entry(tr.value.trait_) 96 .entry(tr.value.trait_)
62 .or_default() 97 .or_default()
63 .entry(self_ty_fp) 98 .entry(self_ty_fp)
@@ -67,18 +102,36 @@ impl CrateImplDefs {
67 None => { 102 None => {
68 let self_ty = db.impl_self_ty(impl_id); 103 let self_ty = db.impl_self_ty(impl_id);
69 if let Some(self_ty_fp) = TyFingerprint::for_impl(&self_ty.value) { 104 if let Some(self_ty_fp) = TyFingerprint::for_impl(&self_ty.value) {
70 res.impls.entry(self_ty_fp).or_default().push(impl_id); 105 self.inherent_impls.entry(self_ty_fp).or_default().push(impl_id);
71 } 106 }
72 } 107 }
73 } 108 }
74 } 109 }
75 } 110 }
111 }
76 112
77 Arc::new(res) 113 fn merge(&mut self, other: &Self) {
114 for (fp, impls) in &other.inherent_impls {
115 let vec = self.inherent_impls.entry(*fp).or_default();
116 vec.extend(impls);
117 vec.sort();
118 vec.dedup();
119 }
120
121 for (trait_, other_map) in &other.impls_by_trait {
122 let map = self.impls_by_trait.entry(*trait_).or_default();
123 for (fp, impls) in other_map {
124 let vec = map.entry(*fp).or_default();
125 vec.extend(impls);
126 vec.sort();
127 vec.dedup();
128 }
129 }
78 } 130 }
131
79 pub fn lookup_impl_defs(&self, ty: &Ty) -> impl Iterator<Item = ImplId> + '_ { 132 pub fn lookup_impl_defs(&self, ty: &Ty) -> impl Iterator<Item = ImplId> + '_ {
80 let fingerprint = TyFingerprint::for_impl(ty); 133 let fingerprint = TyFingerprint::for_impl(ty);
81 fingerprint.and_then(|f| self.impls.get(&f)).into_iter().flatten().copied() 134 fingerprint.and_then(|f| self.inherent_impls.get(&f)).into_iter().flatten().copied()
82 } 135 }
83 136
84 pub fn lookup_impl_defs_for_trait(&self, tr: TraitId) -> impl Iterator<Item = ImplId> + '_ { 137 pub fn lookup_impl_defs_for_trait(&self, tr: TraitId) -> impl Iterator<Item = ImplId> + '_ {
@@ -110,7 +163,7 @@ impl CrateImplDefs {
110 } 163 }
111 164
112 pub fn all_impls<'a>(&'a self) -> impl Iterator<Item = ImplId> + 'a { 165 pub fn all_impls<'a>(&'a self) -> impl Iterator<Item = ImplId> + 'a {
113 self.impls 166 self.inherent_impls
114 .values() 167 .values()
115 .chain(self.impls_by_trait.values().flat_map(|m| m.values())) 168 .chain(self.impls_by_trait.values().flat_map(|m| m.values()))
116 .flatten() 169 .flatten()
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs
index 8dc5603b7..4da2e972b 100644
--- a/crates/ra_hir_ty/src/tests/regression.rs
+++ b/crates/ra_hir_ty/src/tests/regression.rs
@@ -693,3 +693,94 @@ fn check<T: PrimInt>(i: T) {
693 "### 693 "###
694 ); 694 );
695} 695}
696
697#[test]
698fn issue_4885() {
699 assert_snapshot!(
700 infer(r#"
701#[lang = "coerce_unsized"]
702pub trait CoerceUnsized<T> {}
703
704trait Future {
705 type Output;
706}
707trait Foo<R> {
708 type Bar;
709}
710fn foo<R, K>(key: &K) -> impl Future<Output = K::Bar>
711where
712 K: Foo<R>,
713{
714 bar(key)
715}
716fn bar<R, K>(key: &K) -> impl Future<Output = K::Bar>
717where
718 K: Foo<R>,
719{
720}
721"#),
722 @r###"
723 137..140 'key': &K
724 199..215 '{ ...key) }': impl Future<Output = <K as Foo<R>>::Bar>
725 205..208 'bar': fn bar<R, K>(&K) -> impl Future<Output = <K as Foo<R>>::Bar>
726 205..213 'bar(key)': impl Future<Output = <K as Foo<R>>::Bar>
727 209..212 'key': &K
728 229..232 'key': &K
729 291..294 '{ }': ()
730 "###
731 );
732}
733
734#[test]
735fn issue_4800() {
736 assert_snapshot!(
737 infer(r#"
738trait Debug {}
739
740struct Foo<T>;
741
742type E1<T> = (T, T, T);
743type E2<T> = E1<E1<E1<(T, T, T)>>>;
744
745impl Debug for Foo<E2<()>> {}
746
747struct Request;
748
749pub trait Future {
750 type Output;
751}
752
753pub struct PeerSet<D>;
754
755impl<D> Service<Request> for PeerSet<D>
756where
757 D: Discover,
758 D::Key: Debug,
759{
760 type Error = ();
761 type Future = dyn Future<Output = Self::Error>;
762
763 fn call(&mut self) -> Self::Future {
764 loop {}
765 }
766}
767
768pub trait Discover {
769 type Key;
770}
771
772pub trait Service<Request> {
773 type Error;
774 type Future: Future<Output = Self::Error>;
775 fn call(&mut self) -> Self::Future;
776}
777"#),
778 @r###"
779 380..384 'self': &mut PeerSet<D>
780 402..425 '{ ... }': dyn Future<Output = ()>
781 412..419 'loop {}': !
782 417..419 '{}': ()
783 576..580 'self': &mut Self
784 "###
785 );
786}
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index e81193a3c..961be4abd 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -2888,3 +2888,226 @@ impl<A: Step> iter::Iterator for ops::Range<A> {
2888 ); 2888 );
2889 assert_eq!(t, "i32"); 2889 assert_eq!(t, "i32");
2890} 2890}
2891
2892#[test]
2893fn infer_closure_arg() {
2894 assert_snapshot!(
2895 infer(
2896 r#"
2897 //- /lib.rs
2898
2899 enum Option<T> {
2900 None,
2901 Some(T)
2902 }
2903
2904 fn foo() {
2905 let s = Option::None;
2906 let f = |x: Option<i32>| {};
2907 (&f)(s)
2908 }
2909 "#
2910 ),
2911 @r###"
2912 137..259 '{ ... }': ()
2913 159..160 's': Option<i32>
2914 163..175 'Option::None': Option<i32>
2915 197..198 'f': |Option<i32>| -> ()
2916 201..220 '|x: Op...2>| {}': |Option<i32>| -> ()
2917 202..203 'x': Option<i32>
2918 218..220 '{}': ()
2919 238..245 '(&f)(s)': ()
2920 239..241 '&f': &|Option<i32>| -> ()
2921 240..241 'f': |Option<i32>| -> ()
2922 243..244 's': Option<i32>
2923 "###
2924 );
2925}
2926
2927#[test]
2928fn infer_fn_trait_arg() {
2929 assert_snapshot!(
2930 infer(
2931 r#"
2932 //- /lib.rs deps:std
2933
2934 #[lang = "fn_once"]
2935 pub trait FnOnce<Args> {
2936 type Output;
2937
2938 extern "rust-call" fn call_once(&self, args: Args) -> Self::Output;
2939 }
2940
2941 #[lang = "fn"]
2942 pub trait Fn<Args>:FnOnce<Args> {
2943 extern "rust-call" fn call(&self, args: Args) -> Self::Output;
2944 }
2945
2946 enum Option<T> {
2947 None,
2948 Some(T)
2949 }
2950
2951 fn foo<F, T>(f: F) -> T
2952 where
2953 F: Fn(Option<i32>) -> T,
2954 {
2955 let s = None;
2956 f(s)
2957 }
2958 "#
2959 ),
2960 @r###"
2961 183..187 'self': &Self
2962 189..193 'args': Args
2963 350..354 'self': &Self
2964 356..360 'args': Args
2965 515..516 'f': F
2966 597..663 '{ ... }': T
2967 619..620 's': Option<i32>
2968 623..627 'None': Option<i32>
2969 645..646 'f': F
2970 645..649 'f(s)': T
2971 647..648 's': Option<i32>
2972 "###
2973 );
2974}
2975
2976#[test]
2977fn infer_box_fn_arg() {
2978 assert_snapshot!(
2979 infer(
2980 r#"
2981 //- /lib.rs deps:std
2982
2983 #[lang = "fn_once"]
2984 pub trait FnOnce<Args> {
2985 type Output;
2986
2987 extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
2988 }
2989
2990 #[lang = "deref"]
2991 pub trait Deref {
2992 type Target: ?Sized;
2993
2994 fn deref(&self) -> &Self::Target;
2995 }
2996
2997 #[lang = "owned_box"]
2998 pub struct Box<T: ?Sized> {
2999 inner: *mut T,
3000 }
3001
3002 impl<T: ?Sized> Deref for Box<T> {
3003 type Target = T;
3004
3005 fn deref(&self) -> &T {
3006 &self.inner
3007 }
3008 }
3009
3010 enum Option<T> {
3011 None,
3012 Some(T)
3013 }
3014
3015 fn foo() {
3016 let s = Option::None;
3017 let f: Box<dyn FnOnce(&Option<i32>)> = box (|ps| {});
3018 f(&s)
3019 }
3020 "#
3021 ),
3022 @r###"
3023 182..186 'self': Self
3024 188..192 'args': Args
3025 356..360 'self': &Self
3026 622..626 'self': &Box<T>
3027 634..685 '{ ... }': &T
3028 656..667 '&self.inner': &*mut T
3029 657..661 'self': &Box<T>
3030 657..667 'self.inner': *mut T
3031 812..957 '{ ... }': FnOnce::Output<dyn FnOnce<(&Option<i32>,)>, (&Option<i32>,)>
3032 834..835 's': Option<i32>
3033 838..850 'Option::None': Option<i32>
3034 872..873 'f': Box<dyn FnOnce<(&Option<i32>,)>>
3035 907..920 'box (|ps| {})': Box<|{unknown}| -> ()>
3036 912..919 '|ps| {}': |{unknown}| -> ()
3037 913..915 'ps': {unknown}
3038 917..919 '{}': ()
3039 938..939 'f': Box<dyn FnOnce<(&Option<i32>,)>>
3040 938..943 'f(&s)': FnOnce::Output<dyn FnOnce<(&Option<i32>,)>, (&Option<i32>,)>
3041 940..942 '&s': &Option<i32>
3042 941..942 's': Option<i32>
3043 "###
3044 );
3045}
3046
3047#[test]
3048fn infer_dyn_fn_output() {
3049 assert_snapshot!(
3050 infer(
3051 r#"
3052 //- /lib.rs deps:std
3053
3054 #[lang = "fn_once"]
3055 pub trait FnOnce<Args> {
3056 type Output;
3057
3058 extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
3059 }
3060
3061 #[lang = "fn"]
3062 pub trait Fn<Args>:FnOnce<Args> {
3063 extern "rust-call" fn call(&self, args: Args) -> Self::Output;
3064 }
3065
3066 #[lang = "deref"]
3067 pub trait Deref {
3068 type Target: ?Sized;
3069
3070 fn deref(&self) -> &Self::Target;
3071 }
3072
3073 #[lang = "owned_box"]
3074 pub struct Box<T: ?Sized> {
3075 inner: *mut T,
3076 }
3077
3078 impl<T: ?Sized> Deref for Box<T> {
3079 type Target = T;
3080
3081 fn deref(&self) -> &T {
3082 &self.inner
3083 }
3084 }
3085
3086 fn foo() {
3087 let f: Box<dyn Fn() -> i32> = box(|| 5);
3088 let x = f();
3089 }
3090 "#
3091 ),
3092 @r###"
3093 182..186 'self': Self
3094 188..192 'args': Args
3095 349..353 'self': &Self
3096 355..359 'args': Args
3097 523..527 'self': &Self
3098 789..793 'self': &Box<T>
3099 801..852 '{ ... }': &T
3100 823..834 '&self.inner': &*mut T
3101 824..828 'self': &Box<T>
3102 824..834 'self.inner': *mut T
3103 889..990 '{ ... }': ()
3104 911..912 'f': Box<dyn Fn<(), Output = i32>>
3105 937..946 'box(|| 5)': Box<|| -> i32>
3106 941..945 '|| 5': || -> i32
3107 944..945 '5': i32
3108 968..969 'x': FnOnce::Output<dyn Fn<(), Output = i32>, ()>
3109 972..973 'f': Box<dyn Fn<(), Output = i32>>
3110 972..975 'f()': FnOnce::Output<dyn Fn<(), Output = i32>, ()>
3111 "###
3112 );
3113}
diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs
index 6bc6d474c..6f43c3a22 100644
--- a/crates/ra_hir_ty/src/traits.rs
+++ b/crates/ra_hir_ty/src/traits.rs
@@ -2,12 +2,13 @@
2use std::{panic, sync::Arc}; 2use std::{panic, sync::Arc};
3 3
4use chalk_ir::cast::Cast; 4use chalk_ir::cast::Cast;
5use hir_def::{expr::ExprId, DefWithBodyId, ImplId, TraitId, TypeAliasId}; 5use hir_def::{
6 expr::ExprId, lang_item::LangItemTarget, DefWithBodyId, ImplId, TraitId, TypeAliasId,
7};
6use ra_db::{impl_intern_key, salsa, CrateId}; 8use ra_db::{impl_intern_key, salsa, CrateId};
7use ra_prof::profile; 9use ra_prof::profile;
8use rustc_hash::FxHashSet;
9 10
10use crate::{db::HirDatabase, method_resolution::TyFingerprint, DebruijnIndex}; 11use crate::{db::HirDatabase, DebruijnIndex};
11 12
12use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk}; 13use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk};
13 14
@@ -36,34 +37,6 @@ fn create_chalk_solver() -> chalk_solve::Solver<Interner> {
36 solver_choice.into_solver() 37 solver_choice.into_solver()
37} 38}
38 39
39/// Collects impls for the given trait in the whole dependency tree of `krate`.
40pub(crate) fn impls_for_trait_query(
41 db: &dyn HirDatabase,
42 krate: CrateId,
43 trait_: TraitId,
44 self_ty_fp: Option<TyFingerprint>,
45) -> Arc<[ImplId]> {
46 // FIXME: We could be a lot smarter here - because of the orphan rules and
47 // the fact that the trait and the self type need to be in the dependency
48 // tree of a crate somewhere for an impl to exist, we could skip looking in
49 // a lot of crates completely
50 let mut impls = FxHashSet::default();
51 // We call the query recursively here. On the one hand, this means we can
52 // reuse results from queries for different crates; on the other hand, this
53 // will only ever get called for a few crates near the root of the tree (the
54 // ones the user is editing), so this may actually be a waste of memory. I'm
55 // doing it like this mainly for simplicity for now.
56 for dep in &db.crate_graph()[krate].dependencies {
57 impls.extend(db.impls_for_trait(dep.crate_id, trait_, self_ty_fp).iter());
58 }
59 let crate_impl_defs = db.impls_in_crate(krate);
60 match self_ty_fp {
61 Some(fp) => impls.extend(crate_impl_defs.lookup_impl_defs_for_trait_and_ty(trait_, fp)),
62 None => impls.extend(crate_impl_defs.lookup_impl_defs_for_trait(trait_)),
63 }
64 impls.into_iter().collect()
65}
66
67/// A set of clauses that we assume to be true. E.g. if we are inside this function: 40/// A set of clauses that we assume to be true. E.g. if we are inside this function:
68/// ```rust 41/// ```rust
69/// fn foo<T: Default>(t: T) {} 42/// fn foo<T: Default>(t: T) {}
@@ -298,6 +271,14 @@ impl FnTrait {
298 FnTrait::Fn => "fn", 271 FnTrait::Fn => "fn",
299 } 272 }
300 } 273 }
274
275 pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
276 let target = db.lang_item(krate, self.lang_item_name().into())?;
277 match target {
278 LangItemTarget::TraitId(t) => Some(t),
279 _ => None,
280 }
281 }
301} 282}
302 283
303#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 284#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs
index 88a422d2c..6d5f2d46a 100644
--- a/crates/ra_hir_ty/src/traits/builtin.rs
+++ b/crates/ra_hir_ty/src/traits/builtin.rs
@@ -40,7 +40,7 @@ pub(super) fn get_builtin_impls(
40 if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { def, expr }, .. }) = ty { 40 if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { def, expr }, .. }) = ty {
41 for &fn_trait in [super::FnTrait::FnOnce, super::FnTrait::FnMut, super::FnTrait::Fn].iter() 41 for &fn_trait in [super::FnTrait::FnOnce, super::FnTrait::FnMut, super::FnTrait::Fn].iter()
42 { 42 {
43 if let Some(actual_trait) = get_fn_trait(db, krate, fn_trait) { 43 if let Some(actual_trait) = fn_trait.get_id(db, krate) {
44 if trait_ == actual_trait { 44 if trait_ == actual_trait {
45 let impl_ = super::ClosureFnTraitImplData { def: *def, expr: *expr, fn_trait }; 45 let impl_ = super::ClosureFnTraitImplData { def: *def, expr: *expr, fn_trait };
46 if check_closure_fn_trait_impl_prerequisites(db, krate, impl_) { 46 if check_closure_fn_trait_impl_prerequisites(db, krate, impl_) {
@@ -128,7 +128,7 @@ fn check_closure_fn_trait_impl_prerequisites(
128 data: super::ClosureFnTraitImplData, 128 data: super::ClosureFnTraitImplData,
129) -> bool { 129) -> bool {
130 // the respective Fn/FnOnce/FnMut trait needs to exist 130 // the respective Fn/FnOnce/FnMut trait needs to exist
131 if get_fn_trait(db, krate, data.fn_trait).is_none() { 131 if data.fn_trait.get_id(db, krate).is_none() {
132 return false; 132 return false;
133 } 133 }
134 134
@@ -136,7 +136,7 @@ fn check_closure_fn_trait_impl_prerequisites(
136 // the traits having no type params, FnOnce being a supertrait 136 // the traits having no type params, FnOnce being a supertrait
137 137
138 // the FnOnce trait needs to exist and have an assoc type named Output 138 // the FnOnce trait needs to exist and have an assoc type named Output
139 let fn_once_trait = match get_fn_trait(db, krate, super::FnTrait::FnOnce) { 139 let fn_once_trait = match (super::FnTrait::FnOnce).get_id(db, krate) {
140 Some(t) => t, 140 Some(t) => t,
141 None => return false, 141 None => return false,
142 }; 142 };
@@ -151,7 +151,9 @@ fn closure_fn_trait_impl_datum(
151 // for some closure |X, Y| -> Z: 151 // for some closure |X, Y| -> Z:
152 // impl<T, U, V> Fn<(T, U)> for closure<fn(T, U) -> V> { Output = V } 152 // impl<T, U, V> Fn<(T, U)> for closure<fn(T, U) -> V> { Output = V }
153 153
154 let trait_ = get_fn_trait(db, krate, data.fn_trait) // get corresponding fn trait 154 let trait_ = data
155 .fn_trait
156 .get_id(db, krate) // get corresponding fn trait
155 // the existence of the Fn trait has been checked before 157 // the existence of the Fn trait has been checked before
156 .expect("fn trait for closure impl missing"); 158 .expect("fn trait for closure impl missing");
157 159
@@ -211,7 +213,7 @@ fn closure_fn_trait_output_assoc_ty_value(
211 let output_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, num_args.into())); 213 let output_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, num_args.into()));
212 214
213 let fn_once_trait = 215 let fn_once_trait =
214 get_fn_trait(db, krate, super::FnTrait::FnOnce).expect("assoc ty value should not exist"); 216 (super::FnTrait::FnOnce).get_id(db, krate).expect("assoc ty value should not exist");
215 217
216 let output_ty_id = db 218 let output_ty_id = db
217 .trait_data(fn_once_trait) 219 .trait_data(fn_once_trait)
@@ -360,14 +362,6 @@ fn super_trait_object_unsize_impl_datum(
360 BuiltinImplData { num_vars, trait_ref, where_clauses: Vec::new(), assoc_ty_values: Vec::new() } 362 BuiltinImplData { num_vars, trait_ref, where_clauses: Vec::new(), assoc_ty_values: Vec::new() }
361} 363}
362 364
363fn get_fn_trait(db: &dyn HirDatabase, krate: CrateId, fn_trait: super::FnTrait) -> Option<TraitId> {
364 let target = db.lang_item(krate, fn_trait.lang_item_name().into())?;
365 match target {
366 LangItemTarget::TraitId(t) => Some(t),
367 _ => None,
368 }
369}
370
371fn get_unsize_trait(db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> { 365fn get_unsize_trait(db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
372 let target = db.lang_item(krate, "unsize".into())?; 366 let target = db.lang_item(krate, "unsize".into())?;
373 match target { 367 match target {
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index a72a82f5a..2f35d6d49 100644
--- a/crates/ra_hir_ty/src/traits/chalk.rs
+++ b/crates/ra_hir_ty/src/traits/chalk.rs
@@ -74,14 +74,26 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
74 // Note: Since we're using impls_for_trait, only impls where the trait 74 // Note: Since we're using impls_for_trait, only impls where the trait
75 // can be resolved should ever reach Chalk. `impl_datum` relies on that 75 // can be resolved should ever reach Chalk. `impl_datum` relies on that
76 // and will panic if the trait can't be resolved. 76 // and will panic if the trait can't be resolved.
77 let mut result: Vec<_> = self 77 let in_deps = self.db.impls_from_deps(self.krate);
78 .db 78 let in_self = self.db.impls_in_crate(self.krate);
79 .impls_for_trait(self.krate, trait_, self_ty_fp) 79 let impl_maps = [in_deps, in_self];
80 .iter() 80
81 .copied() 81 let id_to_chalk = |id: hir_def::ImplId| Impl::ImplDef(id).to_chalk(self.db);
82 .map(Impl::ImplDef) 82
83 .map(|impl_| impl_.to_chalk(self.db)) 83 let mut result: Vec<_> = match self_ty_fp {
84 .collect(); 84 Some(fp) => impl_maps
85 .iter()
86 .flat_map(|crate_impl_defs| {
87 crate_impl_defs.lookup_impl_defs_for_trait_and_ty(trait_, fp).map(id_to_chalk)
88 })
89 .collect(),
90 None => impl_maps
91 .iter()
92 .flat_map(|crate_impl_defs| {
93 crate_impl_defs.lookup_impl_defs_for_trait(trait_).map(id_to_chalk)
94 })
95 .collect(),
96 };
85 97
86 let arg: Option<Ty> = 98 let arg: Option<Ty> =
87 parameters.get(1).map(|p| from_chalk(self.db, p.assert_ty_ref(&Interner).clone())); 99 parameters.get(1).map(|p| from_chalk(self.db, p.assert_ty_ref(&Interner).clone()));
diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs
index 78ee6a515..98993d571 100644
--- a/crates/ra_ide_db/src/change.rs
+++ b/crates/ra_ide_db/src/change.rs
@@ -283,7 +283,7 @@ impl RootDatabase {
283 hir::db::GenericPredicatesQuery 283 hir::db::GenericPredicatesQuery
284 hir::db::GenericDefaultsQuery 284 hir::db::GenericDefaultsQuery
285 hir::db::ImplsInCrateQuery 285 hir::db::ImplsInCrateQuery
286 hir::db::ImplsForTraitQuery 286 hir::db::ImplsFromDepsQuery
287 hir::db::InternTypeCtorQuery 287 hir::db::InternTypeCtorQuery
288 hir::db::InternTypeParamIdQuery 288 hir::db::InternTypeParamIdQuery
289 hir::db::InternChalkImplQuery 289 hir::db::InternChalkImplQuery
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 458089e53..2b46e8905 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -32,7 +32,7 @@ threadpool = "1.7.1"
32 32
33stdx = { path = "../stdx" } 33stdx = { path = "../stdx" }
34 34
35lsp-server = "0.3.2" 35lsp-server = "0.3.3"
36ra_flycheck = { path = "../ra_flycheck" } 36ra_flycheck = { path = "../ra_flycheck" }
37ra_ide = { path = "../ra_ide" } 37ra_ide = { path = "../ra_ide" }
38ra_prof = { path = "../ra_prof" } 38ra_prof = { path = "../ra_prof" }
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 1527c9947..d04ef4c61 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -20,7 +20,7 @@ use stdx::format_to;
20use crate::{ 20use crate::{
21 config::{Config, FilesWatcher}, 21 config::{Config, FilesWatcher},
22 diagnostics::{CheckFixes, DiagnosticCollection}, 22 diagnostics::{CheckFixes, DiagnosticCollection},
23 main_loop::pending_requests::{CompletedRequest, LatestRequests}, 23 main_loop::request_metrics::{LatestRequests, RequestMetrics},
24 to_proto::url_from_abs_path, 24 to_proto::url_from_abs_path,
25 vfs_glob::{Glob, RustPackageFilterBuilder}, 25 vfs_glob::{Glob, RustPackageFilterBuilder},
26 LspError, Result, 26 LspError, Result,
@@ -55,10 +55,10 @@ pub struct GlobalState {
55 pub analysis_host: AnalysisHost, 55 pub analysis_host: AnalysisHost,
56 pub vfs: Arc<RwLock<Vfs>>, 56 pub vfs: Arc<RwLock<Vfs>>,
57 pub task_receiver: Receiver<VfsTask>, 57 pub task_receiver: Receiver<VfsTask>,
58 pub latest_requests: Arc<RwLock<LatestRequests>>,
59 pub flycheck: Option<Flycheck>, 58 pub flycheck: Option<Flycheck>,
60 pub diagnostics: DiagnosticCollection, 59 pub diagnostics: DiagnosticCollection,
61 pub proc_macro_client: ProcMacroClient, 60 pub proc_macro_client: ProcMacroClient,
61 pub(crate) latest_requests: Arc<RwLock<LatestRequests>>,
62} 62}
63 63
64/// An immutable snapshot of the world's state at a point in time. 64/// An immutable snapshot of the world's state at a point in time.
@@ -66,8 +66,8 @@ pub struct GlobalStateSnapshot {
66 pub config: Config, 66 pub config: Config,
67 pub workspaces: Arc<Vec<ProjectWorkspace>>, 67 pub workspaces: Arc<Vec<ProjectWorkspace>>,
68 pub analysis: Analysis, 68 pub analysis: Analysis,
69 pub latest_requests: Arc<RwLock<LatestRequests>>,
70 pub check_fixes: CheckFixes, 69 pub check_fixes: CheckFixes,
70 pub(crate) latest_requests: Arc<RwLock<LatestRequests>>,
71 vfs: Arc<RwLock<Vfs>>, 71 vfs: Arc<RwLock<Vfs>>,
72} 72}
73 73
@@ -236,7 +236,7 @@ impl GlobalState {
236 self.analysis_host.collect_garbage() 236 self.analysis_host.collect_garbage()
237 } 237 }
238 238
239 pub fn complete_request(&mut self, request: CompletedRequest) { 239 pub(crate) fn complete_request(&mut self, request: RequestMetrics) {
240 self.latest_requests.write().record(request) 240 self.latest_requests.write().record(request)
241 } 241 }
242} 242}
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index 609cb69d3..64e70955f 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -32,7 +32,7 @@ mod semantic_tokens;
32 32
33use serde::de::DeserializeOwned; 33use serde::de::DeserializeOwned;
34 34
35pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; 35pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>;
36pub use crate::{ 36pub use crate::{
37 caps::server_capabilities, 37 caps::server_capabilities,
38 main_loop::LspError, 38 main_loop::LspError,
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index f0aaaa21e..674b1323b 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -3,7 +3,7 @@
3 3
4mod handlers; 4mod handlers;
5mod subscriptions; 5mod subscriptions;
6pub(crate) mod pending_requests; 6pub(crate) mod request_metrics;
7 7
8use std::{ 8use std::{
9 borrow::Cow, 9 borrow::Cow,
@@ -17,18 +17,19 @@ use std::{
17}; 17};
18 18
19use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; 19use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
20use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 20use lsp_server::{
21 Connection, ErrorCode, Message, Notification, ReqQueue, Request, RequestId, Response,
22};
21use lsp_types::{ 23use lsp_types::{
22 DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress, 24 request::Request as _, DidChangeTextDocumentParams, NumberOrString,
23 WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd, 25 TextDocumentContentChangeEvent, WorkDoneProgress, WorkDoneProgressBegin,
24 WorkDoneProgressReport, 26 WorkDoneProgressCreateParams, WorkDoneProgressEnd, WorkDoneProgressReport,
25}; 27};
26use ra_flycheck::{CheckTask, Status}; 28use ra_flycheck::{CheckTask, Status};
27use ra_ide::{Canceled, FileId, LineIndex}; 29use ra_ide::{Canceled, FileId, LineIndex};
28use ra_prof::profile; 30use ra_prof::profile;
29use ra_project_model::{PackageRoot, ProjectWorkspace}; 31use ra_project_model::{PackageRoot, ProjectWorkspace};
30use ra_vfs::VfsTask; 32use ra_vfs::VfsTask;
31use rustc_hash::FxHashSet;
32use serde::{de::DeserializeOwned, Serialize}; 33use serde::{de::DeserializeOwned, Serialize};
33use threadpool::ThreadPool; 34use threadpool::ThreadPool;
34 35
@@ -38,10 +39,7 @@ use crate::{
38 from_proto, 39 from_proto,
39 global_state::{file_id_to_url, GlobalState, GlobalStateSnapshot}, 40 global_state::{file_id_to_url, GlobalState, GlobalStateSnapshot},
40 lsp_ext, 41 lsp_ext,
41 main_loop::{ 42 main_loop::{request_metrics::RequestMetrics, subscriptions::Subscriptions},
42 pending_requests::{PendingRequest, PendingRequests},
43 subscriptions::Subscriptions,
44 },
45 Result, 43 Result,
46}; 44};
47 45
@@ -153,9 +151,10 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
153 register_options: Some(serde_json::to_value(registration_options).unwrap()), 151 register_options: Some(serde_json::to_value(registration_options).unwrap()),
154 }; 152 };
155 let params = lsp_types::RegistrationParams { registrations: vec![registration] }; 153 let params = lsp_types::RegistrationParams { registrations: vec![registration] };
156 let request = request_new::<lsp_types::request::RegisterCapability>( 154 let request = loop_state.req_queue.outgoing.register(
157 loop_state.next_request_id(), 155 lsp_types::request::RegisterCapability::METHOD.to_string(),
158 params, 156 params,
157 DO_NOTHING,
159 ); 158 );
160 connection.sender.send(request.into()).unwrap(); 159 connection.sender.send(request.into()).unwrap();
161 } 160 }
@@ -199,7 +198,7 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
199 global_state.analysis_host.request_cancellation(); 198 global_state.analysis_host.request_cancellation();
200 log::info!("waiting for tasks to finish..."); 199 log::info!("waiting for tasks to finish...");
201 task_receiver.into_iter().for_each(|task| { 200 task_receiver.into_iter().for_each(|task| {
202 on_task(task, &connection.sender, &mut loop_state.pending_requests, &mut global_state) 201 on_task(task, &connection.sender, &mut loop_state.req_queue.incoming, &mut global_state)
203 }); 202 });
204 log::info!("...tasks have finished"); 203 log::info!("...tasks have finished");
205 log::info!("joining threadpool..."); 204 log::info!("joining threadpool...");
@@ -264,27 +263,18 @@ impl fmt::Debug for Event {
264 } 263 }
265} 264}
266 265
267#[derive(Debug, Default)] 266type ReqHandler = fn(&mut GlobalState, Response);
267const DO_NOTHING: ReqHandler = |_, _| ();
268type Incoming = lsp_server::Incoming<(&'static str, Instant)>;
269
270#[derive(Default)]
268struct LoopState { 271struct LoopState {
269 next_request_id: u64, 272 req_queue: ReqQueue<(&'static str, Instant), ReqHandler>,
270 pending_responses: FxHashSet<RequestId>,
271 pending_requests: PendingRequests,
272 subscriptions: Subscriptions, 273 subscriptions: Subscriptions,
273 workspace_loaded: bool, 274 workspace_loaded: bool,
274 roots_progress_reported: Option<usize>, 275 roots_progress_reported: Option<usize>,
275 roots_scanned: usize, 276 roots_scanned: usize,
276 roots_total: usize, 277 roots_total: usize,
277 configuration_request_id: Option<RequestId>,
278}
279
280impl LoopState {
281 fn next_request_id(&mut self) -> RequestId {
282 self.next_request_id += 1;
283 let res: RequestId = self.next_request_id.into();
284 let inserted = self.pending_responses.insert(res.clone());
285 assert!(inserted);
286 res
287 }
288} 278}
289 279
290fn loop_turn( 280fn loop_turn(
@@ -307,7 +297,7 @@ fn loop_turn(
307 297
308 match event { 298 match event {
309 Event::Task(task) => { 299 Event::Task(task) => {
310 on_task(task, &connection.sender, &mut loop_state.pending_requests, global_state); 300 on_task(task, &connection.sender, &mut loop_state.req_queue.incoming, global_state);
311 global_state.maybe_collect_garbage(); 301 global_state.maybe_collect_garbage();
312 } 302 }
313 Event::Vfs(task) => { 303 Event::Vfs(task) => {
@@ -317,7 +307,7 @@ fn loop_turn(
317 Event::Msg(msg) => match msg { 307 Event::Msg(msg) => match msg {
318 Message::Request(req) => on_request( 308 Message::Request(req) => on_request(
319 global_state, 309 global_state,
320 &mut loop_state.pending_requests, 310 &mut loop_state.req_queue.incoming,
321 pool, 311 pool,
322 task_sender, 312 task_sender,
323 &connection.sender, 313 &connection.sender,
@@ -328,32 +318,8 @@ fn loop_turn(
328 on_notification(&connection.sender, global_state, loop_state, not)?; 318 on_notification(&connection.sender, global_state, loop_state, not)?;
329 } 319 }
330 Message::Response(resp) => { 320 Message::Response(resp) => {
331 let removed = loop_state.pending_responses.remove(&resp.id); 321 let handler = loop_state.req_queue.outgoing.complete(resp.id.clone());
332 if !removed { 322 handler(global_state, resp)
333 log::error!("unexpected response: {:?}", resp)
334 }
335
336 if Some(&resp.id) == loop_state.configuration_request_id.as_ref() {
337 loop_state.configuration_request_id = None;
338 log::debug!("config update response: '{:?}", resp);
339 let Response { error, result, .. } = resp;
340
341 match (error, result) {
342 (Some(err), _) => {
343 log::error!("failed to fetch the server settings: {:?}", err)
344 }
345 (None, Some(configs)) => {
346 if let Some(new_config) = configs.get(0) {
347 let mut config = global_state.config.clone();
348 config.update(&new_config);
349 global_state.update_configuration(config);
350 }
351 }
352 (None, None) => {
353 log::error!("received empty server settings response from the client")
354 }
355 }
356 }
357 } 323 }
358 }, 324 },
359 }; 325 };
@@ -407,14 +373,19 @@ fn loop_turn(
407fn on_task( 373fn on_task(
408 task: Task, 374 task: Task,
409 msg_sender: &Sender<Message>, 375 msg_sender: &Sender<Message>,
410 pending_requests: &mut PendingRequests, 376 incoming_requests: &mut Incoming,
411 state: &mut GlobalState, 377 state: &mut GlobalState,
412) { 378) {
413 match task { 379 match task {
414 Task::Respond(response) => { 380 Task::Respond(response) => {
415 if let Some(completed) = pending_requests.finish(&response.id) { 381 if let Some((method, start)) = incoming_requests.complete(response.id.clone()) {
416 log::info!("handled req#{} in {:?}", completed.id, completed.duration); 382 let duration = start.elapsed();
417 state.complete_request(completed); 383 log::info!("handled req#{} in {:?}", response.id, duration);
384 state.complete_request(RequestMetrics {
385 id: response.id.clone(),
386 method: method.to_string(),
387 duration,
388 });
418 msg_sender.send(response.into()).unwrap(); 389 msg_sender.send(response.into()).unwrap();
419 } 390 }
420 } 391 }
@@ -427,7 +398,7 @@ fn on_task(
427 398
428fn on_request( 399fn on_request(
429 global_state: &mut GlobalState, 400 global_state: &mut GlobalState,
430 pending_requests: &mut PendingRequests, 401 incoming_requests: &mut Incoming,
431 pool: &ThreadPool, 402 pool: &ThreadPool,
432 task_sender: &Sender<Task>, 403 task_sender: &Sender<Task>,
433 msg_sender: &Sender<Message>, 404 msg_sender: &Sender<Message>,
@@ -440,7 +411,7 @@ fn on_request(
440 global_state, 411 global_state,
441 task_sender, 412 task_sender,
442 msg_sender, 413 msg_sender,
443 pending_requests, 414 incoming_requests,
444 request_received, 415 request_received,
445 }; 416 };
446 pool_dispatcher 417 pool_dispatcher
@@ -504,12 +475,7 @@ fn on_notification(
504 NumberOrString::Number(id) => id.into(), 475 NumberOrString::Number(id) => id.into(),
505 NumberOrString::String(id) => id.into(), 476 NumberOrString::String(id) => id.into(),
506 }; 477 };
507 if loop_state.pending_requests.cancel(&id) { 478 if let Some(response) = loop_state.req_queue.incoming.cancel(id) {
508 let response = Response::new_err(
509 id,
510 ErrorCode::RequestCanceled as i32,
511 "canceled by client".to_string(),
512 );
513 msg_sender.send(response.into()).unwrap() 479 msg_sender.send(response.into()).unwrap()
514 } 480 }
515 return Ok(()); 481 return Ok(());
@@ -572,18 +538,36 @@ fn on_notification(
572 Ok(_) => { 538 Ok(_) => {
573 // As stated in https://github.com/microsoft/language-server-protocol/issues/676, 539 // As stated in https://github.com/microsoft/language-server-protocol/issues/676,
574 // this notification's parameters should be ignored and the actual config queried separately. 540 // this notification's parameters should be ignored and the actual config queried separately.
575 let request_id = loop_state.next_request_id(); 541 let request = loop_state.req_queue.outgoing.register(
576 let request = request_new::<lsp_types::request::WorkspaceConfiguration>( 542 lsp_types::request::WorkspaceConfiguration::METHOD.to_string(),
577 request_id.clone(),
578 lsp_types::ConfigurationParams { 543 lsp_types::ConfigurationParams {
579 items: vec![lsp_types::ConfigurationItem { 544 items: vec![lsp_types::ConfigurationItem {
580 scope_uri: None, 545 scope_uri: None,
581 section: Some("rust-analyzer".to_string()), 546 section: Some("rust-analyzer".to_string()),
582 }], 547 }],
583 }, 548 },
549 |global_state, resp| {
550 log::debug!("config update response: '{:?}", resp);
551 let Response { error, result, .. } = resp;
552
553 match (error, result) {
554 (Some(err), _) => {
555 log::error!("failed to fetch the server settings: {:?}", err)
556 }
557 (None, Some(configs)) => {
558 if let Some(new_config) = configs.get(0) {
559 let mut config = global_state.config.clone();
560 config.update(&new_config);
561 global_state.update_configuration(config);
562 }
563 }
564 (None, None) => {
565 log::error!("received empty server settings response from the client")
566 }
567 }
568 },
584 ); 569 );
585 msg_sender.send(request.into())?; 570 msg_sender.send(request.into())?;
586 loop_state.configuration_request_id = Some(request_id);
587 571
588 return Ok(()); 572 return Ok(());
589 } 573 }
@@ -752,13 +736,14 @@ fn send_startup_progress(sender: &Sender<Message>, loop_state: &mut LoopState) {
752 736
753 match (prev, loop_state.workspace_loaded) { 737 match (prev, loop_state.workspace_loaded) {
754 (None, false) => { 738 (None, false) => {
755 let work_done_progress_create = request_new::<lsp_types::request::WorkDoneProgressCreate>( 739 let request = loop_state.req_queue.outgoing.register(
756 loop_state.next_request_id(), 740 lsp_types::request::WorkDoneProgressCreate::METHOD.to_string(),
757 WorkDoneProgressCreateParams { 741 WorkDoneProgressCreateParams {
758 token: lsp_types::ProgressToken::String("rustAnalyzer/startup".into()), 742 token: lsp_types::ProgressToken::String("rustAnalyzer/startup".into()),
759 }, 743 },
744 DO_NOTHING,
760 ); 745 );
761 sender.send(work_done_progress_create.into()).unwrap(); 746 sender.send(request.into()).unwrap();
762 send_startup_progress_notif( 747 send_startup_progress_notif(
763 sender, 748 sender,
764 WorkDoneProgress::Begin(WorkDoneProgressBegin { 749 WorkDoneProgress::Begin(WorkDoneProgressBegin {
@@ -800,7 +785,7 @@ struct PoolDispatcher<'a> {
800 req: Option<Request>, 785 req: Option<Request>,
801 pool: &'a ThreadPool, 786 pool: &'a ThreadPool,
802 global_state: &'a mut GlobalState, 787 global_state: &'a mut GlobalState,
803 pending_requests: &'a mut PendingRequests, 788 incoming_requests: &'a mut Incoming,
804 msg_sender: &'a Sender<Message>, 789 msg_sender: &'a Sender<Message>,
805 task_sender: &'a Sender<Task>, 790 task_sender: &'a Sender<Task>,
806 request_received: Instant, 791 request_received: Instant,
@@ -829,7 +814,7 @@ impl<'a> PoolDispatcher<'a> {
829 result_to_task::<R>(id, result) 814 result_to_task::<R>(id, result)
830 }) 815 })
831 .map_err(|_| format!("sync task {:?} panicked", R::METHOD))?; 816 .map_err(|_| format!("sync task {:?} panicked", R::METHOD))?;
832 on_task(task, self.msg_sender, self.pending_requests, self.global_state); 817 on_task(task, self.msg_sender, self.incoming_requests, self.global_state);
833 Ok(self) 818 Ok(self)
834 } 819 }
835 820
@@ -876,11 +861,7 @@ impl<'a> PoolDispatcher<'a> {
876 return None; 861 return None;
877 } 862 }
878 }; 863 };
879 self.pending_requests.start(PendingRequest { 864 self.incoming_requests.register(id.clone(), (R::METHOD, self.request_received));
880 id: id.clone(),
881 method: R::METHOD.to_string(),
882 received: self.request_received,
883 });
884 Some((id, params)) 865 Some((id, params))
885 } 866 }
886 867
@@ -993,14 +974,6 @@ where
993 Notification::new(N::METHOD.to_string(), params) 974 Notification::new(N::METHOD.to_string(), params)
994} 975}
995 976
996fn request_new<R>(id: RequestId, params: R::Params) -> Request
997where
998 R: lsp_types::request::Request,
999 R::Params: Serialize,
1000{
1001 Request::new(id, R::METHOD.to_string(), params)
1002}
1003
1004#[cfg(test)] 977#[cfg(test)]
1005mod tests { 978mod tests {
1006 use std::borrow::Cow; 979 use std::borrow::Cow;
diff --git a/crates/rust-analyzer/src/main_loop/pending_requests.rs b/crates/rust-analyzer/src/main_loop/pending_requests.rs
deleted file mode 100644
index 73b33e419..000000000
--- a/crates/rust-analyzer/src/main_loop/pending_requests.rs
+++ /dev/null
@@ -1,75 +0,0 @@
1//! Data structures that keep track of inflight requests.
2
3use std::time::{Duration, Instant};
4
5use lsp_server::RequestId;
6use rustc_hash::FxHashMap;
7
8#[derive(Debug)]
9pub struct CompletedRequest {
10 pub id: RequestId,
11 pub method: String,
12 pub duration: Duration,
13}
14
15#[derive(Debug)]
16pub(crate) struct PendingRequest {
17 pub(crate) id: RequestId,
18 pub(crate) method: String,
19 pub(crate) received: Instant,
20}
21
22impl From<PendingRequest> for CompletedRequest {
23 fn from(pending: PendingRequest) -> CompletedRequest {
24 CompletedRequest {
25 id: pending.id,
26 method: pending.method,
27 duration: pending.received.elapsed(),
28 }
29 }
30}
31
32#[derive(Debug, Default)]
33pub(crate) struct PendingRequests {
34 map: FxHashMap<RequestId, PendingRequest>,
35}
36
37impl PendingRequests {
38 pub(crate) fn start(&mut self, request: PendingRequest) {
39 let id = request.id.clone();
40 let prev = self.map.insert(id.clone(), request);
41 assert!(prev.is_none(), "duplicate request with id {}", id);
42 }
43 pub(crate) fn cancel(&mut self, id: &RequestId) -> bool {
44 self.map.remove(id).is_some()
45 }
46 pub(crate) fn finish(&mut self, id: &RequestId) -> Option<CompletedRequest> {
47 self.map.remove(id).map(CompletedRequest::from)
48 }
49}
50
51const N_COMPLETED_REQUESTS: usize = 10;
52
53#[derive(Debug, Default)]
54pub struct LatestRequests {
55 // hand-rolling VecDeque here to print things in a nicer way
56 buf: [Option<CompletedRequest>; N_COMPLETED_REQUESTS],
57 idx: usize,
58}
59
60impl LatestRequests {
61 pub(crate) fn record(&mut self, request: CompletedRequest) {
62 // special case: don't track status request itself
63 if request.method == "rust-analyzer/analyzerStatus" {
64 return;
65 }
66 let idx = self.idx;
67 self.buf[idx] = Some(request);
68 self.idx = (idx + 1) % N_COMPLETED_REQUESTS;
69 }
70
71 pub(crate) fn iter(&self) -> impl Iterator<Item = (bool, &CompletedRequest)> {
72 let idx = self.idx;
73 self.buf.iter().enumerate().filter_map(move |(i, req)| Some((i == idx, req.as_ref()?)))
74 }
75}
diff --git a/crates/rust-analyzer/src/main_loop/request_metrics.rs b/crates/rust-analyzer/src/main_loop/request_metrics.rs
new file mode 100644
index 000000000..b1019e2d6
--- /dev/null
+++ b/crates/rust-analyzer/src/main_loop/request_metrics.rs
@@ -0,0 +1,37 @@
1//! Records stats about requests
2use std::time::Duration;
3
4use lsp_server::RequestId;
5
6#[derive(Debug)]
7pub(crate) struct RequestMetrics {
8 pub(crate) id: RequestId,
9 pub(crate) method: String,
10 pub(crate) duration: Duration,
11}
12
13const N_COMPLETED_REQUESTS: usize = 10;
14
15#[derive(Debug, Default)]
16pub(crate) struct LatestRequests {
17 // hand-rolling VecDeque here to print things in a nicer way
18 buf: [Option<RequestMetrics>; N_COMPLETED_REQUESTS],
19 idx: usize,
20}
21
22impl LatestRequests {
23 pub(crate) fn record(&mut self, request: RequestMetrics) {
24 // special case: don't track status request itself
25 if request.method == "rust-analyzer/analyzerStatus" {
26 return;
27 }
28 let idx = self.idx;
29 self.buf[idx] = Some(request);
30 self.idx = (idx + 1) % N_COMPLETED_REQUESTS;
31 }
32
33 pub(crate) fn iter(&self) -> impl Iterator<Item = (bool, &RequestMetrics)> {
34 let idx = self.idx;
35 self.buf.iter().enumerate().filter_map(move |(i, req)| Some((i == idx, req.as_ref()?)))
36 }
37}
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 270fbcb64..670f2ebfd 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -42,7 +42,16 @@ export async function activate(context: vscode.ExtensionContext) {
42 42
43 const config = new Config(context); 43 const config = new Config(context);
44 const state = new PersistentState(context.globalState); 44 const state = new PersistentState(context.globalState);
45 const serverPath = await bootstrap(config, state); 45 const serverPath = await bootstrap(config, state).catch(err => {
46 let message = "Failed to bootstrap rust-analyzer.";
47 if (err.code === "EBUSY" || err.code === "ETXTBSY") {
48 message += " Other vscode windows might be using rust-analyzer, " +
49 "you should close them and reload this window to retry.";
50 }
51 message += " Open \"Help > Toggle Developer Tools > Console\" to see the logs";
52 log.error("Bootstrap error", err);
53 throw new Error(message);
54 });
46 55
47 const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; 56 const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
48 if (workspaceFolder === undefined) { 57 if (workspaceFolder === undefined) {
@@ -285,6 +294,11 @@ async function getServer(config: Config, state: PersistentState): Promise<string
285 const artifact = release.assets.find(artifact => artifact.name === binaryName); 294 const artifact = release.assets.find(artifact => artifact.name === binaryName);
286 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); 295 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
287 296
297 // Unlinking the exe file before moving new one on its place should prevent ETXTBSY error.
298 await fs.unlink(dest).catch(err => {
299 if (err.code !== "ENOENT") throw err;
300 });
301
288 await download(artifact.browser_download_url, dest, "Downloading rust-analyzer server", { mode: 0o755 }); 302 await download(artifact.browser_download_url, dest, "Downloading rust-analyzer server", { mode: 0o755 });
289 303
290 // Patching executable if that's NixOS. 304 // Patching executable if that's NixOS.
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts
index f0b085420..0e7dd29c2 100644
--- a/editors/code/src/net.ts
+++ b/editors/code/src/net.ts
@@ -1,7 +1,9 @@
1import fetch from "node-fetch"; 1import fetch from "node-fetch";
2import * as vscode from "vscode"; 2import * as vscode from "vscode";
3import * as fs from "fs";
4import * as stream from "stream"; 3import * as stream from "stream";
4import * as fs from "fs";
5import * as os from "os";
6import * as path from "path";
5import * as util from "util"; 7import * as util from "util";
6import { log, assert } from "./util"; 8import { log, assert } from "./util";
7 9
@@ -87,7 +89,7 @@ export async function download(
87} 89}
88 90
89/** 91/**
90 * Downloads file from `url` and stores it at `destFilePath` with `destFilePermissions`. 92 * Downloads file from `url` and stores it at `destFilePath` with `mode` (unix permissions).
91 * `onProgress` callback is called on recieveing each chunk of bytes 93 * `onProgress` callback is called on recieveing each chunk of bytes
92 * to track the progress of downloading, it gets the already read and total 94 * to track the progress of downloading, it gets the already read and total
93 * amount of bytes to read as its parameters. 95 * amount of bytes to read as its parameters.
@@ -118,13 +120,46 @@ async function downloadFile(
118 onProgress(readBytes, totalBytes); 120 onProgress(readBytes, totalBytes);
119 }); 121 });
120 122
121 const destFileStream = fs.createWriteStream(destFilePath, { mode }); 123 // Put the artifact into a temporary folder to prevent partially downloaded files when user kills vscode
122 124 await withTempFile(async tempFilePath => {
123 await pipeline(res.body, destFileStream); 125 const destFileStream = fs.createWriteStream(tempFilePath, { mode });
124 return new Promise<void>(resolve => { 126 await pipeline(res.body, destFileStream);
125 destFileStream.on("close", resolve); 127 await new Promise<void>(resolve => {
126 destFileStream.destroy(); 128 destFileStream.on("close", resolve);
127 // This workaround is awaiting to be removed when vscode moves to newer nodejs version: 129 destFileStream.destroy();
128 // https://github.com/rust-analyzer/rust-analyzer/issues/3167 130 // This workaround is awaiting to be removed when vscode moves to newer nodejs version:
131 // https://github.com/rust-analyzer/rust-analyzer/issues/3167
132 });
133 await moveFile(tempFilePath, destFilePath);
129 }); 134 });
130} 135}
136
137async function withTempFile(scope: (tempFilePath: string) => Promise<void>) {
138 // Based on the great article: https://advancedweb.hu/secure-tempfiles-in-nodejs-without-dependencies/
139
140 // `.realpath()` should handle the cases where os.tmpdir() contains symlinks
141 const osTempDir = await fs.promises.realpath(os.tmpdir());
142
143 const tempDir = await fs.promises.mkdtemp(path.join(osTempDir, "rust-analyzer"));
144
145 try {
146 return await scope(path.join(tempDir, "file"));
147 } finally {
148 // We are good citizens :D
149 void fs.promises.rmdir(tempDir, { recursive: true }).catch(log.error);
150 }
151};
152
153async function moveFile(src: fs.PathLike, dest: fs.PathLike) {
154 try {
155 await fs.promises.rename(src, dest);
156 } catch (err) {
157 if (err.code === 'EXDEV') {
158 // We are probably moving the file across partitions/devices
159 await fs.promises.copyFile(src, dest);
160 await fs.promises.unlink(src);
161 } else {
162 log.error(`Failed to rename the file ${src} -> ${dest}`, err);
163 }
164 }
165}