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