diff options
48 files changed, 2321 insertions, 348 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]] |
642 | name = "lsp-server" | 642 | name = "lsp-server" |
643 | version = "0.3.2" | 643 | version = "0.3.3" |
644 | source = "registry+https://github.com/rust-lang/crates.io-index" | 644 | source = "registry+https://github.com/rust-lang/crates.io-index" |
645 | checksum = "dccec31bfd027ac0dd288a78e19005fd89624d9099456e284b5241316a6c3072" | 645 | checksum = "53b4ace8ebe5d2aff3687ce0ed507f6020d6a47a7de2b0d3d664ea237ffb0c62" |
646 | dependencies = [ | 646 | dependencies = [ |
647 | "crossbeam-channel", | 647 | "crossbeam-channel", |
648 | "log", | 648 | "log", |
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index c7ce0c42f..190c50913 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | //! relative paths. | 2 | //! relative paths. |
3 | use std::{ | 3 | use std::{ |
4 | convert::{TryFrom, TryInto}, | 4 | convert::{TryFrom, TryInto}, |
5 | ops, | 5 | io, ops, |
6 | path::{Component, Path, PathBuf}, | 6 | path::{Component, Path, PathBuf}, |
7 | }; | 7 | }; |
8 | 8 | ||
@@ -46,6 +46,9 @@ impl TryFrom<&str> for AbsPathBuf { | |||
46 | } | 46 | } |
47 | 47 | ||
48 | impl AbsPathBuf { | 48 | impl AbsPathBuf { |
49 | pub fn canonicalized(path: &Path) -> io::Result<AbsPathBuf> { | ||
50 | path.canonicalize().map(|it| AbsPathBuf::try_from(it).unwrap()) | ||
51 | } | ||
49 | pub fn as_path(&self) -> &AbsPath { | 52 | pub fn as_path(&self) -> &AbsPath { |
50 | AbsPath::new_unchecked(self.0.as_path()) | 53 | AbsPath::new_unchecked(self.0.as_path()) |
51 | } | 54 | } |
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/code_model.rs b/crates/ra_hir/src/code_model.rs index 1a9f6cc76..ffd5278ec 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -26,8 +26,8 @@ use hir_ty::{ | |||
26 | autoderef, | 26 | autoderef, |
27 | display::{HirDisplayError, HirFormatter}, | 27 | display::{HirDisplayError, HirFormatter}, |
28 | expr::ExprValidator, | 28 | expr::ExprValidator, |
29 | method_resolution, ApplicationTy, Canonical, InEnvironment, Substs, TraitEnvironment, Ty, | 29 | method_resolution, ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs, |
30 | TyDefId, TypeCtor, | 30 | TraitEnvironment, Ty, TyDefId, TypeCtor, |
31 | }; | 31 | }; |
32 | use ra_db::{CrateId, CrateName, Edition, FileId}; | 32 | use ra_db::{CrateId, CrateName, Edition, FileId}; |
33 | use ra_prof::profile; | 33 | use ra_prof::profile; |
@@ -186,6 +186,22 @@ impl ModuleDef { | |||
186 | 186 | ||
187 | module.visibility_of(db, self) | 187 | module.visibility_of(db, self) |
188 | } | 188 | } |
189 | |||
190 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
191 | match self { | ||
192 | ModuleDef::Adt(it) => Some(it.name(db)), | ||
193 | ModuleDef::Trait(it) => Some(it.name(db)), | ||
194 | ModuleDef::Function(it) => Some(it.name(db)), | ||
195 | ModuleDef::EnumVariant(it) => Some(it.name(db)), | ||
196 | ModuleDef::TypeAlias(it) => Some(it.name(db)), | ||
197 | |||
198 | ModuleDef::Module(it) => it.name(db), | ||
199 | ModuleDef::Const(it) => it.name(db), | ||
200 | ModuleDef::Static(it) => it.name(db), | ||
201 | |||
202 | ModuleDef::BuiltinType(it) => Some(it.as_name()), | ||
203 | } | ||
204 | } | ||
189 | } | 205 | } |
190 | 206 | ||
191 | pub use hir_def::{ | 207 | pub use hir_def::{ |
@@ -1359,6 +1375,27 @@ impl Type { | |||
1359 | Some(adt.into()) | 1375 | Some(adt.into()) |
1360 | } | 1376 | } |
1361 | 1377 | ||
1378 | pub fn as_dyn_trait(&self) -> Option<Trait> { | ||
1379 | self.ty.value.dyn_trait().map(Into::into) | ||
1380 | } | ||
1381 | |||
1382 | pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> { | ||
1383 | self.ty.value.impl_trait_bounds(db).map(|it| { | ||
1384 | it.into_iter() | ||
1385 | .filter_map(|pred| match pred { | ||
1386 | hir_ty::GenericPredicate::Implemented(trait_ref) => { | ||
1387 | Some(Trait::from(trait_ref.trait_)) | ||
1388 | } | ||
1389 | _ => None, | ||
1390 | }) | ||
1391 | .collect() | ||
1392 | }) | ||
1393 | } | ||
1394 | |||
1395 | pub fn as_associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<Trait> { | ||
1396 | self.ty.value.associated_type_parent_trait(db).map(Into::into) | ||
1397 | } | ||
1398 | |||
1362 | // FIXME: provide required accessors such that it becomes implementable from outside. | 1399 | // FIXME: provide required accessors such that it becomes implementable from outside. |
1363 | pub fn is_equal_for_find_impls(&self, other: &Type) -> bool { | 1400 | pub fn is_equal_for_find_impls(&self, other: &Type) -> bool { |
1364 | match (&self.ty.value, &other.ty.value) { | 1401 | match (&self.ty.value, &other.ty.value) { |
@@ -1380,6 +1417,80 @@ impl Type { | |||
1380 | ty: InEnvironment { value: ty, environment: self.ty.environment.clone() }, | 1417 | ty: InEnvironment { value: ty, environment: self.ty.environment.clone() }, |
1381 | } | 1418 | } |
1382 | } | 1419 | } |
1420 | |||
1421 | pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) { | ||
1422 | // TypeWalk::walk for a Ty at first visits parameters and only after that the Ty itself. | ||
1423 | // We need a different order here. | ||
1424 | |||
1425 | fn walk_substs( | ||
1426 | db: &dyn HirDatabase, | ||
1427 | type_: &Type, | ||
1428 | substs: &Substs, | ||
1429 | cb: &mut impl FnMut(Type), | ||
1430 | ) { | ||
1431 | for ty in substs.iter() { | ||
1432 | walk_type(db, &type_.derived(ty.clone()), cb); | ||
1433 | } | ||
1434 | } | ||
1435 | |||
1436 | fn walk_bounds( | ||
1437 | db: &dyn HirDatabase, | ||
1438 | type_: &Type, | ||
1439 | bounds: &[GenericPredicate], | ||
1440 | cb: &mut impl FnMut(Type), | ||
1441 | ) { | ||
1442 | for pred in bounds { | ||
1443 | match pred { | ||
1444 | GenericPredicate::Implemented(trait_ref) => { | ||
1445 | cb(type_.clone()); | ||
1446 | walk_substs(db, type_, &trait_ref.substs, cb); | ||
1447 | } | ||
1448 | _ => (), | ||
1449 | } | ||
1450 | } | ||
1451 | } | ||
1452 | |||
1453 | fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) { | ||
1454 | let ty = type_.ty.value.strip_references(); | ||
1455 | match ty { | ||
1456 | Ty::Apply(ApplicationTy { ctor, parameters }) => { | ||
1457 | match ctor { | ||
1458 | TypeCtor::Adt(_) => { | ||
1459 | cb(type_.derived(ty.clone())); | ||
1460 | } | ||
1461 | TypeCtor::AssociatedType(_) => { | ||
1462 | if let Some(_) = ty.associated_type_parent_trait(db) { | ||
1463 | cb(type_.derived(ty.clone())); | ||
1464 | } | ||
1465 | } | ||
1466 | _ => (), | ||
1467 | } | ||
1468 | |||
1469 | // adt params, tuples, etc... | ||
1470 | walk_substs(db, type_, parameters, cb); | ||
1471 | } | ||
1472 | Ty::Opaque(opaque_ty) => { | ||
1473 | if let Some(bounds) = ty.impl_trait_bounds(db) { | ||
1474 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); | ||
1475 | } | ||
1476 | |||
1477 | walk_substs(db, type_, &opaque_ty.parameters, cb); | ||
1478 | } | ||
1479 | Ty::Placeholder(_) => { | ||
1480 | if let Some(bounds) = ty.impl_trait_bounds(db) { | ||
1481 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); | ||
1482 | } | ||
1483 | } | ||
1484 | Ty::Dyn(bounds) => { | ||
1485 | walk_bounds(db, &type_.derived(ty.clone()), bounds.as_ref(), cb); | ||
1486 | } | ||
1487 | |||
1488 | _ => (), | ||
1489 | } | ||
1490 | } | ||
1491 | |||
1492 | walk_type(db, self, &mut cb); | ||
1493 | } | ||
1383 | } | 1494 | } |
1384 | 1495 | ||
1385 | impl HirDisplay for Type { | 1496 | impl HirDisplay for Type { |
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::{ | |||
16 | pub use hir_ty::db::{ | 16 | pub 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/src/semantics.rs b/crates/ra_hir/src/semantics.rs index a232a5856..6a49c424a 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -6,9 +6,9 @@ use std::{cell::RefCell, fmt, iter::successors}; | |||
6 | 6 | ||
7 | use hir_def::{ | 7 | use hir_def::{ |
8 | resolver::{self, HasResolver, Resolver}, | 8 | resolver::{self, HasResolver, Resolver}, |
9 | AsMacroCall, TraitId, | 9 | AsMacroCall, TraitId, VariantId, |
10 | }; | 10 | }; |
11 | use hir_expand::{hygiene::Hygiene, ExpansionInfo}; | 11 | use hir_expand::{diagnostics::AstDiagnostic, hygiene::Hygiene, ExpansionInfo}; |
12 | use hir_ty::associated_type_shorthand_candidates; | 12 | use hir_ty::associated_type_shorthand_candidates; |
13 | use itertools::Itertools; | 13 | use itertools::Itertools; |
14 | use ra_db::{FileId, FileRange}; | 14 | use ra_db::{FileId, FileRange}; |
@@ -104,6 +104,13 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
104 | tree | 104 | tree |
105 | } | 105 | } |
106 | 106 | ||
107 | pub fn ast<T: AstDiagnostic + Diagnostic>(&self, d: &T) -> <T as AstDiagnostic>::AST { | ||
108 | let file_id = d.source().file_id; | ||
109 | let root = self.db.parse_or_expand(file_id).unwrap(); | ||
110 | self.cache(root, file_id); | ||
111 | d.ast(self.db) | ||
112 | } | ||
113 | |||
107 | pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { | 114 | pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { |
108 | let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call); | 115 | let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call); |
109 | let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); | 116 | let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); |
@@ -247,6 +254,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
247 | self.analyze(path.syntax()).resolve_path(self.db, path) | 254 | self.analyze(path.syntax()).resolve_path(self.db, path) |
248 | } | 255 | } |
249 | 256 | ||
257 | pub fn resolve_variant(&self, record_lit: ast::RecordLit) -> Option<VariantId> { | ||
258 | self.analyze(record_lit.syntax()).resolve_variant(self.db, record_lit) | ||
259 | } | ||
260 | |||
250 | pub fn lower_path(&self, path: &ast::Path) -> Option<Path> { | 261 | pub fn lower_path(&self, path: &ast::Path) -> Option<Path> { |
251 | let src = self.find_file(path.syntax().clone()); | 262 | let src = self.find_file(path.syntax().clone()); |
252 | Path::from_src(path.clone(), &Hygiene::new(self.db.upcast(), src.file_id.into())) | 263 | Path::from_src(path.clone(), &Hygiene::new(self.db.upcast(), src.file_id.into())) |
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index 7c6bbea13..757d1e397 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs | |||
@@ -313,6 +313,16 @@ impl SourceAnalyzer { | |||
313 | })?; | 313 | })?; |
314 | Some(macro_call_id.as_file()) | 314 | Some(macro_call_id.as_file()) |
315 | } | 315 | } |
316 | |||
317 | pub(crate) fn resolve_variant( | ||
318 | &self, | ||
319 | db: &dyn HirDatabase, | ||
320 | record_lit: ast::RecordLit, | ||
321 | ) -> Option<VariantId> { | ||
322 | let infer = self.infer.as_ref()?; | ||
323 | let expr_id = self.expr_id(db, &record_lit.into())?; | ||
324 | infer.variant_resolution_for_expr(expr_id) | ||
325 | } | ||
316 | } | 326 | } |
317 | 327 | ||
318 | fn scope_for( | 328 | fn scope_for( |
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); | |||
159 | type TypeAliasLoc = AssocItemLoc<ast::TypeAliasDef>; | 159 | type TypeAliasLoc = AssocItemLoc<ast::TypeAliasDef>; |
160 | impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias); | 160 | impl_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)] |
163 | pub struct ImplId(salsa::InternId); | 163 | pub struct ImplId(salsa::InternId); |
164 | type ImplLoc = ItemLoc<ast::ImplDef>; | 164 | type ImplLoc = ItemLoc<ast::ImplDef>; |
165 | impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl); | 165 | impl_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 @@ | |||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use hir_def::{ | 5 | use 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 | }; |
9 | use ra_arena::map::ArenaMap; | 9 | use ra_arena::map::ArenaMap; |
10 | use ra_db::{impl_intern_key, salsa, CrateId, Upcast}; | 10 | use ra_db::{impl_intern_key, salsa, CrateId, Upcast}; |
11 | use ra_prof::profile; | 11 | use ra_prof::profile; |
12 | 12 | ||
13 | use crate::{ | 13 | use 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/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index 2c7298714..ebd9cb08f 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs | |||
@@ -6,7 +6,7 @@ use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; | |||
6 | use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; | 6 | use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; |
7 | use stdx::format_to; | 7 | use stdx::format_to; |
8 | 8 | ||
9 | pub use hir_def::{diagnostics::UnresolvedModule, expr::MatchArm}; | 9 | pub use hir_def::{diagnostics::UnresolvedModule, expr::MatchArm, path::Path}; |
10 | pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; | 10 | pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; |
11 | 11 | ||
12 | #[derive(Debug)] | 12 | #[derive(Debug)] |
@@ -29,6 +29,16 @@ impl Diagnostic for NoSuchField { | |||
29 | } | 29 | } |
30 | } | 30 | } |
31 | 31 | ||
32 | impl AstDiagnostic for NoSuchField { | ||
33 | type AST = ast::RecordField; | ||
34 | |||
35 | fn ast(&self, db: &impl AstDatabase) -> Self::AST { | ||
36 | let root = db.parse_or_expand(self.source().file_id).unwrap(); | ||
37 | let node = self.source().value.to_node(&root); | ||
38 | ast::RecordField::cast(node).unwrap() | ||
39 | } | ||
40 | } | ||
41 | |||
32 | #[derive(Debug)] | 42 | #[derive(Debug)] |
33 | pub struct MissingFields { | 43 | pub struct MissingFields { |
34 | pub file: HirFileId, | 44 | pub file: HirFileId, |
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 | }; |
13 | use hir_expand::name::Name; | 13 | use hir_expand::name::{name, Name}; |
14 | use ra_syntax::ast::RangeOp; | 14 | use ra_syntax::ast::RangeOp; |
15 | 15 | ||
16 | use crate::{ | 16 | use 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, ¶m_tys); | 271 | self.check_call_arguments(args, ¶m_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/infer/path.rs b/crates/ra_hir_ty/src/infer/path.rs index 1ad0d8397..80d7ed10e 100644 --- a/crates/ra_hir_ty/src/infer/path.rs +++ b/crates/ra_hir_ty/src/infer/path.rs | |||
@@ -81,7 +81,7 @@ impl<'a> InferenceContext<'a> { | |||
81 | let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); | 81 | let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); |
82 | let substs = Substs::type_params_for_generics(&generics); | 82 | let substs = Substs::type_params_for_generics(&generics); |
83 | let ty = self.db.impl_self_ty(impl_id).subst(&substs); | 83 | let ty = self.db.impl_self_ty(impl_id).subst(&substs); |
84 | if let Some((AdtId::StructId(struct_id), _)) = ty.as_adt() { | 84 | if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() { |
85 | let ty = self.db.value_ty(struct_id.into()).subst(&substs); | 85 | let ty = self.db.value_ty(struct_id.into()).subst(&substs); |
86 | return Some(ty); | 86 | return Some(ty); |
87 | } else { | 87 | } else { |
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 2b9372b4b..f22232324 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -73,6 +73,7 @@ pub use lower::{ | |||
73 | pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; | 73 | pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; |
74 | 74 | ||
75 | pub use chalk_ir::{BoundVar, DebruijnIndex}; | 75 | pub use chalk_ir::{BoundVar, DebruijnIndex}; |
76 | use itertools::Itertools; | ||
76 | 77 | ||
77 | /// A type constructor or type name: this might be something like the primitive | 78 | /// A type constructor or type name: this might be something like the primitive |
78 | /// type `bool`, a struct like `Vec`, or things like function pointers or | 79 | /// type `bool`, a struct like `Vec`, or things like function pointers or |
@@ -815,6 +816,11 @@ impl Ty { | |||
815 | } | 816 | } |
816 | } | 817 | } |
817 | 818 | ||
819 | /// If this is a `dyn Trait`, returns that trait. | ||
820 | pub fn dyn_trait(&self) -> Option<TraitId> { | ||
821 | self.dyn_trait_ref().map(|it| it.trait_) | ||
822 | } | ||
823 | |||
818 | fn builtin_deref(&self) -> Option<Ty> { | 824 | fn builtin_deref(&self) -> Option<Ty> { |
819 | match self { | 825 | match self { |
820 | Ty::Apply(a_ty) => match a_ty.ctor { | 826 | Ty::Apply(a_ty) => match a_ty.ctor { |
@@ -867,13 +873,56 @@ impl Ty { | |||
867 | } | 873 | } |
868 | } | 874 | } |
869 | 875 | ||
870 | /// If this is a `dyn Trait`, returns that trait. | 876 | pub fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<GenericPredicate>> { |
871 | pub fn dyn_trait(&self) -> Option<TraitId> { | ||
872 | match self { | 877 | match self { |
873 | Ty::Dyn(predicates) => predicates.iter().find_map(|pred| match pred { | 878 | Ty::Opaque(opaque_ty) => { |
874 | GenericPredicate::Implemented(tr) => Some(tr.trait_), | 879 | let predicates = match opaque_ty.opaque_ty_id { |
875 | _ => None, | 880 | OpaqueTyId::ReturnTypeImplTrait(func, idx) => { |
876 | }), | 881 | db.return_type_impl_traits(func).map(|it| { |
882 | let data = (*it) | ||
883 | .as_ref() | ||
884 | .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); | ||
885 | data.clone().subst(&opaque_ty.parameters) | ||
886 | }) | ||
887 | } | ||
888 | }; | ||
889 | |||
890 | predicates.map(|it| it.value) | ||
891 | } | ||
892 | Ty::Placeholder(id) => { | ||
893 | let generic_params = db.generic_params(id.parent); | ||
894 | let param_data = &generic_params.types[id.local_id]; | ||
895 | match param_data.provenance { | ||
896 | hir_def::generics::TypeParamProvenance::ArgumentImplTrait => { | ||
897 | let predicates = db | ||
898 | .generic_predicates_for_param(*id) | ||
899 | .into_iter() | ||
900 | .map(|pred| pred.value.clone()) | ||
901 | .collect_vec(); | ||
902 | |||
903 | Some(predicates) | ||
904 | } | ||
905 | _ => None, | ||
906 | } | ||
907 | } | ||
908 | _ => None, | ||
909 | } | ||
910 | } | ||
911 | |||
912 | pub fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId> { | ||
913 | match self { | ||
914 | Ty::Apply(ApplicationTy { ctor: TypeCtor::AssociatedType(type_alias_id), .. }) => { | ||
915 | match type_alias_id.lookup(db.upcast()).container { | ||
916 | AssocContainerId::TraitId(trait_id) => Some(trait_id), | ||
917 | _ => None, | ||
918 | } | ||
919 | } | ||
920 | Ty::Projection(projection_ty) => { | ||
921 | match projection_ty.associated_ty.lookup(db.upcast()).container { | ||
922 | AssocContainerId::TraitId(trait_id) => Some(trait_id), | ||
923 | _ => None, | ||
924 | } | ||
925 | } | ||
877 | _ => None, | 926 | _ => None, |
878 | } | 927 | } |
879 | } | 928 | } |
@@ -1057,5 +1106,5 @@ pub struct ReturnTypeImplTraits { | |||
1057 | 1106 | ||
1058 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | 1107 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
1059 | pub(crate) struct ReturnTypeImplTrait { | 1108 | pub(crate) struct ReturnTypeImplTrait { |
1060 | pub(crate) bounds: Binders<Vec<GenericPredicate>>, | 1109 | pub bounds: Binders<Vec<GenericPredicate>>, |
1061 | } | 1110 | } |
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index 42713928f..d5154f436 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs | |||
@@ -337,17 +337,17 @@ impl Ty { | |||
337 | TraitRef::from_resolved_path(ctx, trait_, resolved_segment, self_ty); | 337 | TraitRef::from_resolved_path(ctx, trait_, resolved_segment, self_ty); |
338 | let ty = if remaining_segments.len() == 1 { | 338 | let ty = if remaining_segments.len() == 1 { |
339 | let segment = remaining_segments.first().unwrap(); | 339 | let segment = remaining_segments.first().unwrap(); |
340 | let associated_ty = associated_type_by_name_including_super_traits( | 340 | let found = associated_type_by_name_including_super_traits( |
341 | ctx.db.upcast(), | 341 | ctx.db, |
342 | trait_ref.trait_, | 342 | trait_ref.clone(), |
343 | &segment.name, | 343 | &segment.name, |
344 | ); | 344 | ); |
345 | match associated_ty { | 345 | match found { |
346 | Some(associated_ty) => { | 346 | Some((super_trait_ref, associated_ty)) => { |
347 | // FIXME handle type parameters on the segment | 347 | // FIXME handle type parameters on the segment |
348 | Ty::Projection(ProjectionTy { | 348 | Ty::Projection(ProjectionTy { |
349 | associated_ty, | 349 | associated_ty, |
350 | parameters: trait_ref.substs, | 350 | parameters: super_trait_ref.substs, |
351 | }) | 351 | }) |
352 | } | 352 | } |
353 | None => { | 353 | None => { |
@@ -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, |
@@ -706,17 +709,17 @@ fn assoc_type_bindings_from_type_bound<'a>( | |||
706 | .flat_map(|segment| segment.args_and_bindings.into_iter()) | 709 | .flat_map(|segment| segment.args_and_bindings.into_iter()) |
707 | .flat_map(|args_and_bindings| args_and_bindings.bindings.iter()) | 710 | .flat_map(|args_and_bindings| args_and_bindings.bindings.iter()) |
708 | .flat_map(move |binding| { | 711 | .flat_map(move |binding| { |
709 | let associated_ty = associated_type_by_name_including_super_traits( | 712 | let found = associated_type_by_name_including_super_traits( |
710 | ctx.db.upcast(), | 713 | ctx.db, |
711 | trait_ref.trait_, | 714 | trait_ref.clone(), |
712 | &binding.name, | 715 | &binding.name, |
713 | ); | 716 | ); |
714 | let associated_ty = match associated_ty { | 717 | let (super_trait_ref, associated_ty) = match found { |
715 | None => return SmallVec::<[GenericPredicate; 1]>::new(), | 718 | None => return SmallVec::<[GenericPredicate; 1]>::new(), |
716 | Some(t) => t, | 719 | Some(t) => t, |
717 | }; | 720 | }; |
718 | let projection_ty = | 721 | let projection_ty = |
719 | ProjectionTy { associated_ty, parameters: trait_ref.substs.clone() }; | 722 | ProjectionTy { associated_ty, parameters: super_trait_ref.substs.clone() }; |
720 | let mut preds = SmallVec::with_capacity( | 723 | let mut preds = SmallVec::with_capacity( |
721 | binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), | 724 | binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), |
722 | ); | 725 | ); |
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)] |
42 | pub struct CrateImplDefs { | 43 | pub 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 | ||
47 | impl CrateImplDefs { | 48 | impl 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 1f004bd63..4da2e972b 100644 --- a/crates/ra_hir_ty/src/tests/regression.rs +++ b/crates/ra_hir_ty/src/tests/regression.rs | |||
@@ -633,3 +633,154 @@ where | |||
633 | "### | 633 | "### |
634 | ); | 634 | ); |
635 | } | 635 | } |
636 | |||
637 | #[test] | ||
638 | fn issue_4953() { | ||
639 | assert_snapshot!( | ||
640 | infer(r#" | ||
641 | pub struct Foo(pub i64); | ||
642 | impl Foo { | ||
643 | fn test() -> Self { Self(0i64) } | ||
644 | } | ||
645 | "#), | ||
646 | @r###" | ||
647 | 59..73 '{ Self(0i64) }': Foo | ||
648 | 61..65 'Self': Foo(i64) -> Foo | ||
649 | 61..71 'Self(0i64)': Foo | ||
650 | 66..70 '0i64': i64 | ||
651 | "### | ||
652 | ); | ||
653 | assert_snapshot!( | ||
654 | infer(r#" | ||
655 | pub struct Foo<T>(pub T); | ||
656 | impl Foo<i64> { | ||
657 | fn test() -> Self { Self(0i64) } | ||
658 | } | ||
659 | "#), | ||
660 | @r###" | ||
661 | 65..79 '{ Self(0i64) }': Foo<i64> | ||
662 | 67..71 'Self': Foo<i64>(i64) -> Foo<i64> | ||
663 | 67..77 'Self(0i64)': Foo<i64> | ||
664 | 72..76 '0i64': i64 | ||
665 | "### | ||
666 | ); | ||
667 | } | ||
668 | |||
669 | #[test] | ||
670 | fn issue_4931() { | ||
671 | assert_snapshot!( | ||
672 | infer(r#" | ||
673 | trait Div<T> { | ||
674 | type Output; | ||
675 | } | ||
676 | |||
677 | trait CheckedDiv: Div<()> {} | ||
678 | |||
679 | trait PrimInt: CheckedDiv<Output = ()> { | ||
680 | fn pow(self); | ||
681 | } | ||
682 | |||
683 | fn check<T: PrimInt>(i: T) { | ||
684 | i.pow(); | ||
685 | } | ||
686 | "#), | ||
687 | @r###" | ||
688 | 118..122 'self': Self | ||
689 | 149..150 'i': T | ||
690 | 155..171 '{ ...w(); }': () | ||
691 | 161..162 'i': T | ||
692 | 161..168 'i.pow()': () | ||
693 | "### | ||
694 | ); | ||
695 | } | ||
696 | |||
697 | #[test] | ||
698 | fn issue_4885() { | ||
699 | assert_snapshot!( | ||
700 | infer(r#" | ||
701 | #[lang = "coerce_unsized"] | ||
702 | pub trait CoerceUnsized<T> {} | ||
703 | |||
704 | trait Future { | ||
705 | type Output; | ||
706 | } | ||
707 | trait Foo<R> { | ||
708 | type Bar; | ||
709 | } | ||
710 | fn foo<R, K>(key: &K) -> impl Future<Output = K::Bar> | ||
711 | where | ||
712 | K: Foo<R>, | ||
713 | { | ||
714 | bar(key) | ||
715 | } | ||
716 | fn bar<R, K>(key: &K) -> impl Future<Output = K::Bar> | ||
717 | where | ||
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] | ||
735 | fn issue_4800() { | ||
736 | assert_snapshot!( | ||
737 | infer(r#" | ||
738 | trait Debug {} | ||
739 | |||
740 | struct Foo<T>; | ||
741 | |||
742 | type E1<T> = (T, T, T); | ||
743 | type E2<T> = E1<E1<E1<(T, T, T)>>>; | ||
744 | |||
745 | impl Debug for Foo<E2<()>> {} | ||
746 | |||
747 | struct Request; | ||
748 | |||
749 | pub trait Future { | ||
750 | type Output; | ||
751 | } | ||
752 | |||
753 | pub struct PeerSet<D>; | ||
754 | |||
755 | impl<D> Service<Request> for PeerSet<D> | ||
756 | where | ||
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 | |||
768 | pub trait Discover { | ||
769 | type Key; | ||
770 | } | ||
771 | |||
772 | pub 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] | ||
2893 | fn 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] | ||
2928 | fn 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] | ||
2977 | fn 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] | ||
3048 | fn 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 @@ | |||
2 | use std::{panic, sync::Arc}; | 2 | use std::{panic, sync::Arc}; |
3 | 3 | ||
4 | use chalk_ir::cast::Cast; | 4 | use chalk_ir::cast::Cast; |
5 | use hir_def::{expr::ExprId, DefWithBodyId, ImplId, TraitId, TypeAliasId}; | 5 | use hir_def::{ |
6 | expr::ExprId, lang_item::LangItemTarget, DefWithBodyId, ImplId, TraitId, TypeAliasId, | ||
7 | }; | ||
6 | use ra_db::{impl_intern_key, salsa, CrateId}; | 8 | use ra_db::{impl_intern_key, salsa, CrateId}; |
7 | use ra_prof::profile; | 9 | use ra_prof::profile; |
8 | use rustc_hash::FxHashSet; | ||
9 | 10 | ||
10 | use crate::{db::HirDatabase, method_resolution::TyFingerprint, DebruijnIndex}; | 11 | use crate::{db::HirDatabase, DebruijnIndex}; |
11 | 12 | ||
12 | use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk}; | 13 | use 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`. | ||
40 | pub(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 | ||
363 | fn 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 | |||
371 | fn get_unsize_trait(db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> { | 365 | fn 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_hir_ty/src/utils.rs b/crates/ra_hir_ty/src/utils.rs index f98350bf9..c45820ff0 100644 --- a/crates/ra_hir_ty/src/utils.rs +++ b/crates/ra_hir_ty/src/utils.rs | |||
@@ -143,13 +143,14 @@ pub(super) fn find_super_trait_path( | |||
143 | } | 143 | } |
144 | 144 | ||
145 | pub(super) fn associated_type_by_name_including_super_traits( | 145 | pub(super) fn associated_type_by_name_including_super_traits( |
146 | db: &dyn DefDatabase, | 146 | db: &dyn HirDatabase, |
147 | trait_: TraitId, | 147 | trait_ref: TraitRef, |
148 | name: &Name, | 148 | name: &Name, |
149 | ) -> Option<TypeAliasId> { | 149 | ) -> Option<(TraitRef, TypeAliasId)> { |
150 | all_super_traits(db, trait_) | 150 | all_super_trait_refs(db, trait_ref).into_iter().find_map(|t| { |
151 | .into_iter() | 151 | let assoc_type = db.trait_data(t.trait_).associated_type_by_name(name)?; |
152 | .find_map(|t| db.trait_data(t).associated_type_by_name(name)) | 152 | Some((t, assoc_type)) |
153 | }) | ||
153 | } | 154 | } |
154 | 155 | ||
155 | pub(super) fn variant_data(db: &dyn DefDatabase, var: VariantId) -> Arc<VariantData> { | 156 | pub(super) fn variant_data(db: &dyn DefDatabase, var: VariantId) -> Arc<VariantData> { |
@@ -176,6 +177,7 @@ pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics { | |||
176 | Generics { def, params: db.generic_params(def), parent_generics } | 177 | Generics { def, params: db.generic_params(def), parent_generics } |
177 | } | 178 | } |
178 | 179 | ||
180 | #[derive(Debug)] | ||
179 | pub(crate) struct Generics { | 181 | pub(crate) struct Generics { |
180 | def: GenericDefId, | 182 | def: GenericDefId, |
181 | pub(crate) params: Arc<GenericParams>, | 183 | pub(crate) params: Arc<GenericParams>, |
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index fd9abb55b..a88a978d7 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -8,7 +8,7 @@ use std::cell::RefCell; | |||
8 | 8 | ||
9 | use hir::{ | 9 | use hir::{ |
10 | diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}, | 10 | diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}, |
11 | Semantics, | 11 | HasSource, HirDisplay, Semantics, VariantDef, |
12 | }; | 12 | }; |
13 | use itertools::Itertools; | 13 | use itertools::Itertools; |
14 | use ra_db::SourceDatabase; | 14 | use ra_db::SourceDatabase; |
@@ -16,7 +16,7 @@ use ra_ide_db::RootDatabase; | |||
16 | use ra_prof::profile; | 16 | use ra_prof::profile; |
17 | use ra_syntax::{ | 17 | use ra_syntax::{ |
18 | algo, | 18 | algo, |
19 | ast::{self, make, AstNode}, | 19 | ast::{self, edit::IndentLevel, make, AstNode}, |
20 | SyntaxNode, TextRange, T, | 20 | SyntaxNode, TextRange, T, |
21 | }; | 21 | }; |
22 | use ra_text_edit::{TextEdit, TextEditBuilder}; | 22 | use ra_text_edit::{TextEdit, TextEditBuilder}; |
@@ -119,7 +119,16 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
119 | severity: Severity::Error, | 119 | severity: Severity::Error, |
120 | fix: Some(fix), | 120 | fix: Some(fix), |
121 | }) | 121 | }) |
122 | }) | ||
123 | .on::<hir::diagnostics::NoSuchField, _>(|d| { | ||
124 | res.borrow_mut().push(Diagnostic { | ||
125 | range: sema.diagnostics_range(d).range, | ||
126 | message: d.message(), | ||
127 | severity: Severity::Error, | ||
128 | fix: missing_struct_field_fix(&sema, file_id, d), | ||
129 | }) | ||
122 | }); | 130 | }); |
131 | |||
123 | if let Some(m) = sema.to_module_def(file_id) { | 132 | if let Some(m) = sema.to_module_def(file_id) { |
124 | m.diagnostics(db, &mut sink); | 133 | m.diagnostics(db, &mut sink); |
125 | }; | 134 | }; |
@@ -127,6 +136,68 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
127 | res.into_inner() | 136 | res.into_inner() |
128 | } | 137 | } |
129 | 138 | ||
139 | fn missing_struct_field_fix( | ||
140 | sema: &Semantics<RootDatabase>, | ||
141 | file_id: FileId, | ||
142 | d: &hir::diagnostics::NoSuchField, | ||
143 | ) -> Option<Fix> { | ||
144 | let record_expr = sema.ast(d); | ||
145 | |||
146 | let record_lit = ast::RecordLit::cast(record_expr.syntax().parent()?.parent()?)?; | ||
147 | let def_id = sema.resolve_variant(record_lit)?; | ||
148 | let module; | ||
149 | let record_fields = match VariantDef::from(def_id) { | ||
150 | VariantDef::Struct(s) => { | ||
151 | module = s.module(sema.db); | ||
152 | let source = s.source(sema.db); | ||
153 | let fields = source.value.field_def_list()?; | ||
154 | record_field_def_list(fields)? | ||
155 | } | ||
156 | VariantDef::Union(u) => { | ||
157 | module = u.module(sema.db); | ||
158 | let source = u.source(sema.db); | ||
159 | source.value.record_field_def_list()? | ||
160 | } | ||
161 | VariantDef::EnumVariant(e) => { | ||
162 | module = e.module(sema.db); | ||
163 | let source = e.source(sema.db); | ||
164 | let fields = source.value.field_def_list()?; | ||
165 | record_field_def_list(fields)? | ||
166 | } | ||
167 | }; | ||
168 | |||
169 | let new_field_type = sema.type_of_expr(&record_expr.expr()?)?; | ||
170 | let new_field = make::record_field_def( | ||
171 | record_expr.field_name()?, | ||
172 | make::type_ref(&new_field_type.display_source_code(sema.db, module.into()).ok()?), | ||
173 | ); | ||
174 | |||
175 | let last_field = record_fields.fields().last()?; | ||
176 | let last_field_syntax = last_field.syntax(); | ||
177 | let indent = IndentLevel::from_node(last_field_syntax); | ||
178 | |||
179 | let mut new_field = format!("\n{}{}", indent, new_field); | ||
180 | |||
181 | let needs_comma = !last_field_syntax.to_string().ends_with(","); | ||
182 | if needs_comma { | ||
183 | new_field = format!(",{}", new_field); | ||
184 | } | ||
185 | |||
186 | let source_change = SourceFileEdit { | ||
187 | file_id, | ||
188 | edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field), | ||
189 | }; | ||
190 | let fix = Fix::new("Create field", source_change.into()); | ||
191 | return Some(fix); | ||
192 | |||
193 | fn record_field_def_list(field_def_list: ast::FieldDefList) -> Option<ast::RecordFieldDefList> { | ||
194 | match field_def_list { | ||
195 | ast::FieldDefList::RecordFieldDefList(it) => Some(it), | ||
196 | ast::FieldDefList::TupleFieldDefList(_) => None, | ||
197 | } | ||
198 | } | ||
199 | } | ||
200 | |||
130 | fn check_unnecessary_braces_in_use_statement( | 201 | fn check_unnecessary_braces_in_use_statement( |
131 | acc: &mut Vec<Diagnostic>, | 202 | acc: &mut Vec<Diagnostic>, |
132 | file_id: FileId, | 203 | file_id: FileId, |
@@ -791,4 +862,27 @@ fn main() { | |||
791 | check_struct_shorthand_initialization, | 862 | check_struct_shorthand_initialization, |
792 | ); | 863 | ); |
793 | } | 864 | } |
865 | |||
866 | #[test] | ||
867 | fn test_add_field_from_usage() { | ||
868 | check_apply_diagnostic_fix( | ||
869 | r" | ||
870 | fn main() { | ||
871 | Foo { bar: 3, baz: false}; | ||
872 | } | ||
873 | struct Foo { | ||
874 | bar: i32 | ||
875 | } | ||
876 | ", | ||
877 | r" | ||
878 | fn main() { | ||
879 | Foo { bar: 3, baz: false}; | ||
880 | } | ||
881 | struct Foo { | ||
882 | bar: i32, | ||
883 | baz: bool | ||
884 | } | ||
885 | ", | ||
886 | ) | ||
887 | } | ||
794 | } | 888 | } |
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index ad78b7671..d870e4cbc 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -2,7 +2,7 @@ use std::iter::once; | |||
2 | 2 | ||
3 | use hir::{ | 3 | use hir::{ |
4 | Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay, | 4 | Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay, |
5 | ModuleDef, ModuleSource, Semantics, | 5 | Module, ModuleDef, ModuleSource, Semantics, |
6 | }; | 6 | }; |
7 | use itertools::Itertools; | 7 | use itertools::Itertools; |
8 | use ra_db::SourceDatabase; | 8 | use ra_db::SourceDatabase; |
@@ -13,7 +13,9 @@ use ra_ide_db::{ | |||
13 | use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; | 13 | use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; |
14 | 14 | ||
15 | use crate::{ | 15 | use crate::{ |
16 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav}, | 16 | display::{ |
17 | macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav, TryToNav, | ||
18 | }, | ||
17 | runnables::runnable, | 19 | runnables::runnable, |
18 | FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, | 20 | FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, |
19 | }; | 21 | }; |
@@ -24,19 +26,21 @@ pub struct HoverConfig { | |||
24 | pub implementations: bool, | 26 | pub implementations: bool, |
25 | pub run: bool, | 27 | pub run: bool, |
26 | pub debug: bool, | 28 | pub debug: bool, |
29 | pub goto_type_def: bool, | ||
27 | } | 30 | } |
28 | 31 | ||
29 | impl Default for HoverConfig { | 32 | impl Default for HoverConfig { |
30 | fn default() -> Self { | 33 | fn default() -> Self { |
31 | Self { implementations: true, run: true, debug: true } | 34 | Self { implementations: true, run: true, debug: true, goto_type_def: true } |
32 | } | 35 | } |
33 | } | 36 | } |
34 | 37 | ||
35 | impl HoverConfig { | 38 | impl HoverConfig { |
36 | pub const NO_ACTIONS: Self = Self { implementations: false, run: false, debug: false }; | 39 | pub const NO_ACTIONS: Self = |
40 | Self { implementations: false, run: false, debug: false, goto_type_def: false }; | ||
37 | 41 | ||
38 | pub fn any(&self) -> bool { | 42 | pub fn any(&self) -> bool { |
39 | self.implementations || self.runnable() | 43 | self.implementations || self.runnable() || self.goto_type_def |
40 | } | 44 | } |
41 | 45 | ||
42 | pub fn none(&self) -> bool { | 46 | pub fn none(&self) -> bool { |
@@ -52,6 +56,13 @@ impl HoverConfig { | |||
52 | pub enum HoverAction { | 56 | pub enum HoverAction { |
53 | Runnable(Runnable), | 57 | Runnable(Runnable), |
54 | Implementaion(FilePosition), | 58 | Implementaion(FilePosition), |
59 | GoToType(Vec<HoverGotoTypeData>), | ||
60 | } | ||
61 | |||
62 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
63 | pub struct HoverGotoTypeData { | ||
64 | pub mod_path: String, | ||
65 | pub nav: NavigationTarget, | ||
55 | } | 66 | } |
56 | 67 | ||
57 | /// Contains the results when hovering over an item | 68 | /// Contains the results when hovering over an item |
@@ -138,6 +149,10 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
138 | res.push_action(action); | 149 | res.push_action(action); |
139 | } | 150 | } |
140 | 151 | ||
152 | if let Some(action) = goto_type_action(db, name_kind) { | ||
153 | res.push_action(action); | ||
154 | } | ||
155 | |||
141 | return Some(RangeInfo::new(range, res)); | 156 | return Some(RangeInfo::new(range, res)); |
142 | } | 157 | } |
143 | } | 158 | } |
@@ -218,6 +233,44 @@ fn runnable_action( | |||
218 | } | 233 | } |
219 | } | 234 | } |
220 | 235 | ||
236 | fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { | ||
237 | match def { | ||
238 | Definition::Local(it) => { | ||
239 | let mut targets: Vec<ModuleDef> = Vec::new(); | ||
240 | let mut push_new_def = |item: ModuleDef| { | ||
241 | if !targets.contains(&item) { | ||
242 | targets.push(item); | ||
243 | } | ||
244 | }; | ||
245 | |||
246 | it.ty(db).walk(db, |t| { | ||
247 | if let Some(adt) = t.as_adt() { | ||
248 | push_new_def(adt.into()); | ||
249 | } else if let Some(trait_) = t.as_dyn_trait() { | ||
250 | push_new_def(trait_.into()); | ||
251 | } else if let Some(traits) = t.as_impl_traits(db) { | ||
252 | traits.into_iter().for_each(|it| push_new_def(it.into())); | ||
253 | } else if let Some(trait_) = t.as_associated_type_parent_trait(db) { | ||
254 | push_new_def(trait_.into()); | ||
255 | } | ||
256 | }); | ||
257 | |||
258 | let targets = targets | ||
259 | .into_iter() | ||
260 | .filter_map(|it| { | ||
261 | Some(HoverGotoTypeData { | ||
262 | mod_path: mod_path(db, &it)?, | ||
263 | nav: it.try_to_nav(db)?, | ||
264 | }) | ||
265 | }) | ||
266 | .collect(); | ||
267 | |||
268 | Some(HoverAction::GoToType(targets)) | ||
269 | } | ||
270 | _ => None, | ||
271 | } | ||
272 | } | ||
273 | |||
221 | fn hover_text( | 274 | fn hover_text( |
222 | docs: Option<String>, | 275 | docs: Option<String>, |
223 | desc: Option<String>, | 276 | desc: Option<String>, |
@@ -248,25 +301,31 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> | |||
248 | .map(|name| name.to_string()) | 301 | .map(|name| name.to_string()) |
249 | } | 302 | } |
250 | 303 | ||
251 | fn determine_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { | 304 | fn determine_mod_path(db: &RootDatabase, module: Module, name: Option<String>) -> String { |
252 | let mod_path = def.module(db).map(|module| { | 305 | once(db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string)) |
253 | once(db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string)) | 306 | .chain( |
254 | .chain( | 307 | module |
255 | module | 308 | .path_to_root(db) |
256 | .path_to_root(db) | 309 | .into_iter() |
257 | .into_iter() | 310 | .rev() |
258 | .rev() | 311 | .map(|it| it.name(db).map(|name| name.to_string())), |
259 | .map(|it| it.name(db).map(|name| name.to_string())), | 312 | ) |
260 | ) | 313 | .chain(once(name)) |
261 | .chain(once(definition_owner_name(db, def))) | 314 | .flatten() |
262 | .flatten() | 315 | .join("::") |
263 | .join("::") | 316 | } |
264 | }); | 317 | |
265 | mod_path | 318 | // returns None only for ModuleDef::BuiltinType |
319 | fn mod_path(db: &RootDatabase, item: &ModuleDef) -> Option<String> { | ||
320 | Some(determine_mod_path(db, item.module(db)?, item.name(db).map(|name| name.to_string()))) | ||
321 | } | ||
322 | |||
323 | fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { | ||
324 | def.module(db).map(|module| determine_mod_path(db, module, definition_owner_name(db, def))) | ||
266 | } | 325 | } |
267 | 326 | ||
268 | fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> { | 327 | fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> { |
269 | let mod_path = determine_mod_path(db, &def); | 328 | let mod_path = definition_mod_path(db, &def); |
270 | return match def { | 329 | return match def { |
271 | Definition::Macro(it) => { | 330 | Definition::Macro(it) => { |
272 | let src = it.source(db); | 331 | let src = it.source(db); |
@@ -1310,4 +1369,1045 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1310 | ] | 1369 | ] |
1311 | "###); | 1370 | "###); |
1312 | } | 1371 | } |
1372 | |||
1373 | #[test] | ||
1374 | fn test_hover_struct_has_goto_type_action() { | ||
1375 | let (_, actions) = check_hover_result( | ||
1376 | " | ||
1377 | //- /main.rs | ||
1378 | struct S{ f1: u32 } | ||
1379 | |||
1380 | fn main() { | ||
1381 | let s<|>t = S{ f1:0 }; | ||
1382 | } | ||
1383 | ", | ||
1384 | &["S"], | ||
1385 | ); | ||
1386 | assert_debug_snapshot!(actions, | ||
1387 | @r###" | ||
1388 | [ | ||
1389 | GoToType( | ||
1390 | [ | ||
1391 | HoverGotoTypeData { | ||
1392 | mod_path: "S", | ||
1393 | nav: NavigationTarget { | ||
1394 | file_id: FileId( | ||
1395 | 1, | ||
1396 | ), | ||
1397 | full_range: 0..19, | ||
1398 | name: "S", | ||
1399 | kind: STRUCT_DEF, | ||
1400 | focus_range: Some( | ||
1401 | 7..8, | ||
1402 | ), | ||
1403 | container_name: None, | ||
1404 | description: Some( | ||
1405 | "struct S", | ||
1406 | ), | ||
1407 | docs: None, | ||
1408 | }, | ||
1409 | }, | ||
1410 | ], | ||
1411 | ), | ||
1412 | ] | ||
1413 | "###); | ||
1414 | } | ||
1415 | |||
1416 | #[test] | ||
1417 | fn test_hover_generic_struct_has_goto_type_actions() { | ||
1418 | let (_, actions) = check_hover_result( | ||
1419 | " | ||
1420 | //- /main.rs | ||
1421 | struct Arg(u32); | ||
1422 | struct S<T>{ f1: T } | ||
1423 | |||
1424 | fn main() { | ||
1425 | let s<|>t = S{ f1:Arg(0) }; | ||
1426 | } | ||
1427 | ", | ||
1428 | &["S<Arg>"], | ||
1429 | ); | ||
1430 | assert_debug_snapshot!(actions, | ||
1431 | @r###" | ||
1432 | [ | ||
1433 | GoToType( | ||
1434 | [ | ||
1435 | HoverGotoTypeData { | ||
1436 | mod_path: "S", | ||
1437 | nav: NavigationTarget { | ||
1438 | file_id: FileId( | ||
1439 | 1, | ||
1440 | ), | ||
1441 | full_range: 17..37, | ||
1442 | name: "S", | ||
1443 | kind: STRUCT_DEF, | ||
1444 | focus_range: Some( | ||
1445 | 24..25, | ||
1446 | ), | ||
1447 | container_name: None, | ||
1448 | description: Some( | ||
1449 | "struct S", | ||
1450 | ), | ||
1451 | docs: None, | ||
1452 | }, | ||
1453 | }, | ||
1454 | HoverGotoTypeData { | ||
1455 | mod_path: "Arg", | ||
1456 | nav: NavigationTarget { | ||
1457 | file_id: FileId( | ||
1458 | 1, | ||
1459 | ), | ||
1460 | full_range: 0..16, | ||
1461 | name: "Arg", | ||
1462 | kind: STRUCT_DEF, | ||
1463 | focus_range: Some( | ||
1464 | 7..10, | ||
1465 | ), | ||
1466 | container_name: None, | ||
1467 | description: Some( | ||
1468 | "struct Arg", | ||
1469 | ), | ||
1470 | docs: None, | ||
1471 | }, | ||
1472 | }, | ||
1473 | ], | ||
1474 | ), | ||
1475 | ] | ||
1476 | "###); | ||
1477 | } | ||
1478 | |||
1479 | #[test] | ||
1480 | fn test_hover_generic_struct_has_flattened_goto_type_actions() { | ||
1481 | let (_, actions) = check_hover_result( | ||
1482 | " | ||
1483 | //- /main.rs | ||
1484 | struct Arg(u32); | ||
1485 | struct S<T>{ f1: T } | ||
1486 | |||
1487 | fn main() { | ||
1488 | let s<|>t = S{ f1: S{ f1: Arg(0) } }; | ||
1489 | } | ||
1490 | ", | ||
1491 | &["S<S<Arg>>"], | ||
1492 | ); | ||
1493 | assert_debug_snapshot!(actions, | ||
1494 | @r###" | ||
1495 | [ | ||
1496 | GoToType( | ||
1497 | [ | ||
1498 | HoverGotoTypeData { | ||
1499 | mod_path: "S", | ||
1500 | nav: NavigationTarget { | ||
1501 | file_id: FileId( | ||
1502 | 1, | ||
1503 | ), | ||
1504 | full_range: 17..37, | ||
1505 | name: "S", | ||
1506 | kind: STRUCT_DEF, | ||
1507 | focus_range: Some( | ||
1508 | 24..25, | ||
1509 | ), | ||
1510 | container_name: None, | ||
1511 | description: Some( | ||
1512 | "struct S", | ||
1513 | ), | ||
1514 | docs: None, | ||
1515 | }, | ||
1516 | }, | ||
1517 | HoverGotoTypeData { | ||
1518 | mod_path: "Arg", | ||
1519 | nav: NavigationTarget { | ||
1520 | file_id: FileId( | ||
1521 | 1, | ||
1522 | ), | ||
1523 | full_range: 0..16, | ||
1524 | name: "Arg", | ||
1525 | kind: STRUCT_DEF, | ||
1526 | focus_range: Some( | ||
1527 | 7..10, | ||
1528 | ), | ||
1529 | container_name: None, | ||
1530 | description: Some( | ||
1531 | "struct Arg", | ||
1532 | ), | ||
1533 | docs: None, | ||
1534 | }, | ||
1535 | }, | ||
1536 | ], | ||
1537 | ), | ||
1538 | ] | ||
1539 | "###); | ||
1540 | } | ||
1541 | |||
1542 | #[test] | ||
1543 | fn test_hover_tuple_has_goto_type_actions() { | ||
1544 | let (_, actions) = check_hover_result( | ||
1545 | " | ||
1546 | //- /main.rs | ||
1547 | struct A(u32); | ||
1548 | struct B(u32); | ||
1549 | mod M { | ||
1550 | pub struct C(u32); | ||
1551 | } | ||
1552 | |||
1553 | fn main() { | ||
1554 | let s<|>t = (A(1), B(2), M::C(3) ); | ||
1555 | } | ||
1556 | ", | ||
1557 | &["(A, B, C)"], | ||
1558 | ); | ||
1559 | assert_debug_snapshot!(actions, | ||
1560 | @r###" | ||
1561 | [ | ||
1562 | GoToType( | ||
1563 | [ | ||
1564 | HoverGotoTypeData { | ||
1565 | mod_path: "A", | ||
1566 | nav: NavigationTarget { | ||
1567 | file_id: FileId( | ||
1568 | 1, | ||
1569 | ), | ||
1570 | full_range: 0..14, | ||
1571 | name: "A", | ||
1572 | kind: STRUCT_DEF, | ||
1573 | focus_range: Some( | ||
1574 | 7..8, | ||
1575 | ), | ||
1576 | container_name: None, | ||
1577 | description: Some( | ||
1578 | "struct A", | ||
1579 | ), | ||
1580 | docs: None, | ||
1581 | }, | ||
1582 | }, | ||
1583 | HoverGotoTypeData { | ||
1584 | mod_path: "B", | ||
1585 | nav: NavigationTarget { | ||
1586 | file_id: FileId( | ||
1587 | 1, | ||
1588 | ), | ||
1589 | full_range: 15..29, | ||
1590 | name: "B", | ||
1591 | kind: STRUCT_DEF, | ||
1592 | focus_range: Some( | ||
1593 | 22..23, | ||
1594 | ), | ||
1595 | container_name: None, | ||
1596 | description: Some( | ||
1597 | "struct B", | ||
1598 | ), | ||
1599 | docs: None, | ||
1600 | }, | ||
1601 | }, | ||
1602 | HoverGotoTypeData { | ||
1603 | mod_path: "M::C", | ||
1604 | nav: NavigationTarget { | ||
1605 | file_id: FileId( | ||
1606 | 1, | ||
1607 | ), | ||
1608 | full_range: 42..60, | ||
1609 | name: "C", | ||
1610 | kind: STRUCT_DEF, | ||
1611 | focus_range: Some( | ||
1612 | 53..54, | ||
1613 | ), | ||
1614 | container_name: None, | ||
1615 | description: Some( | ||
1616 | "pub struct C", | ||
1617 | ), | ||
1618 | docs: None, | ||
1619 | }, | ||
1620 | }, | ||
1621 | ], | ||
1622 | ), | ||
1623 | ] | ||
1624 | "###); | ||
1625 | } | ||
1626 | |||
1627 | #[test] | ||
1628 | fn test_hover_return_impl_trait_has_goto_type_action() { | ||
1629 | let (_, actions) = check_hover_result( | ||
1630 | " | ||
1631 | //- /main.rs | ||
1632 | trait Foo {} | ||
1633 | |||
1634 | fn foo() -> impl Foo {} | ||
1635 | |||
1636 | fn main() { | ||
1637 | let s<|>t = foo(); | ||
1638 | } | ||
1639 | ", | ||
1640 | &["impl Foo"], | ||
1641 | ); | ||
1642 | assert_debug_snapshot!(actions, | ||
1643 | @r###" | ||
1644 | [ | ||
1645 | GoToType( | ||
1646 | [ | ||
1647 | HoverGotoTypeData { | ||
1648 | mod_path: "Foo", | ||
1649 | nav: NavigationTarget { | ||
1650 | file_id: FileId( | ||
1651 | 1, | ||
1652 | ), | ||
1653 | full_range: 0..12, | ||
1654 | name: "Foo", | ||
1655 | kind: TRAIT_DEF, | ||
1656 | focus_range: Some( | ||
1657 | 6..9, | ||
1658 | ), | ||
1659 | container_name: None, | ||
1660 | description: Some( | ||
1661 | "trait Foo", | ||
1662 | ), | ||
1663 | docs: None, | ||
1664 | }, | ||
1665 | }, | ||
1666 | ], | ||
1667 | ), | ||
1668 | ] | ||
1669 | "###); | ||
1670 | } | ||
1671 | |||
1672 | #[test] | ||
1673 | fn test_hover_generic_return_impl_trait_has_goto_type_action() { | ||
1674 | let (_, actions) = check_hover_result( | ||
1675 | " | ||
1676 | //- /main.rs | ||
1677 | trait Foo<T> {} | ||
1678 | struct S; | ||
1679 | |||
1680 | fn foo() -> impl Foo<S> {} | ||
1681 | |||
1682 | fn main() { | ||
1683 | let s<|>t = foo(); | ||
1684 | } | ||
1685 | ", | ||
1686 | &["impl Foo<S>"], | ||
1687 | ); | ||
1688 | assert_debug_snapshot!(actions, | ||
1689 | @r###" | ||
1690 | [ | ||
1691 | GoToType( | ||
1692 | [ | ||
1693 | HoverGotoTypeData { | ||
1694 | mod_path: "Foo", | ||
1695 | nav: NavigationTarget { | ||
1696 | file_id: FileId( | ||
1697 | 1, | ||
1698 | ), | ||
1699 | full_range: 0..15, | ||
1700 | name: "Foo", | ||
1701 | kind: TRAIT_DEF, | ||
1702 | focus_range: Some( | ||
1703 | 6..9, | ||
1704 | ), | ||
1705 | container_name: None, | ||
1706 | description: Some( | ||
1707 | "trait Foo", | ||
1708 | ), | ||
1709 | docs: None, | ||
1710 | }, | ||
1711 | }, | ||
1712 | HoverGotoTypeData { | ||
1713 | mod_path: "S", | ||
1714 | nav: NavigationTarget { | ||
1715 | file_id: FileId( | ||
1716 | 1, | ||
1717 | ), | ||
1718 | full_range: 16..25, | ||
1719 | name: "S", | ||
1720 | kind: STRUCT_DEF, | ||
1721 | focus_range: Some( | ||
1722 | 23..24, | ||
1723 | ), | ||
1724 | container_name: None, | ||
1725 | description: Some( | ||
1726 | "struct S", | ||
1727 | ), | ||
1728 | docs: None, | ||
1729 | }, | ||
1730 | }, | ||
1731 | ], | ||
1732 | ), | ||
1733 | ] | ||
1734 | "###); | ||
1735 | } | ||
1736 | |||
1737 | #[test] | ||
1738 | fn test_hover_return_impl_traits_has_goto_type_action() { | ||
1739 | let (_, actions) = check_hover_result( | ||
1740 | " | ||
1741 | //- /main.rs | ||
1742 | trait Foo {} | ||
1743 | trait Bar {} | ||
1744 | |||
1745 | fn foo() -> impl Foo + Bar {} | ||
1746 | |||
1747 | fn main() { | ||
1748 | let s<|>t = foo(); | ||
1749 | } | ||
1750 | ", | ||
1751 | &["impl Foo + Bar"], | ||
1752 | ); | ||
1753 | assert_debug_snapshot!(actions, | ||
1754 | @r###" | ||
1755 | [ | ||
1756 | GoToType( | ||
1757 | [ | ||
1758 | HoverGotoTypeData { | ||
1759 | mod_path: "Foo", | ||
1760 | nav: NavigationTarget { | ||
1761 | file_id: FileId( | ||
1762 | 1, | ||
1763 | ), | ||
1764 | full_range: 0..12, | ||
1765 | name: "Foo", | ||
1766 | kind: TRAIT_DEF, | ||
1767 | focus_range: Some( | ||
1768 | 6..9, | ||
1769 | ), | ||
1770 | container_name: None, | ||
1771 | description: Some( | ||
1772 | "trait Foo", | ||
1773 | ), | ||
1774 | docs: None, | ||
1775 | }, | ||
1776 | }, | ||
1777 | HoverGotoTypeData { | ||
1778 | mod_path: "Bar", | ||
1779 | nav: NavigationTarget { | ||
1780 | file_id: FileId( | ||
1781 | 1, | ||
1782 | ), | ||
1783 | full_range: 13..25, | ||
1784 | name: "Bar", | ||
1785 | kind: TRAIT_DEF, | ||
1786 | focus_range: Some( | ||
1787 | 19..22, | ||
1788 | ), | ||
1789 | container_name: None, | ||
1790 | description: Some( | ||
1791 | "trait Bar", | ||
1792 | ), | ||
1793 | docs: None, | ||
1794 | }, | ||
1795 | }, | ||
1796 | ], | ||
1797 | ), | ||
1798 | ] | ||
1799 | "###); | ||
1800 | } | ||
1801 | |||
1802 | #[test] | ||
1803 | fn test_hover_generic_return_impl_traits_has_goto_type_action() { | ||
1804 | let (_, actions) = check_hover_result( | ||
1805 | " | ||
1806 | //- /main.rs | ||
1807 | trait Foo<T> {} | ||
1808 | trait Bar<T> {} | ||
1809 | struct S1 {} | ||
1810 | struct S2 {} | ||
1811 | |||
1812 | fn foo() -> impl Foo<S1> + Bar<S2> {} | ||
1813 | |||
1814 | fn main() { | ||
1815 | let s<|>t = foo(); | ||
1816 | } | ||
1817 | ", | ||
1818 | &["impl Foo<S1> + Bar<S2>"], | ||
1819 | ); | ||
1820 | assert_debug_snapshot!(actions, | ||
1821 | @r###" | ||
1822 | [ | ||
1823 | GoToType( | ||
1824 | [ | ||
1825 | HoverGotoTypeData { | ||
1826 | mod_path: "Foo", | ||
1827 | nav: NavigationTarget { | ||
1828 | file_id: FileId( | ||
1829 | 1, | ||
1830 | ), | ||
1831 | full_range: 0..15, | ||
1832 | name: "Foo", | ||
1833 | kind: TRAIT_DEF, | ||
1834 | focus_range: Some( | ||
1835 | 6..9, | ||
1836 | ), | ||
1837 | container_name: None, | ||
1838 | description: Some( | ||
1839 | "trait Foo", | ||
1840 | ), | ||
1841 | docs: None, | ||
1842 | }, | ||
1843 | }, | ||
1844 | HoverGotoTypeData { | ||
1845 | mod_path: "Bar", | ||
1846 | nav: NavigationTarget { | ||
1847 | file_id: FileId( | ||
1848 | 1, | ||
1849 | ), | ||
1850 | full_range: 16..31, | ||
1851 | name: "Bar", | ||
1852 | kind: TRAIT_DEF, | ||
1853 | focus_range: Some( | ||
1854 | 22..25, | ||
1855 | ), | ||
1856 | container_name: None, | ||
1857 | description: Some( | ||
1858 | "trait Bar", | ||
1859 | ), | ||
1860 | docs: None, | ||
1861 | }, | ||
1862 | }, | ||
1863 | HoverGotoTypeData { | ||
1864 | mod_path: "S1", | ||
1865 | nav: NavigationTarget { | ||
1866 | file_id: FileId( | ||
1867 | 1, | ||
1868 | ), | ||
1869 | full_range: 32..44, | ||
1870 | name: "S1", | ||
1871 | kind: STRUCT_DEF, | ||
1872 | focus_range: Some( | ||
1873 | 39..41, | ||
1874 | ), | ||
1875 | container_name: None, | ||
1876 | description: Some( | ||
1877 | "struct S1", | ||
1878 | ), | ||
1879 | docs: None, | ||
1880 | }, | ||
1881 | }, | ||
1882 | HoverGotoTypeData { | ||
1883 | mod_path: "S2", | ||
1884 | nav: NavigationTarget { | ||
1885 | file_id: FileId( | ||
1886 | 1, | ||
1887 | ), | ||
1888 | full_range: 45..57, | ||
1889 | name: "S2", | ||
1890 | kind: STRUCT_DEF, | ||
1891 | focus_range: Some( | ||
1892 | 52..54, | ||
1893 | ), | ||
1894 | container_name: None, | ||
1895 | description: Some( | ||
1896 | "struct S2", | ||
1897 | ), | ||
1898 | docs: None, | ||
1899 | }, | ||
1900 | }, | ||
1901 | ], | ||
1902 | ), | ||
1903 | ] | ||
1904 | "###); | ||
1905 | } | ||
1906 | |||
1907 | #[test] | ||
1908 | fn test_hover_arg_impl_trait_has_goto_type_action() { | ||
1909 | let (_, actions) = check_hover_result( | ||
1910 | " | ||
1911 | //- /lib.rs | ||
1912 | trait Foo {} | ||
1913 | fn foo(ar<|>g: &impl Foo) {} | ||
1914 | ", | ||
1915 | &["&impl Foo"], | ||
1916 | ); | ||
1917 | assert_debug_snapshot!(actions, | ||
1918 | @r###" | ||
1919 | [ | ||
1920 | GoToType( | ||
1921 | [ | ||
1922 | HoverGotoTypeData { | ||
1923 | mod_path: "Foo", | ||
1924 | nav: NavigationTarget { | ||
1925 | file_id: FileId( | ||
1926 | 1, | ||
1927 | ), | ||
1928 | full_range: 0..12, | ||
1929 | name: "Foo", | ||
1930 | kind: TRAIT_DEF, | ||
1931 | focus_range: Some( | ||
1932 | 6..9, | ||
1933 | ), | ||
1934 | container_name: None, | ||
1935 | description: Some( | ||
1936 | "trait Foo", | ||
1937 | ), | ||
1938 | docs: None, | ||
1939 | }, | ||
1940 | }, | ||
1941 | ], | ||
1942 | ), | ||
1943 | ] | ||
1944 | "###); | ||
1945 | } | ||
1946 | |||
1947 | #[test] | ||
1948 | fn test_hover_arg_impl_traits_has_goto_type_action() { | ||
1949 | let (_, actions) = check_hover_result( | ||
1950 | " | ||
1951 | //- /lib.rs | ||
1952 | trait Foo {} | ||
1953 | trait Bar<T> {} | ||
1954 | struct S{} | ||
1955 | |||
1956 | fn foo(ar<|>g: &impl Foo + Bar<S>) {} | ||
1957 | ", | ||
1958 | &["&impl Foo + Bar<S>"], | ||
1959 | ); | ||
1960 | assert_debug_snapshot!(actions, | ||
1961 | @r###" | ||
1962 | [ | ||
1963 | GoToType( | ||
1964 | [ | ||
1965 | HoverGotoTypeData { | ||
1966 | mod_path: "Foo", | ||
1967 | nav: NavigationTarget { | ||
1968 | file_id: FileId( | ||
1969 | 1, | ||
1970 | ), | ||
1971 | full_range: 0..12, | ||
1972 | name: "Foo", | ||
1973 | kind: TRAIT_DEF, | ||
1974 | focus_range: Some( | ||
1975 | 6..9, | ||
1976 | ), | ||
1977 | container_name: None, | ||
1978 | description: Some( | ||
1979 | "trait Foo", | ||
1980 | ), | ||
1981 | docs: None, | ||
1982 | }, | ||
1983 | }, | ||
1984 | HoverGotoTypeData { | ||
1985 | mod_path: "Bar", | ||
1986 | nav: NavigationTarget { | ||
1987 | file_id: FileId( | ||
1988 | 1, | ||
1989 | ), | ||
1990 | full_range: 13..28, | ||
1991 | name: "Bar", | ||
1992 | kind: TRAIT_DEF, | ||
1993 | focus_range: Some( | ||
1994 | 19..22, | ||
1995 | ), | ||
1996 | container_name: None, | ||
1997 | description: Some( | ||
1998 | "trait Bar", | ||
1999 | ), | ||
2000 | docs: None, | ||
2001 | }, | ||
2002 | }, | ||
2003 | HoverGotoTypeData { | ||
2004 | mod_path: "S", | ||
2005 | nav: NavigationTarget { | ||
2006 | file_id: FileId( | ||
2007 | 1, | ||
2008 | ), | ||
2009 | full_range: 29..39, | ||
2010 | name: "S", | ||
2011 | kind: STRUCT_DEF, | ||
2012 | focus_range: Some( | ||
2013 | 36..37, | ||
2014 | ), | ||
2015 | container_name: None, | ||
2016 | description: Some( | ||
2017 | "struct S", | ||
2018 | ), | ||
2019 | docs: None, | ||
2020 | }, | ||
2021 | }, | ||
2022 | ], | ||
2023 | ), | ||
2024 | ] | ||
2025 | "###); | ||
2026 | } | ||
2027 | |||
2028 | #[test] | ||
2029 | fn test_hover_arg_generic_impl_trait_has_goto_type_action() { | ||
2030 | let (_, actions) = check_hover_result( | ||
2031 | " | ||
2032 | //- /lib.rs | ||
2033 | trait Foo<T> {} | ||
2034 | struct S {} | ||
2035 | fn foo(ar<|>g: &impl Foo<S>) {} | ||
2036 | ", | ||
2037 | &["&impl Foo<S>"], | ||
2038 | ); | ||
2039 | assert_debug_snapshot!(actions, | ||
2040 | @r###" | ||
2041 | [ | ||
2042 | GoToType( | ||
2043 | [ | ||
2044 | HoverGotoTypeData { | ||
2045 | mod_path: "Foo", | ||
2046 | nav: NavigationTarget { | ||
2047 | file_id: FileId( | ||
2048 | 1, | ||
2049 | ), | ||
2050 | full_range: 0..15, | ||
2051 | name: "Foo", | ||
2052 | kind: TRAIT_DEF, | ||
2053 | focus_range: Some( | ||
2054 | 6..9, | ||
2055 | ), | ||
2056 | container_name: None, | ||
2057 | description: Some( | ||
2058 | "trait Foo", | ||
2059 | ), | ||
2060 | docs: None, | ||
2061 | }, | ||
2062 | }, | ||
2063 | HoverGotoTypeData { | ||
2064 | mod_path: "S", | ||
2065 | nav: NavigationTarget { | ||
2066 | file_id: FileId( | ||
2067 | 1, | ||
2068 | ), | ||
2069 | full_range: 16..27, | ||
2070 | name: "S", | ||
2071 | kind: STRUCT_DEF, | ||
2072 | focus_range: Some( | ||
2073 | 23..24, | ||
2074 | ), | ||
2075 | container_name: None, | ||
2076 | description: Some( | ||
2077 | "struct S", | ||
2078 | ), | ||
2079 | docs: None, | ||
2080 | }, | ||
2081 | }, | ||
2082 | ], | ||
2083 | ), | ||
2084 | ] | ||
2085 | "###); | ||
2086 | } | ||
2087 | |||
2088 | #[test] | ||
2089 | fn test_hover_dyn_return_has_goto_type_action() { | ||
2090 | let (_, actions) = check_hover_result( | ||
2091 | " | ||
2092 | //- /main.rs | ||
2093 | trait Foo {} | ||
2094 | struct S; | ||
2095 | impl Foo for S {} | ||
2096 | |||
2097 | struct B<T>{} | ||
2098 | |||
2099 | fn foo() -> B<dyn Foo> {} | ||
2100 | |||
2101 | fn main() { | ||
2102 | let s<|>t = foo(); | ||
2103 | } | ||
2104 | ", | ||
2105 | &["B<dyn Foo>"], | ||
2106 | ); | ||
2107 | assert_debug_snapshot!(actions, | ||
2108 | @r###" | ||
2109 | [ | ||
2110 | GoToType( | ||
2111 | [ | ||
2112 | HoverGotoTypeData { | ||
2113 | mod_path: "B", | ||
2114 | nav: NavigationTarget { | ||
2115 | file_id: FileId( | ||
2116 | 1, | ||
2117 | ), | ||
2118 | full_range: 41..54, | ||
2119 | name: "B", | ||
2120 | kind: STRUCT_DEF, | ||
2121 | focus_range: Some( | ||
2122 | 48..49, | ||
2123 | ), | ||
2124 | container_name: None, | ||
2125 | description: Some( | ||
2126 | "struct B", | ||
2127 | ), | ||
2128 | docs: None, | ||
2129 | }, | ||
2130 | }, | ||
2131 | HoverGotoTypeData { | ||
2132 | mod_path: "Foo", | ||
2133 | nav: NavigationTarget { | ||
2134 | file_id: FileId( | ||
2135 | 1, | ||
2136 | ), | ||
2137 | full_range: 0..12, | ||
2138 | name: "Foo", | ||
2139 | kind: TRAIT_DEF, | ||
2140 | focus_range: Some( | ||
2141 | 6..9, | ||
2142 | ), | ||
2143 | container_name: None, | ||
2144 | description: Some( | ||
2145 | "trait Foo", | ||
2146 | ), | ||
2147 | docs: None, | ||
2148 | }, | ||
2149 | }, | ||
2150 | ], | ||
2151 | ), | ||
2152 | ] | ||
2153 | "###); | ||
2154 | } | ||
2155 | |||
2156 | #[test] | ||
2157 | fn test_hover_dyn_arg_has_goto_type_action() { | ||
2158 | let (_, actions) = check_hover_result( | ||
2159 | " | ||
2160 | //- /lib.rs | ||
2161 | trait Foo {} | ||
2162 | fn foo(ar<|>g: &dyn Foo) {} | ||
2163 | ", | ||
2164 | &["&dyn Foo"], | ||
2165 | ); | ||
2166 | assert_debug_snapshot!(actions, | ||
2167 | @r###" | ||
2168 | [ | ||
2169 | GoToType( | ||
2170 | [ | ||
2171 | HoverGotoTypeData { | ||
2172 | mod_path: "Foo", | ||
2173 | nav: NavigationTarget { | ||
2174 | file_id: FileId( | ||
2175 | 1, | ||
2176 | ), | ||
2177 | full_range: 0..12, | ||
2178 | name: "Foo", | ||
2179 | kind: TRAIT_DEF, | ||
2180 | focus_range: Some( | ||
2181 | 6..9, | ||
2182 | ), | ||
2183 | container_name: None, | ||
2184 | description: Some( | ||
2185 | "trait Foo", | ||
2186 | ), | ||
2187 | docs: None, | ||
2188 | }, | ||
2189 | }, | ||
2190 | ], | ||
2191 | ), | ||
2192 | ] | ||
2193 | "###); | ||
2194 | } | ||
2195 | |||
2196 | #[test] | ||
2197 | fn test_hover_generic_dyn_arg_has_goto_type_action() { | ||
2198 | let (_, actions) = check_hover_result( | ||
2199 | " | ||
2200 | //- /lib.rs | ||
2201 | trait Foo<T> {} | ||
2202 | struct S {} | ||
2203 | fn foo(ar<|>g: &dyn Foo<S>) {} | ||
2204 | ", | ||
2205 | &["&dyn Foo<S>"], | ||
2206 | ); | ||
2207 | assert_debug_snapshot!(actions, | ||
2208 | @r###" | ||
2209 | [ | ||
2210 | GoToType( | ||
2211 | [ | ||
2212 | HoverGotoTypeData { | ||
2213 | mod_path: "Foo", | ||
2214 | nav: NavigationTarget { | ||
2215 | file_id: FileId( | ||
2216 | 1, | ||
2217 | ), | ||
2218 | full_range: 0..15, | ||
2219 | name: "Foo", | ||
2220 | kind: TRAIT_DEF, | ||
2221 | focus_range: Some( | ||
2222 | 6..9, | ||
2223 | ), | ||
2224 | container_name: None, | ||
2225 | description: Some( | ||
2226 | "trait Foo", | ||
2227 | ), | ||
2228 | docs: None, | ||
2229 | }, | ||
2230 | }, | ||
2231 | HoverGotoTypeData { | ||
2232 | mod_path: "S", | ||
2233 | nav: NavigationTarget { | ||
2234 | file_id: FileId( | ||
2235 | 1, | ||
2236 | ), | ||
2237 | full_range: 16..27, | ||
2238 | name: "S", | ||
2239 | kind: STRUCT_DEF, | ||
2240 | focus_range: Some( | ||
2241 | 23..24, | ||
2242 | ), | ||
2243 | container_name: None, | ||
2244 | description: Some( | ||
2245 | "struct S", | ||
2246 | ), | ||
2247 | docs: None, | ||
2248 | }, | ||
2249 | }, | ||
2250 | ], | ||
2251 | ), | ||
2252 | ] | ||
2253 | "###); | ||
2254 | } | ||
2255 | |||
2256 | #[test] | ||
2257 | fn test_hover_goto_type_action_links_order() { | ||
2258 | let (_, actions) = check_hover_result( | ||
2259 | " | ||
2260 | //- /lib.rs | ||
2261 | trait ImplTrait<T> {} | ||
2262 | trait DynTrait<T> {} | ||
2263 | struct B<T> {} | ||
2264 | struct S {} | ||
2265 | |||
2266 | fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} | ||
2267 | ", | ||
2268 | &["&impl ImplTrait<B<dyn DynTrait<B<S>>>>"], | ||
2269 | ); | ||
2270 | assert_debug_snapshot!(actions, | ||
2271 | @r###" | ||
2272 | [ | ||
2273 | GoToType( | ||
2274 | [ | ||
2275 | HoverGotoTypeData { | ||
2276 | mod_path: "ImplTrait", | ||
2277 | nav: NavigationTarget { | ||
2278 | file_id: FileId( | ||
2279 | 1, | ||
2280 | ), | ||
2281 | full_range: 0..21, | ||
2282 | name: "ImplTrait", | ||
2283 | kind: TRAIT_DEF, | ||
2284 | focus_range: Some( | ||
2285 | 6..15, | ||
2286 | ), | ||
2287 | container_name: None, | ||
2288 | description: Some( | ||
2289 | "trait ImplTrait", | ||
2290 | ), | ||
2291 | docs: None, | ||
2292 | }, | ||
2293 | }, | ||
2294 | HoverGotoTypeData { | ||
2295 | mod_path: "B", | ||
2296 | nav: NavigationTarget { | ||
2297 | file_id: FileId( | ||
2298 | 1, | ||
2299 | ), | ||
2300 | full_range: 43..57, | ||
2301 | name: "B", | ||
2302 | kind: STRUCT_DEF, | ||
2303 | focus_range: Some( | ||
2304 | 50..51, | ||
2305 | ), | ||
2306 | container_name: None, | ||
2307 | description: Some( | ||
2308 | "struct B", | ||
2309 | ), | ||
2310 | docs: None, | ||
2311 | }, | ||
2312 | }, | ||
2313 | HoverGotoTypeData { | ||
2314 | mod_path: "DynTrait", | ||
2315 | nav: NavigationTarget { | ||
2316 | file_id: FileId( | ||
2317 | 1, | ||
2318 | ), | ||
2319 | full_range: 22..42, | ||
2320 | name: "DynTrait", | ||
2321 | kind: TRAIT_DEF, | ||
2322 | focus_range: Some( | ||
2323 | 28..36, | ||
2324 | ), | ||
2325 | container_name: None, | ||
2326 | description: Some( | ||
2327 | "trait DynTrait", | ||
2328 | ), | ||
2329 | docs: None, | ||
2330 | }, | ||
2331 | }, | ||
2332 | HoverGotoTypeData { | ||
2333 | mod_path: "S", | ||
2334 | nav: NavigationTarget { | ||
2335 | file_id: FileId( | ||
2336 | 1, | ||
2337 | ), | ||
2338 | full_range: 58..69, | ||
2339 | name: "S", | ||
2340 | kind: STRUCT_DEF, | ||
2341 | focus_range: Some( | ||
2342 | 65..66, | ||
2343 | ), | ||
2344 | container_name: None, | ||
2345 | description: Some( | ||
2346 | "struct S", | ||
2347 | ), | ||
2348 | docs: None, | ||
2349 | }, | ||
2350 | }, | ||
2351 | ], | ||
2352 | ), | ||
2353 | ] | ||
2354 | "###); | ||
2355 | } | ||
2356 | |||
2357 | #[test] | ||
2358 | fn test_hover_associated_type_has_goto_type_action() { | ||
2359 | let (_, actions) = check_hover_result( | ||
2360 | " | ||
2361 | //- /main.rs | ||
2362 | trait Foo { | ||
2363 | type Item; | ||
2364 | fn get(self) -> Self::Item {} | ||
2365 | } | ||
2366 | |||
2367 | struct Bar{} | ||
2368 | struct S{} | ||
2369 | |||
2370 | impl Foo for S{ | ||
2371 | type Item = Bar; | ||
2372 | } | ||
2373 | |||
2374 | fn test() -> impl Foo { | ||
2375 | S{} | ||
2376 | } | ||
2377 | |||
2378 | fn main() { | ||
2379 | let s<|>t = test().get(); | ||
2380 | } | ||
2381 | ", | ||
2382 | &["Foo::Item<impl Foo>"], | ||
2383 | ); | ||
2384 | assert_debug_snapshot!(actions, | ||
2385 | @r###" | ||
2386 | [ | ||
2387 | GoToType( | ||
2388 | [ | ||
2389 | HoverGotoTypeData { | ||
2390 | mod_path: "Foo", | ||
2391 | nav: NavigationTarget { | ||
2392 | file_id: FileId( | ||
2393 | 1, | ||
2394 | ), | ||
2395 | full_range: 0..62, | ||
2396 | name: "Foo", | ||
2397 | kind: TRAIT_DEF, | ||
2398 | focus_range: Some( | ||
2399 | 6..9, | ||
2400 | ), | ||
2401 | container_name: None, | ||
2402 | description: Some( | ||
2403 | "trait Foo", | ||
2404 | ), | ||
2405 | docs: None, | ||
2406 | }, | ||
2407 | }, | ||
2408 | ], | ||
2409 | ), | ||
2410 | ] | ||
2411 | "###); | ||
2412 | } | ||
1313 | } | 2413 | } |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 51dc1f041..be9ab62c0 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -66,7 +66,7 @@ pub use crate::{ | |||
66 | display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, | 66 | display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, |
67 | expand_macro::ExpandedMacro, | 67 | expand_macro::ExpandedMacro, |
68 | folding_ranges::{Fold, FoldKind}, | 68 | folding_ranges::{Fold, FoldKind}, |
69 | hover::{HoverAction, HoverConfig, HoverResult}, | 69 | hover::{HoverAction, HoverConfig, HoverGotoTypeData, HoverResult}, |
70 | inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, | 70 | inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, |
71 | references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, | 71 | references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, |
72 | runnables::{Runnable, RunnableKind, TestId}, | 72 | runnables::{Runnable, RunnableKind, TestId}, |
diff --git a/crates/ra_ide/src/snapshots/highlight_doctest.html b/crates/ra_ide/src/snapshots/highlight_doctest.html index f61c0daa5..63199cdbe 100644 --- a/crates/ra_ide/src/snapshots/highlight_doctest.html +++ b/crates/ra_ide/src/snapshots/highlight_doctest.html | |||
@@ -84,4 +84,13 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
84 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</span>(&<span class="self_keyword">self</span>) -> <span class="builtin_type">bool</span> { | 84 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</span>(&<span class="self_keyword">self</span>) -> <span class="builtin_type">bool</span> { |
85 | <span class="bool_literal">true</span> | 85 | <span class="bool_literal">true</span> |
86 | } | 86 | } |
87 | } | ||
88 | |||
89 | <span class="comment documentation">/// ```</span> | ||
90 | <span class="comment documentation">/// </span><span class="macro">noop!</span>(<span class="numeric_literal">1</span>); | ||
91 | <span class="comment documentation">/// ```</span> | ||
92 | <span class="macro">macro_rules!</span> <span class="macro declaration">noop</span> { | ||
93 | ($expr:expr) => { | ||
94 | $expr | ||
95 | } | ||
87 | }</code></pre> \ No newline at end of file | 96 | }</code></pre> \ No newline at end of file |
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index f8f790e59..448645bdc 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -121,7 +121,6 @@ pub(crate) fn highlight( | |||
121 | assert!(current_macro_call == Some(mc)); | 121 | assert!(current_macro_call == Some(mc)); |
122 | current_macro_call = None; | 122 | current_macro_call = None; |
123 | format_string = None; | 123 | format_string = None; |
124 | continue; | ||
125 | } | 124 | } |
126 | _ => (), | 125 | _ => (), |
127 | } | 126 | } |
@@ -150,7 +149,7 @@ pub(crate) fn highlight( | |||
150 | 149 | ||
151 | let range = element.text_range(); | 150 | let range = element.text_range(); |
152 | 151 | ||
153 | let element_to_highlight = if current_macro_call.is_some() { | 152 | let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT { |
154 | // Inside a macro -- expand it first | 153 | // Inside a macro -- expand it first |
155 | let token = match element.clone().into_token() { | 154 | let token = match element.clone().into_token() { |
156 | Some(it) if it.parent().kind() == TOKEN_TREE => it, | 155 | Some(it) if it.parent().kind() == TOKEN_TREE => it, |
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index b4d56a7a0..93a276ffe 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs | |||
@@ -344,6 +344,15 @@ impl Foo { | |||
344 | true | 344 | true |
345 | } | 345 | } |
346 | } | 346 | } |
347 | |||
348 | /// ``` | ||
349 | /// noop!(1); | ||
350 | /// ``` | ||
351 | macro_rules! noop { | ||
352 | ($expr:expr) => { | ||
353 | $expr | ||
354 | } | ||
355 | } | ||
347 | "# | 356 | "# |
348 | .trim(), | 357 | .trim(), |
349 | "crates/ra_ide/src/snapshots/highlight_doctest.html", | 358 | "crates/ra_ide/src/snapshots/highlight_doctest.html", |
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/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index da0eb0926..192c610f1 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs | |||
@@ -75,6 +75,10 @@ pub fn record_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordF | |||
75 | } | 75 | } |
76 | } | 76 | } |
77 | 77 | ||
78 | pub fn record_field_def(name: ast::NameRef, ty: ast::TypeRef) -> ast::RecordFieldDef { | ||
79 | ast_from_text(&format!("struct S {{ {}: {}, }}", name, ty)) | ||
80 | } | ||
81 | |||
78 | pub fn block_expr( | 82 | pub fn block_expr( |
79 | stmts: impl IntoIterator<Item = ast::Stmt>, | 83 | stmts: impl IntoIterator<Item = ast::Stmt>, |
80 | tail_expr: Option<ast::Expr>, | 84 | tail_expr: Option<ast::Expr>, |
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 | ||
33 | stdx = { path = "../stdx" } | 33 | stdx = { path = "../stdx" } |
34 | 34 | ||
35 | lsp-server = "0.3.2" | 35 | lsp-server = "0.3.3" |
36 | ra_flycheck = { path = "../ra_flycheck" } | 36 | ra_flycheck = { path = "../ra_flycheck" } |
37 | ra_ide = { path = "../ra_ide" } | 37 | ra_ide = { path = "../ra_ide" } |
38 | ra_prof = { path = "../ra_prof" } | 38 | ra_prof = { path = "../ra_prof" } |
diff --git a/crates/rust-analyzer/build.rs b/crates/rust-analyzer/build.rs index d4b010c04..5ae76ba30 100644 --- a/crates/rust-analyzer/build.rs +++ b/crates/rust-analyzer/build.rs | |||
@@ -5,11 +5,14 @@ use std::{env, path::PathBuf, process::Command}; | |||
5 | fn main() { | 5 | fn main() { |
6 | set_rerun(); | 6 | set_rerun(); |
7 | 7 | ||
8 | let rev = rev().unwrap_or_else(|| "???????".to_string()); | 8 | let rev = |
9 | env::var("RUST_ANALYZER_REV").ok().or_else(rev).unwrap_or_else(|| "???????".to_string()); | ||
9 | println!("cargo:rustc-env=REV={}", rev) | 10 | println!("cargo:rustc-env=REV={}", rev) |
10 | } | 11 | } |
11 | 12 | ||
12 | fn set_rerun() { | 13 | fn set_rerun() { |
14 | println!("cargo:rerun-if-env-changed=RUST_ANALYZER_REV"); | ||
15 | |||
13 | let mut manifest_dir = PathBuf::from( | 16 | let mut manifest_dir = PathBuf::from( |
14 | env::var("CARGO_MANIFEST_DIR").expect("`CARGO_MANIFEST_DIR` is always set by cargo."), | 17 | env::var("CARGO_MANIFEST_DIR").expect("`CARGO_MANIFEST_DIR` is always set by cargo."), |
15 | ); | 18 | ); |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 0df7427cb..aa2c4ae15 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -296,6 +296,7 @@ impl Config { | |||
296 | set(value, "/hoverActions/implementations", &mut self.hover.implementations); | 296 | set(value, "/hoverActions/implementations", &mut self.hover.implementations); |
297 | set(value, "/hoverActions/run", &mut self.hover.run); | 297 | set(value, "/hoverActions/run", &mut self.hover.run); |
298 | set(value, "/hoverActions/debug", &mut self.hover.debug); | 298 | set(value, "/hoverActions/debug", &mut self.hover.debug); |
299 | set(value, "/hoverActions/gotoTypeDef", &mut self.hover.goto_type_def); | ||
299 | } else { | 300 | } else { |
300 | self.hover = HoverConfig::NO_ACTIONS; | 301 | self.hover = HoverConfig::NO_ACTIONS; |
301 | } | 302 | } |
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; | |||
20 | use crate::{ | 20 | use 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 | ||
33 | use serde::de::DeserializeOwned; | 33 | use serde::de::DeserializeOwned; |
34 | 34 | ||
35 | pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; | 35 | pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>; |
36 | pub use crate::{ | 36 | pub 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 | ||
4 | mod handlers; | 4 | mod handlers; |
5 | mod subscriptions; | 5 | mod subscriptions; |
6 | pub(crate) mod pending_requests; | 6 | pub(crate) mod request_metrics; |
7 | 7 | ||
8 | use std::{ | 8 | use std::{ |
9 | borrow::Cow, | 9 | borrow::Cow, |
@@ -17,18 +17,19 @@ use std::{ | |||
17 | }; | 17 | }; |
18 | 18 | ||
19 | use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; | 19 | use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; |
20 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; | 20 | use lsp_server::{ |
21 | Connection, ErrorCode, Message, Notification, ReqQueue, Request, RequestId, Response, | ||
22 | }; | ||
21 | use lsp_types::{ | 23 | use 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 | }; |
26 | use ra_flycheck::{CheckTask, Status}; | 28 | use ra_flycheck::{CheckTask, Status}; |
27 | use ra_ide::{Canceled, FileId, LineIndex}; | 29 | use ra_ide::{Canceled, FileId, LineIndex}; |
28 | use ra_prof::profile; | 30 | use ra_prof::profile; |
29 | use ra_project_model::{PackageRoot, ProjectWorkspace}; | 31 | use ra_project_model::{PackageRoot, ProjectWorkspace}; |
30 | use ra_vfs::VfsTask; | 32 | use ra_vfs::VfsTask; |
31 | use rustc_hash::FxHashSet; | ||
32 | use serde::{de::DeserializeOwned, Serialize}; | 33 | use serde::{de::DeserializeOwned, Serialize}; |
33 | use threadpool::ThreadPool; | 34 | use 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)] | 266 | type ReqHandler = fn(&mut GlobalState, Response); |
267 | const DO_NOTHING: ReqHandler = |_, _| (); | ||
268 | type Incoming = lsp_server::Incoming<(&'static str, Instant)>; | ||
269 | |||
270 | #[derive(Default)] | ||
268 | struct LoopState { | 271 | struct 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 | |||
280 | impl 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 | ||
290 | fn loop_turn( | 280 | fn 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( | |||
407 | fn on_task( | 373 | fn 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 | ||
428 | fn on_request( | 399 | fn 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 | ||
996 | fn request_new<R>(id: RequestId, params: R::Params) -> Request | ||
997 | where | ||
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)] |
1005 | mod tests { | 978 | mod tests { |
1006 | use std::borrow::Cow; | 979 | use std::borrow::Cow; |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index b34b529b5..2d7e649d2 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -18,8 +18,8 @@ use lsp_types::{ | |||
18 | TextDocumentIdentifier, Url, WorkspaceEdit, | 18 | TextDocumentIdentifier, Url, WorkspaceEdit, |
19 | }; | 19 | }; |
20 | use ra_ide::{ | 20 | use ra_ide::{ |
21 | FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, Runnable, RunnableKind, | 21 | FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, NavigationTarget, Query, |
22 | SearchScope, TextEdit, | 22 | RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit, |
23 | }; | 23 | }; |
24 | use ra_prof::profile; | 24 | use ra_prof::profile; |
25 | use ra_project_model::TargetKind; | 25 | use ra_project_model::TargetKind; |
@@ -1150,6 +1150,23 @@ fn debug_single_command(runnable: &lsp_ext::Runnable) -> Command { | |||
1150 | } | 1150 | } |
1151 | } | 1151 | } |
1152 | 1152 | ||
1153 | fn goto_location_command(snap: &GlobalStateSnapshot, nav: &NavigationTarget) -> Option<Command> { | ||
1154 | let value = if snap.config.client_caps.location_link { | ||
1155 | let link = to_proto::location_link(snap, None, nav.clone()).ok()?; | ||
1156 | to_value(link).ok()? | ||
1157 | } else { | ||
1158 | let range = FileRange { file_id: nav.file_id(), range: nav.range() }; | ||
1159 | let location = to_proto::location(snap, range).ok()?; | ||
1160 | to_value(location).ok()? | ||
1161 | }; | ||
1162 | |||
1163 | Some(Command { | ||
1164 | title: nav.name().to_string(), | ||
1165 | command: "rust-analyzer.gotoLocation".into(), | ||
1166 | arguments: Some(vec![value]), | ||
1167 | }) | ||
1168 | } | ||
1169 | |||
1153 | fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink { | 1170 | fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink { |
1154 | lsp_ext::CommandLink { tooltip: Some(tooltip), command } | 1171 | lsp_ext::CommandLink { tooltip: Some(tooltip), command } |
1155 | } | 1172 | } |
@@ -1180,13 +1197,13 @@ fn show_impl_command_link( | |||
1180 | None | 1197 | None |
1181 | } | 1198 | } |
1182 | 1199 | ||
1183 | fn to_runnable_action( | 1200 | fn runnable_action_links( |
1184 | snap: &GlobalStateSnapshot, | 1201 | snap: &GlobalStateSnapshot, |
1185 | file_id: FileId, | 1202 | file_id: FileId, |
1186 | runnable: Runnable, | 1203 | runnable: Runnable, |
1187 | ) -> Option<lsp_ext::CommandLinkGroup> { | 1204 | ) -> Option<lsp_ext::CommandLinkGroup> { |
1188 | let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?; | 1205 | let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?; |
1189 | if should_skip_target(&runnable, cargo_spec.as_ref()) { | 1206 | if !snap.config.hover.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) { |
1190 | return None; | 1207 | return None; |
1191 | } | 1208 | } |
1192 | 1209 | ||
@@ -1208,6 +1225,26 @@ fn to_runnable_action( | |||
1208 | }) | 1225 | }) |
1209 | } | 1226 | } |
1210 | 1227 | ||
1228 | fn goto_type_action_links( | ||
1229 | snap: &GlobalStateSnapshot, | ||
1230 | nav_targets: &[HoverGotoTypeData], | ||
1231 | ) -> Option<lsp_ext::CommandLinkGroup> { | ||
1232 | if !snap.config.hover.goto_type_def || nav_targets.is_empty() { | ||
1233 | return None; | ||
1234 | } | ||
1235 | |||
1236 | Some(lsp_ext::CommandLinkGroup { | ||
1237 | title: Some("Go to ".into()), | ||
1238 | commands: nav_targets | ||
1239 | .iter() | ||
1240 | .filter_map(|it| { | ||
1241 | goto_location_command(snap, &it.nav) | ||
1242 | .map(|cmd| to_command_link(cmd, it.mod_path.clone())) | ||
1243 | }) | ||
1244 | .collect(), | ||
1245 | }) | ||
1246 | } | ||
1247 | |||
1211 | fn prepare_hover_actions( | 1248 | fn prepare_hover_actions( |
1212 | snap: &GlobalStateSnapshot, | 1249 | snap: &GlobalStateSnapshot, |
1213 | file_id: FileId, | 1250 | file_id: FileId, |
@@ -1221,7 +1258,8 @@ fn prepare_hover_actions( | |||
1221 | .iter() | 1258 | .iter() |
1222 | .filter_map(|it| match it { | 1259 | .filter_map(|it| match it { |
1223 | HoverAction::Implementaion(position) => show_impl_command_link(snap, position), | 1260 | HoverAction::Implementaion(position) => show_impl_command_link(snap, position), |
1224 | HoverAction::Runnable(r) => to_runnable_action(snap, file_id, r.clone()), | 1261 | HoverAction::Runnable(r) => runnable_action_links(snap, file_id, r.clone()), |
1262 | HoverAction::GoToType(targets) => goto_type_action_links(snap, targets), | ||
1225 | }) | 1263 | }) |
1226 | .collect() | 1264 | .collect() |
1227 | } | 1265 | } |
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 | |||
3 | use std::time::{Duration, Instant}; | ||
4 | |||
5 | use lsp_server::RequestId; | ||
6 | use rustc_hash::FxHashMap; | ||
7 | |||
8 | #[derive(Debug)] | ||
9 | pub struct CompletedRequest { | ||
10 | pub id: RequestId, | ||
11 | pub method: String, | ||
12 | pub duration: Duration, | ||
13 | } | ||
14 | |||
15 | #[derive(Debug)] | ||
16 | pub(crate) struct PendingRequest { | ||
17 | pub(crate) id: RequestId, | ||
18 | pub(crate) method: String, | ||
19 | pub(crate) received: Instant, | ||
20 | } | ||
21 | |||
22 | impl 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)] | ||
33 | pub(crate) struct PendingRequests { | ||
34 | map: FxHashMap<RequestId, PendingRequest>, | ||
35 | } | ||
36 | |||
37 | impl 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 | |||
51 | const N_COMPLETED_REQUESTS: usize = 10; | ||
52 | |||
53 | #[derive(Debug, Default)] | ||
54 | pub 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 | |||
60 | impl 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 | ||
2 | use std::time::Duration; | ||
3 | |||
4 | use lsp_server::RequestId; | ||
5 | |||
6 | #[derive(Debug)] | ||
7 | pub(crate) struct RequestMetrics { | ||
8 | pub(crate) id: RequestId, | ||
9 | pub(crate) method: String, | ||
10 | pub(crate) duration: Duration, | ||
11 | } | ||
12 | |||
13 | const N_COMPLETED_REQUESTS: usize = 10; | ||
14 | |||
15 | #[derive(Debug, Default)] | ||
16 | pub(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 | |||
22 | impl 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/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index c0356344c..f2ff0e435 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | //! Missing batteries for standard libraries. | 1 | //! Missing batteries for standard libraries. |
2 | |||
3 | use std::{cell::Cell, fmt, time::Instant}; | 2 | use std::{cell::Cell, fmt, time::Instant}; |
4 | 3 | ||
5 | #[inline(always)] | 4 | #[inline(always)] |
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs index 7dc721f7e..724606a3d 100644 --- a/crates/vfs/src/file_set.rs +++ b/crates/vfs/src/file_set.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | //! | 2 | //! |
3 | //! Files which do not belong to any explicitly configured `FileSet` belong to | 3 | //! Files which do not belong to any explicitly configured `FileSet` belong to |
4 | //! the default `FileSet`. | 4 | //! the default `FileSet`. |
5 | use std::{cmp, fmt, iter}; | 5 | use std::{fmt, iter}; |
6 | 6 | ||
7 | use paths::AbsPathBuf; | 7 | use paths::AbsPathBuf; |
8 | use rustc_hash::FxHashMap; | 8 | use rustc_hash::FxHashMap; |
@@ -44,6 +44,12 @@ pub struct FileSetConfig { | |||
44 | roots: Vec<(AbsPathBuf, usize)>, | 44 | roots: Vec<(AbsPathBuf, usize)>, |
45 | } | 45 | } |
46 | 46 | ||
47 | impl Default for FileSetConfig { | ||
48 | fn default() -> Self { | ||
49 | FileSetConfig::builder().build() | ||
50 | } | ||
51 | } | ||
52 | |||
47 | impl FileSetConfig { | 53 | impl FileSetConfig { |
48 | pub fn builder() -> FileSetConfigBuilder { | 54 | pub fn builder() -> FileSetConfigBuilder { |
49 | FileSetConfigBuilder::default() | 55 | FileSetConfigBuilder::default() |
@@ -60,14 +66,19 @@ impl FileSetConfig { | |||
60 | self.n_file_sets | 66 | self.n_file_sets |
61 | } | 67 | } |
62 | fn classify(&self, path: &VfsPath) -> usize { | 68 | fn classify(&self, path: &VfsPath) -> usize { |
63 | for (root, idx) in self.roots.iter() { | 69 | let path = match path.as_path() { |
64 | if let Some(path) = path.as_path() { | 70 | Some(it) => it, |
65 | if path.starts_with(root) { | 71 | None => return self.len() - 1, |
66 | return *idx; | 72 | }; |
67 | } | 73 | let idx = match self.roots.binary_search_by(|(p, _)| p.as_path().cmp(path)) { |
68 | } | 74 | Ok(it) => it, |
75 | Err(it) => it.saturating_sub(1), | ||
76 | }; | ||
77 | if path.starts_with(&self.roots[idx].0) { | ||
78 | self.roots[idx].1 | ||
79 | } else { | ||
80 | self.len() - 1 | ||
69 | } | 81 | } |
70 | self.len() - 1 | ||
71 | } | 82 | } |
72 | } | 83 | } |
73 | 84 | ||
@@ -82,6 +93,9 @@ impl Default for FileSetConfigBuilder { | |||
82 | } | 93 | } |
83 | 94 | ||
84 | impl FileSetConfigBuilder { | 95 | impl FileSetConfigBuilder { |
96 | pub fn len(&self) -> usize { | ||
97 | self.roots.len() | ||
98 | } | ||
85 | pub fn add_file_set(&mut self, roots: Vec<AbsPathBuf>) { | 99 | pub fn add_file_set(&mut self, roots: Vec<AbsPathBuf>) { |
86 | self.roots.push(roots) | 100 | self.roots.push(roots) |
87 | } | 101 | } |
@@ -93,7 +107,7 @@ impl FileSetConfigBuilder { | |||
93 | .enumerate() | 107 | .enumerate() |
94 | .flat_map(|(i, paths)| paths.into_iter().zip(iter::repeat(i))) | 108 | .flat_map(|(i, paths)| paths.into_iter().zip(iter::repeat(i))) |
95 | .collect(); | 109 | .collect(); |
96 | roots.sort_by_key(|(path, _)| cmp::Reverse(path.to_string_lossy().len())); | 110 | roots.sort(); |
97 | FileSetConfig { n_file_sets, roots } | 111 | FileSetConfig { n_file_sets, roots } |
98 | } | 112 | } |
99 | } | 113 | } |
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index 75ce61cf9..055219b0c 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs | |||
@@ -79,6 +79,9 @@ pub enum ChangeKind { | |||
79 | } | 79 | } |
80 | 80 | ||
81 | impl Vfs { | 81 | impl Vfs { |
82 | pub fn len(&self) -> usize { | ||
83 | self.data.len() | ||
84 | } | ||
82 | pub fn file_id(&self, path: &VfsPath) -> Option<FileId> { | 85 | pub fn file_id(&self, path: &VfsPath) -> Option<FileId> { |
83 | self.interner.get(path).filter(|&it| self.get(it).is_some()) | 86 | self.interner.get(path).filter(|&it| self.get(it).is_some()) |
84 | } | 87 | } |
diff --git a/editors/code/package.json b/editors/code/package.json index 3acc375f6..e6ceb235f 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -510,6 +510,11 @@ | |||
510 | "type": "boolean", | 510 | "type": "boolean", |
511 | "default": true | 511 | "default": true |
512 | }, | 512 | }, |
513 | "rust-analyzer.hoverActions.gotoTypeDef": { | ||
514 | "markdownDescription": "Whether to show `Go to Type Definition` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", | ||
515 | "type": "boolean", | ||
516 | "default": true | ||
517 | }, | ||
513 | "rust-analyzer.linkedProjects": { | 518 | "rust-analyzer.linkedProjects": { |
514 | "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set of projects. \nElements must be paths pointing to Cargo.toml, rust-project.json, or JSON objects in rust-project.json format", | 519 | "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set of projects. \nElements must be paths pointing to Cargo.toml, rust-project.json, or JSON objects in rust-project.json format", |
515 | "type": "array", | 520 | "type": "array", |
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 3e9c3aa0e..48a25495f 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts | |||
@@ -353,6 +353,20 @@ export function applyActionGroup(_ctx: Ctx): Cmd { | |||
353 | }; | 353 | }; |
354 | } | 354 | } |
355 | 355 | ||
356 | export function gotoLocation(ctx: Ctx): Cmd { | ||
357 | return async (locationLink: lc.LocationLink) => { | ||
358 | const client = ctx.client; | ||
359 | if (client) { | ||
360 | const uri = client.protocol2CodeConverter.asUri(locationLink.targetUri); | ||
361 | let range = client.protocol2CodeConverter.asRange(locationLink.targetSelectionRange); | ||
362 | // collapse the range to a cursor position | ||
363 | range = range.with({ end: range.start }); | ||
364 | |||
365 | await vscode.window.showTextDocument(uri, { selection: range }); | ||
366 | } | ||
367 | }; | ||
368 | } | ||
369 | |||
356 | export function resolveCodeAction(ctx: Ctx): Cmd { | 370 | export function resolveCodeAction(ctx: Ctx): Cmd { |
357 | const client = ctx.client; | 371 | const client = ctx.client; |
358 | return async (params: ra.ResolveCodeActionParams) => { | 372 | return async (params: ra.ResolveCodeActionParams) => { |
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index d8f0037d4..9591d4fe3 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -117,7 +117,7 @@ export class Config { | |||
117 | return { | 117 | return { |
118 | engine: this.get<string>("debug.engine"), | 118 | engine: this.get<string>("debug.engine"), |
119 | engineSettings: this.get<object>("debug.engineSettings"), | 119 | engineSettings: this.get<object>("debug.engineSettings"), |
120 | openUpDebugPane: this.get<boolean>("debug.openUpDebugPane"), | 120 | openDebugPane: this.get<boolean>("debug.openDebugPane"), |
121 | sourceFileMap: sourceFileMap | 121 | sourceFileMap: sourceFileMap |
122 | }; | 122 | }; |
123 | } | 123 | } |
@@ -135,6 +135,9 @@ export class Config { | |||
135 | return { | 135 | return { |
136 | enable: this.get<boolean>("hoverActions.enable"), | 136 | enable: this.get<boolean>("hoverActions.enable"), |
137 | implementations: this.get<boolean>("hoverActions.implementations"), | 137 | implementations: this.get<boolean>("hoverActions.implementations"), |
138 | run: this.get<boolean>("hoverActions.run"), | ||
139 | debug: this.get<boolean>("hoverActions.debug"), | ||
140 | gotoTypeDef: this.get<boolean>("hoverActions.gotoTypeDef"), | ||
138 | }; | 141 | }; |
139 | } | 142 | } |
140 | } | 143 | } |
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index a0c9b3ab2..61c12dbe0 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts | |||
@@ -82,7 +82,7 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<v | |||
82 | } | 82 | } |
83 | 83 | ||
84 | debugOutput.clear(); | 84 | debugOutput.clear(); |
85 | if (ctx.config.debug.openUpDebugPane) { | 85 | if (ctx.config.debug.openDebugPane) { |
86 | debugOutput.show(true); | 86 | debugOutput.show(true); |
87 | } | 87 | } |
88 | 88 | ||
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index a92c676fa..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) { |
@@ -100,6 +109,7 @@ export async function activate(context: vscode.ExtensionContext) { | |||
100 | ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand); | 109 | ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand); |
101 | ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction); | 110 | ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction); |
102 | ctx.registerCommand('applyActionGroup', commands.applyActionGroup); | 111 | ctx.registerCommand('applyActionGroup', commands.applyActionGroup); |
112 | ctx.registerCommand('gotoLocation', commands.gotoLocation); | ||
103 | 113 | ||
104 | ctx.pushCleanup(activateTaskProvider(workspaceFolder)); | 114 | ctx.pushCleanup(activateTaskProvider(workspaceFolder)); |
105 | 115 | ||
@@ -284,6 +294,11 @@ async function getServer(config: Config, state: PersistentState): Promise<string | |||
284 | const artifact = release.assets.find(artifact => artifact.name === binaryName); | 294 | const artifact = release.assets.find(artifact => artifact.name === binaryName); |
285 | assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); | 295 | assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); |
286 | 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 | |||
287 | 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 }); |
288 | 303 | ||
289 | // 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 492213937..0e7dd29c2 100644 --- a/editors/code/src/net.ts +++ b/editors/code/src/net.ts | |||
@@ -1,7 +1,9 @@ | |||
1 | import fetch from "node-fetch"; | 1 | import fetch from "node-fetch"; |
2 | import * as vscode from "vscode"; | 2 | import * as vscode from "vscode"; |
3 | import * as fs from "fs"; | ||
4 | import * as stream from "stream"; | 3 | import * as stream from "stream"; |
4 | import * as fs from "fs"; | ||
5 | import * as os from "os"; | ||
6 | import * as path from "path"; | ||
5 | import * as util from "util"; | 7 | import * as util from "util"; |
6 | import { log, assert } from "./util"; | 8 | import { 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,14 +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 |
124 | await withTempFile(async tempFilePath => { | ||
125 | const destFileStream = fs.createWriteStream(tempFilePath, { mode }); | ||
126 | await pipeline(res.body, destFileStream); | ||
127 | await new Promise<void>(resolve => { | ||
128 | destFileStream.on("close", resolve); | ||
129 | destFileStream.destroy(); | ||
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); | ||
134 | }); | ||
135 | } | ||
122 | 136 | ||
123 | await pipeline(res.body, destFileStream); | 137 | async function withTempFile(scope: (tempFilePath: string) => Promise<void>) { |
124 | return new Promise<void>(resolve => { | 138 | // Based on the great article: https://advancedweb.hu/secure-tempfiles-in-nodejs-without-dependencies/ |
125 | destFileStream.on("close", resolve); | ||
126 | destFileStream.destroy(); | ||
127 | 139 | ||
128 | // Details on workaround: https://github.com/rust-analyzer/rust-analyzer/pull/3092#discussion_r378191131 | 140 | // `.realpath()` should handle the cases where os.tmpdir() contains symlinks |
129 | // Issue at nodejs repo: https://github.com/nodejs/node/issues/31776 | 141 | const osTempDir = await fs.promises.realpath(os.tmpdir()); |
130 | }); | 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 | |||
153 | async 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 | } | ||
131 | } | 165 | } |