aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--crates/hir/src/lib.rs2
-rw-r--r--crates/hir/src/source_analyzer.rs15
-rw-r--r--crates/hir_def/src/body/lower.rs29
-rw-r--r--crates/hir_def/src/expr.rs19
-rw-r--r--crates/hir_def/src/lib.rs13
-rw-r--r--crates/hir_def/src/path.rs6
-rw-r--r--crates/hir_ty/src/builder.rs2
-rw-r--r--crates/hir_ty/src/chalk_ext.rs229
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs9
-rw-r--r--crates/hir_ty/src/diagnostics/match_check.rs2
-rw-r--r--crates/hir_ty/src/diagnostics/unsafe_check.rs4
-rw-r--r--crates/hir_ty/src/infer.rs7
-rw-r--r--crates/hir_ty/src/infer/coerce.rs2
-rw-r--r--crates/hir_ty/src/infer/expr.rs20
-rw-r--r--crates/hir_ty/src/infer/pat.rs12
-rw-r--r--crates/hir_ty/src/infer/path.rs4
-rw-r--r--crates/hir_ty/src/lib.rs214
-rw-r--r--crates/hir_ty/src/lower.rs5
-rw-r--r--crates/hir_ty/src/method_resolution.rs2
-rw-r--r--crates/hir_ty/src/traits/chalk.rs2
-rw-r--r--crates/hir_ty/src/utils.rs13
-rw-r--r--crates/paths/src/lib.rs7
-rw-r--r--crates/project_model/src/build_data.rs86
-rw-r--r--crates/project_model/src/workspace.rs10
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs4
-rw-r--r--crates/rust-analyzer/src/global_state.rs62
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs30
-rw-r--r--crates/rust-analyzer/src/main_loop.rs101
-rw-r--r--crates/rust-analyzer/src/op_queue.rs16
-rw-r--r--crates/rust-analyzer/src/reload.rs213
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/support.rs11
-rw-r--r--crates/stdx/src/lib.rs8
-rw-r--r--crates/vfs/Cargo.toml1
-rw-r--r--crates/vfs/src/path_interner.rs29
-rw-r--r--docs/dev/lsp-extensions.md30
-rw-r--r--editors/code/src/client.ts2
-rw-r--r--editors/code/src/ctx.ts47
-rw-r--r--editors/code/src/lsp_ext.ts9
40 files changed, 712 insertions, 568 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e40c12a92..ca0311595 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1867,6 +1867,7 @@ name = "vfs"
1867version = "0.0.0" 1867version = "0.0.0"
1868dependencies = [ 1868dependencies = [
1869 "fst", 1869 "fst",
1870 "indexmap",
1870 "paths", 1871 "paths",
1871 "rustc-hash", 1872 "rustc-hash",
1872] 1873]
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index dfc1d8a0c..8d00f7401 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -59,7 +59,7 @@ use hir_ty::{
59 traits::FnTrait, 59 traits::FnTrait,
60 AliasEq, AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, 60 AliasEq, AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast,
61 DebruijnIndex, InEnvironment, Interner, QuantifiedWhereClause, Scalar, Solution, 61 DebruijnIndex, InEnvironment, Interner, QuantifiedWhereClause, Scalar, Solution,
62 SolutionVariables, Substitution, TraitEnvironment, Ty, TyBuilder, TyDefId, TyKind, 62 SolutionVariables, Substitution, TraitEnvironment, Ty, TyBuilder, TyDefId, TyExt, TyKind,
63 TyVariableKind, WhereClause, 63 TyVariableKind, WhereClause,
64}; 64};
65use itertools::Itertools; 65use itertools::Itertools;
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 4ce1c2080..c013e78d9 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -161,14 +161,15 @@ impl SourceAnalyzer {
161 db: &dyn HirDatabase, 161 db: &dyn HirDatabase,
162 field: &ast::RecordExprField, 162 field: &ast::RecordExprField,
163 ) -> Option<(Field, Option<Local>)> { 163 ) -> Option<(Field, Option<Local>)> {
164 let expr_id = 164 let record_expr = ast::RecordExpr::cast(field.syntax().parent().and_then(|p| p.parent())?)?;
165 self.body_source_map.as_ref()?.node_field(InFile::new(self.file_id, field))?; 165 let expr = ast::Expr::from(record_expr);
166 let expr_id = self.body_source_map.as_ref()?.node_expr(InFile::new(self.file_id, &expr))?;
166 167
168 let local_name = field.field_name()?.as_name();
167 let local = if field.name_ref().is_some() { 169 let local = if field.name_ref().is_some() {
168 None 170 None
169 } else { 171 } else {
170 let local_name = field.field_name()?.as_name(); 172 let path = ModPath::from_segments(PathKind::Plain, once(local_name.clone()));
171 let path = ModPath::from_segments(PathKind::Plain, once(local_name));
172 match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) { 173 match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) {
173 Some(ValueNs::LocalBinding(pat_id)) => { 174 Some(ValueNs::LocalBinding(pat_id)) => {
174 Some(Local { pat_id, parent: self.resolver.body_owner()? }) 175 Some(Local { pat_id, parent: self.resolver.body_owner()? })
@@ -176,8 +177,10 @@ impl SourceAnalyzer {
176 _ => None, 177 _ => None,
177 } 178 }
178 }; 179 };
179 let struct_field = self.infer.as_ref()?.record_field_resolution(expr_id)?; 180 let variant = self.infer.as_ref()?.variant_resolution_for_expr(expr_id)?;
180 Some((struct_field.into(), local)) 181 let variant_data = variant.variant_data(db.upcast());
182 let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? };
183 Some((field.into(), local))
181 } 184 }
182 185
183 pub(crate) fn resolve_record_pat_field( 186 pub(crate) fn resolve_record_pat_field(
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 63e89a1f4..bfb75a8a5 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -30,6 +30,7 @@ use crate::{
30 LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, 30 LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField,
31 Statement, 31 Statement,
32 }, 32 },
33 intern::Interned,
33 item_scope::BuiltinShadowMode, 34 item_scope::BuiltinShadowMode,
34 path::{GenericArgs, Path}, 35 path::{GenericArgs, Path},
35 type_ref::{Mutability, Rawness, TypeRef}, 36 type_ref::{Mutability, Rawness, TypeRef},
@@ -322,8 +323,10 @@ impl ExprCollector<'_> {
322 Vec::new() 323 Vec::new()
323 }; 324 };
324 let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); 325 let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
325 let generic_args = 326 let generic_args = e
326 e.generic_arg_list().and_then(|it| GenericArgs::from_ast(&self.ctx(), it)); 327 .generic_arg_list()
328 .and_then(|it| GenericArgs::from_ast(&self.ctx(), it))
329 .map(Box::new);
327 self.alloc_expr( 330 self.alloc_expr(
328 Expr::MethodCall { receiver, method_name, args, generic_args }, 331 Expr::MethodCall { receiver, method_name, args, generic_args },
329 syntax_ptr, 332 syntax_ptr,
@@ -385,7 +388,7 @@ impl ExprCollector<'_> {
385 self.alloc_expr(Expr::Yield { expr }, syntax_ptr) 388 self.alloc_expr(Expr::Yield { expr }, syntax_ptr)
386 } 389 }
387 ast::Expr::RecordExpr(e) => { 390 ast::Expr::RecordExpr(e) => {
388 let path = e.path().and_then(|path| self.expander.parse_path(path)); 391 let path = e.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
389 let record_lit = if let Some(nfl) = e.record_expr_field_list() { 392 let record_lit = if let Some(nfl) = e.record_expr_field_list() {
390 let fields = nfl 393 let fields = nfl
391 .fields() 394 .fields()
@@ -430,7 +433,7 @@ impl ExprCollector<'_> {
430 } 433 }
431 ast::Expr::CastExpr(e) => { 434 ast::Expr::CastExpr(e) => {
432 let expr = self.collect_expr_opt(e.expr()); 435 let expr = self.collect_expr_opt(e.expr());
433 let type_ref = TypeRef::from_ast_opt(&self.ctx(), e.ty()); 436 let type_ref = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
434 self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr) 437 self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr)
435 } 438 }
436 ast::Expr::RefExpr(e) => { 439 ast::Expr::RefExpr(e) => {
@@ -464,13 +467,16 @@ impl ExprCollector<'_> {
464 if let Some(pl) = e.param_list() { 467 if let Some(pl) = e.param_list() {
465 for param in pl.params() { 468 for param in pl.params() {
466 let pat = self.collect_pat_opt(param.pat()); 469 let pat = self.collect_pat_opt(param.pat());
467 let type_ref = param.ty().map(|it| TypeRef::from_ast(&self.ctx(), it)); 470 let type_ref =
471 param.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
468 args.push(pat); 472 args.push(pat);
469 arg_types.push(type_ref); 473 arg_types.push(type_ref);
470 } 474 }
471 } 475 }
472 let ret_type = 476 let ret_type = e
473 e.ret_type().and_then(|r| r.ty()).map(|it| TypeRef::from_ast(&self.ctx(), it)); 477 .ret_type()
478 .and_then(|r| r.ty())
479 .map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
474 let body = self.collect_expr_opt(e.body()); 480 let body = self.collect_expr_opt(e.body());
475 self.alloc_expr(Expr::Lambda { args, arg_types, ret_type, body }, syntax_ptr) 481 self.alloc_expr(Expr::Lambda { args, arg_types, ret_type, body }, syntax_ptr)
476 } 482 }
@@ -625,7 +631,8 @@ impl ExprCollector<'_> {
625 return; 631 return;
626 } 632 }
627 let pat = self.collect_pat_opt(stmt.pat()); 633 let pat = self.collect_pat_opt(stmt.pat());
628 let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it)); 634 let type_ref =
635 stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
629 let initializer = stmt.initializer().map(|e| self.collect_expr(e)); 636 let initializer = stmt.initializer().map(|e| self.collect_expr(e));
630 self.statements_in_scope.push(Statement::Let { pat, type_ref, initializer }); 637 self.statements_in_scope.push(Statement::Let { pat, type_ref, initializer });
631 } 638 }
@@ -755,7 +762,7 @@ impl ExprCollector<'_> {
755 } 762 }
756 } 763 }
757 ast::Pat::TupleStructPat(p) => { 764 ast::Pat::TupleStructPat(p) => {
758 let path = p.path().and_then(|path| self.expander.parse_path(path)); 765 let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
759 let (args, ellipsis) = self.collect_tuple_pat(p.fields()); 766 let (args, ellipsis) = self.collect_tuple_pat(p.fields());
760 Pat::TupleStruct { path, args, ellipsis } 767 Pat::TupleStruct { path, args, ellipsis }
761 } 768 }
@@ -765,7 +772,7 @@ impl ExprCollector<'_> {
765 Pat::Ref { pat, mutability } 772 Pat::Ref { pat, mutability }
766 } 773 }
767 ast::Pat::PathPat(p) => { 774 ast::Pat::PathPat(p) => {
768 let path = p.path().and_then(|path| self.expander.parse_path(path)); 775 let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
769 path.map(Pat::Path).unwrap_or(Pat::Missing) 776 path.map(Pat::Path).unwrap_or(Pat::Missing)
770 } 777 }
771 ast::Pat::OrPat(p) => { 778 ast::Pat::OrPat(p) => {
@@ -779,7 +786,7 @@ impl ExprCollector<'_> {
779 } 786 }
780 ast::Pat::WildcardPat(_) => Pat::Wild, 787 ast::Pat::WildcardPat(_) => Pat::Wild,
781 ast::Pat::RecordPat(p) => { 788 ast::Pat::RecordPat(p) => {
782 let path = p.path().and_then(|path| self.expander.parse_path(path)); 789 let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
783 let args: Vec<_> = p 790 let args: Vec<_> = p
784 .record_pat_field_list() 791 .record_pat_field_list()
785 .expect("every struct should have a field list") 792 .expect("every struct should have a field list")
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs
index 6c7376fad..b4ad984bd 100644
--- a/crates/hir_def/src/expr.rs
+++ b/crates/hir_def/src/expr.rs
@@ -18,6 +18,7 @@ use syntax::ast::RangeOp;
18 18
19use crate::{ 19use crate::{
20 builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}, 20 builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
21 intern::Interned,
21 path::{GenericArgs, Path}, 22 path::{GenericArgs, Path},
22 type_ref::{Mutability, Rawness, TypeRef}, 23 type_ref::{Mutability, Rawness, TypeRef},
23 BlockId, 24 BlockId,
@@ -86,7 +87,7 @@ pub enum Expr {
86 receiver: ExprId, 87 receiver: ExprId,
87 method_name: Name, 88 method_name: Name,
88 args: Vec<ExprId>, 89 args: Vec<ExprId>,
89 generic_args: Option<GenericArgs>, 90 generic_args: Option<Box<GenericArgs>>,
90 }, 91 },
91 Match { 92 Match {
92 expr: ExprId, 93 expr: ExprId,
@@ -106,7 +107,7 @@ pub enum Expr {
106 expr: Option<ExprId>, 107 expr: Option<ExprId>,
107 }, 108 },
108 RecordLit { 109 RecordLit {
109 path: Option<Path>, 110 path: Option<Box<Path>>,
110 fields: Vec<RecordLitField>, 111 fields: Vec<RecordLitField>,
111 spread: Option<ExprId>, 112 spread: Option<ExprId>,
112 }, 113 },
@@ -131,7 +132,7 @@ pub enum Expr {
131 }, 132 },
132 Cast { 133 Cast {
133 expr: ExprId, 134 expr: ExprId,
134 type_ref: TypeRef, 135 type_ref: Interned<TypeRef>,
135 }, 136 },
136 Ref { 137 Ref {
137 expr: ExprId, 138 expr: ExprId,
@@ -161,8 +162,8 @@ pub enum Expr {
161 }, 162 },
162 Lambda { 163 Lambda {
163 args: Vec<PatId>, 164 args: Vec<PatId>,
164 arg_types: Vec<Option<TypeRef>>, 165 arg_types: Vec<Option<Interned<TypeRef>>>,
165 ret_type: Option<TypeRef>, 166 ret_type: Option<Interned<TypeRef>>,
166 body: ExprId, 167 body: ExprId,
167 }, 168 },
168 Tuple { 169 Tuple {
@@ -240,7 +241,7 @@ pub struct RecordLitField {
240 241
241#[derive(Debug, Clone, Eq, PartialEq)] 242#[derive(Debug, Clone, Eq, PartialEq)]
242pub enum Statement { 243pub enum Statement {
243 Let { pat: PatId, type_ref: Option<TypeRef>, initializer: Option<ExprId> }, 244 Let { pat: PatId, type_ref: Option<Interned<TypeRef>>, initializer: Option<ExprId> },
244 Expr(ExprId), 245 Expr(ExprId),
245} 246}
246 247
@@ -412,13 +413,13 @@ pub enum Pat {
412 Wild, 413 Wild,
413 Tuple { args: Vec<PatId>, ellipsis: Option<usize> }, 414 Tuple { args: Vec<PatId>, ellipsis: Option<usize> },
414 Or(Vec<PatId>), 415 Or(Vec<PatId>),
415 Record { path: Option<Path>, args: Vec<RecordFieldPat>, ellipsis: bool }, 416 Record { path: Option<Box<Path>>, args: Vec<RecordFieldPat>, ellipsis: bool },
416 Range { start: ExprId, end: ExprId }, 417 Range { start: ExprId, end: ExprId },
417 Slice { prefix: Vec<PatId>, slice: Option<PatId>, suffix: Vec<PatId> }, 418 Slice { prefix: Vec<PatId>, slice: Option<PatId>, suffix: Vec<PatId> },
418 Path(Path), 419 Path(Box<Path>),
419 Lit(ExprId), 420 Lit(ExprId),
420 Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> }, 421 Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> },
421 TupleStruct { path: Option<Path>, args: Vec<PatId>, ellipsis: Option<usize> }, 422 TupleStruct { path: Option<Box<Path>>, args: Vec<PatId>, ellipsis: Option<usize> },
422 Ref { pat: PatId, mutability: Mutability }, 423 Ref { pat: PatId, mutability: Mutability },
423 Box { inner: PatId }, 424 Box { inner: PatId },
424 ConstBlock(ExprId), 425 ConstBlock(ExprId),
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index be9a5e1a0..abd6c553f 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -56,6 +56,7 @@ use std::{
56 sync::Arc, 56 sync::Arc,
57}; 57};
58 58
59use adt::VariantData;
59use base_db::{impl_intern_key, salsa, CrateId}; 60use base_db::{impl_intern_key, salsa, CrateId};
60use hir_expand::{ 61use hir_expand::{
61 ast_id_map::FileAstId, 62 ast_id_map::FileAstId,
@@ -442,6 +443,18 @@ pub enum VariantId {
442} 443}
443impl_from!(EnumVariantId, StructId, UnionId for VariantId); 444impl_from!(EnumVariantId, StructId, UnionId for VariantId);
444 445
446impl VariantId {
447 pub fn variant_data(self, db: &dyn db::DefDatabase) -> Arc<VariantData> {
448 match self {
449 VariantId::StructId(it) => db.struct_data(it).variant_data.clone(),
450 VariantId::UnionId(it) => db.union_data(it).variant_data.clone(),
451 VariantId::EnumVariantId(it) => {
452 db.enum_data(it.parent).variants[it.local_id].variant_data.clone()
453 }
454 }
455 }
456}
457
445trait Intern { 458trait Intern {
446 type ID; 459 type ID;
447 fn intern(self, db: &dyn db::DefDatabase) -> Self::ID; 460 fn intern(self, db: &dyn db::DefDatabase) -> Self::ID;
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs
index f9c8328f0..b528ff8ba 100644
--- a/crates/hir_def/src/path.rs
+++ b/crates/hir_def/src/path.rs
@@ -289,6 +289,12 @@ impl From<Name> for Path {
289 } 289 }
290} 290}
291 291
292impl From<Name> for Box<Path> {
293 fn from(name: Name) -> Box<Path> {
294 Box::new(Path::from(name))
295 }
296}
297
292impl From<Name> for ModPath { 298impl From<Name> for ModPath {
293 fn from(name: Name) -> ModPath { 299 fn from(name: Name) -> ModPath {
294 ModPath::from_segments(PathKind::Plain, iter::once(name)) 300 ModPath::from_segments(PathKind::Plain, iter::once(name))
diff --git a/crates/hir_ty/src/builder.rs b/crates/hir_ty/src/builder.rs
index 791915fe0..09512d1ce 100644
--- a/crates/hir_ty/src/builder.rs
+++ b/crates/hir_ty/src/builder.rs
@@ -13,7 +13,7 @@ use smallvec::SmallVec;
13use crate::{ 13use crate::{
14 db::HirDatabase, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, 14 db::HirDatabase, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders,
15 CallableSig, FnPointer, FnSig, FnSubst, GenericArg, Interner, ProjectionTy, Substitution, 15 CallableSig, FnPointer, FnSig, FnSubst, GenericArg, Interner, ProjectionTy, Substitution,
16 TraitRef, Ty, TyDefId, TyKind, TypeWalk, ValueTyDefId, 16 TraitRef, Ty, TyDefId, TyExt, TyKind, TypeWalk, ValueTyDefId,
17}; 17};
18 18
19/// This is a builder for `Ty` or anything that needs a `Substitution`. 19/// This is a builder for `Ty` or anything that needs a `Substitution`.
diff --git a/crates/hir_ty/src/chalk_ext.rs b/crates/hir_ty/src/chalk_ext.rs
index 0f4cb43e9..8e8a1aa48 100644
--- a/crates/hir_ty/src/chalk_ext.rs
+++ b/crates/hir_ty/src/chalk_ext.rs
@@ -1,20 +1,243 @@
1//! Various extensions traits for Chalk types. 1//! Various extensions traits for Chalk types.
2 2
3use hir_def::{AssocContainerId, Lookup, TraitId}; 3use chalk_ir::Mutability;
4use hir_def::{
5 type_ref::Rawness, AssocContainerId, FunctionId, GenericDefId, HasModule, Lookup, TraitId,
6};
4 7
5use crate::{ 8use crate::{
6 db::HirDatabase, from_assoc_type_id, to_chalk_trait_id, Interner, ProjectionTy, TraitRef, Ty, 9 db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
7 TyKind, 10 from_placeholder_idx, to_chalk_trait_id, AdtId, AliasEq, AliasTy, Binders, CallableDefId,
11 CallableSig, ImplTraitId, Interner, Lifetime, ProjectionTy, QuantifiedWhereClause,
12 Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
8}; 13};
9 14
10pub trait TyExt { 15pub trait TyExt {
11 fn is_unit(&self) -> bool; 16 fn is_unit(&self) -> bool;
17 fn is_never(&self) -> bool;
18 fn is_unknown(&self) -> bool;
19
20 fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
21 fn as_tuple(&self) -> Option<&Substitution>;
22 fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>;
23 fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>;
24 fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>;
25 fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId>;
26
27 fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId>;
28 fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig>;
29
30 fn strip_references(&self) -> &Ty;
31
32 /// If this is a `dyn Trait`, returns that trait.
33 fn dyn_trait(&self) -> Option<TraitId>;
34
35 fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>;
36 fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>;
12} 37}
13 38
14impl TyExt for Ty { 39impl TyExt for Ty {
15 fn is_unit(&self) -> bool { 40 fn is_unit(&self) -> bool {
16 matches!(self.kind(&Interner), TyKind::Tuple(0, _)) 41 matches!(self.kind(&Interner), TyKind::Tuple(0, _))
17 } 42 }
43
44 fn is_never(&self) -> bool {
45 matches!(self.kind(&Interner), TyKind::Never)
46 }
47
48 fn is_unknown(&self) -> bool {
49 matches!(self.kind(&Interner), TyKind::Error)
50 }
51
52 fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> {
53 match self.kind(&Interner) {
54 TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)),
55 _ => None,
56 }
57 }
58
59 fn as_tuple(&self) -> Option<&Substitution> {
60 match self.kind(&Interner) {
61 TyKind::Tuple(_, substs) => Some(substs),
62 _ => None,
63 }
64 }
65
66 fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> {
67 if let Some(CallableDefId::FunctionId(func)) = self.callable_def(db) {
68 Some(func)
69 } else {
70 None
71 }
72 }
73 fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> {
74 match self.kind(&Interner) {
75 TyKind::Ref(mutability, lifetime, ty) => Some((ty, *lifetime, *mutability)),
76 _ => None,
77 }
78 }
79
80 fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> {
81 match self.kind(&Interner) {
82 TyKind::Ref(mutability, _, ty) => Some((ty, Rawness::Ref, *mutability)),
83 TyKind::Raw(mutability, ty) => Some((ty, Rawness::RawPtr, *mutability)),
84 _ => None,
85 }
86 }
87
88 fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId> {
89 match *self.kind(&Interner) {
90 TyKind::Adt(AdtId(adt), ..) => Some(adt.into()),
91 TyKind::FnDef(callable, ..) => {
92 Some(db.lookup_intern_callable_def(callable.into()).into())
93 }
94 TyKind::AssociatedType(type_alias, ..) => Some(from_assoc_type_id(type_alias).into()),
95 TyKind::Foreign(type_alias, ..) => Some(from_foreign_def_id(type_alias).into()),
96 _ => None,
97 }
98 }
99
100 fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId> {
101 match self.kind(&Interner) {
102 &TyKind::FnDef(def, ..) => Some(db.lookup_intern_callable_def(def.into())),
103 _ => None,
104 }
105 }
106
107 fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig> {
108 match self.kind(&Interner) {
109 TyKind::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)),
110 TyKind::FnDef(def, parameters) => {
111 let callable_def = db.lookup_intern_callable_def((*def).into());
112 let sig = db.callable_item_signature(callable_def);
113 Some(sig.substitute(&Interner, &parameters))
114 }
115 TyKind::Closure(.., substs) => {
116 let sig_param = substs.at(&Interner, 0).assert_ty_ref(&Interner);
117 sig_param.callable_sig(db)
118 }
119 _ => None,
120 }
121 }
122
123 fn dyn_trait(&self) -> Option<TraitId> {
124 let trait_ref = match self.kind(&Interner) {
125 TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().get(0).and_then(|b| {
126 match b.skip_binders() {
127 WhereClause::Implemented(trait_ref) => Some(trait_ref),
128 _ => None,
129 }
130 }),
131 _ => None,
132 }?;
133 Some(from_chalk_trait_id(trait_ref.trait_id))
134 }
135
136 fn strip_references(&self) -> &Ty {
137 let mut t: &Ty = self;
138 while let TyKind::Ref(_mutability, _lifetime, ty) = t.kind(&Interner) {
139 t = ty;
140 }
141 t
142 }
143
144 fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>> {
145 match self.kind(&Interner) {
146 TyKind::OpaqueType(opaque_ty_id, ..) => {
147 match db.lookup_intern_impl_trait_id((*opaque_ty_id).into()) {
148 ImplTraitId::AsyncBlockTypeImplTrait(def, _expr) => {
149 let krate = def.module(db.upcast()).krate();
150 if let Some(future_trait) = db
151 .lang_item(krate, "future_trait".into())
152 .and_then(|item| item.as_trait())
153 {
154 // This is only used by type walking.
155 // Parameters will be walked outside, and projection predicate is not used.
156 // So just provide the Future trait.
157 let impl_bound = Binders::empty(
158 &Interner,
159 WhereClause::Implemented(TraitRef {
160 trait_id: to_chalk_trait_id(future_trait),
161 substitution: Substitution::empty(&Interner),
162 }),
163 );
164 Some(vec![impl_bound])
165 } else {
166 None
167 }
168 }
169 ImplTraitId::ReturnTypeImplTrait(..) => None,
170 }
171 }
172 TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
173 let predicates = match db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into())
174 {
175 ImplTraitId::ReturnTypeImplTrait(func, idx) => {
176 db.return_type_impl_traits(func).map(|it| {
177 let data = (*it)
178 .as_ref()
179 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
180 data.substitute(&Interner, &opaque_ty.substitution)
181 })
182 }
183 // It always has an parameter for Future::Output type.
184 ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(),
185 };
186
187 predicates.map(|it| it.into_value_and_skipped_binders().0)
188 }
189 TyKind::Placeholder(idx) => {
190 let id = from_placeholder_idx(db, *idx);
191 let generic_params = db.generic_params(id.parent);
192 let param_data = &generic_params.types[id.local_id];
193 match param_data.provenance {
194 hir_def::generics::TypeParamProvenance::ArgumentImplTrait => {
195 let substs = TyBuilder::type_params_subst(db, id.parent);
196 let predicates = db
197 .generic_predicates(id.parent)
198 .into_iter()
199 .map(|pred| pred.clone().substitute(&Interner, &substs))
200 .filter(|wc| match &wc.skip_binders() {
201 WhereClause::Implemented(tr) => {
202 tr.self_type_parameter(&Interner) == self
203 }
204 WhereClause::AliasEq(AliasEq {
205 alias: AliasTy::Projection(proj),
206 ty: _,
207 }) => proj.self_type_parameter(&Interner) == self,
208 _ => false,
209 })
210 .collect::<Vec<_>>();
211
212 Some(predicates)
213 }
214 _ => None,
215 }
216 }
217 _ => None,
218 }
219 }
220
221 fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId> {
222 match self.kind(&Interner) {
223 TyKind::AssociatedType(id, ..) => {
224 match from_assoc_type_id(*id).lookup(db.upcast()).container {
225 AssocContainerId::TraitId(trait_id) => Some(trait_id),
226 _ => None,
227 }
228 }
229 TyKind::Alias(AliasTy::Projection(projection_ty)) => {
230 match from_assoc_type_id(projection_ty.associated_ty_id)
231 .lookup(db.upcast())
232 .container
233 {
234 AssocContainerId::TraitId(trait_id) => Some(trait_id),
235 _ => None,
236 }
237 }
238 _ => None,
239 }
240 }
18} 241}
19 242
20pub trait ProjectionTyExt { 243pub trait ProjectionTyExt {
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index d7bf9fdf7..79602c3dd 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -14,7 +14,6 @@ use crate::{
14 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, 14 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
15 MissingPatFields, RemoveThisSemicolon, 15 MissingPatFields, RemoveThisSemicolon,
16 }, 16 },
17 utils::variant_data,
18 AdtId, InferenceResult, Interner, TyExt, TyKind, 17 AdtId, InferenceResult, Interner, TyExt, TyKind,
19}; 18};
20 19
@@ -104,7 +103,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
104 let root = source_ptr.file_syntax(db.upcast()); 103 let root = source_ptr.file_syntax(db.upcast());
105 if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root) { 104 if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root) {
106 if let Some(_) = record_expr.record_expr_field_list() { 105 if let Some(_) = record_expr.record_expr_field_list() {
107 let variant_data = variant_data(db.upcast(), variant_def); 106 let variant_data = variant_def.variant_data(db.upcast());
108 let missed_fields = missed_fields 107 let missed_fields = missed_fields
109 .into_iter() 108 .into_iter()
110 .map(|idx| variant_data.fields()[idx].name.clone()) 109 .map(|idx| variant_data.fields()[idx].name.clone())
@@ -135,7 +134,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
135 let root = source_ptr.file_syntax(db.upcast()); 134 let root = source_ptr.file_syntax(db.upcast());
136 if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) { 135 if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
137 if let Some(_) = record_pat.record_pat_field_list() { 136 if let Some(_) = record_pat.record_pat_field_list() {
138 let variant_data = variant_data(db.upcast(), variant_def); 137 let variant_data = variant_def.variant_data(db.upcast());
139 let missed_fields = missed_fields 138 let missed_fields = missed_fields
140 .into_iter() 139 .into_iter()
141 .map(|idx| variant_data.fields()[idx].name.clone()) 140 .map(|idx| variant_data.fields()[idx].name.clone())
@@ -453,7 +452,7 @@ pub fn record_literal_missing_fields(
453 return None; 452 return None;
454 } 453 }
455 454
456 let variant_data = variant_data(db.upcast(), variant_def); 455 let variant_data = variant_def.variant_data(db.upcast());
457 456
458 let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect(); 457 let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
459 let missed_fields: Vec<LocalFieldId> = variant_data 458 let missed_fields: Vec<LocalFieldId> = variant_data
@@ -483,7 +482,7 @@ pub fn record_pattern_missing_fields(
483 return None; 482 return None;
484 } 483 }
485 484
486 let variant_data = variant_data(db.upcast(), variant_def); 485 let variant_data = variant_def.variant_data(db.upcast());
487 486
488 let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect(); 487 let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
489 let missed_fields: Vec<LocalFieldId> = variant_data 488 let missed_fields: Vec<LocalFieldId> = variant_data
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index 34291578a..e9762622f 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -227,7 +227,7 @@ use hir_def::{
227use la_arena::Idx; 227use la_arena::Idx;
228use smallvec::{smallvec, SmallVec}; 228use smallvec::{smallvec, SmallVec};
229 229
230use crate::{db::HirDatabase, AdtId, InferenceResult, Interner, TyKind}; 230use crate::{db::HirDatabase, AdtId, InferenceResult, Interner, TyExt, TyKind};
231 231
232#[derive(Debug, Clone, Copy)] 232#[derive(Debug, Clone, Copy)]
233/// Either a pattern from the source code being analyzed, represented as 233/// Either a pattern from the source code being analyzed, represented as
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs
index b5efe9df5..ed97dc0e3 100644
--- a/crates/hir_ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs
@@ -11,7 +11,9 @@ use hir_def::{
11}; 11};
12use hir_expand::diagnostics::DiagnosticSink; 12use hir_expand::diagnostics::DiagnosticSink;
13 13
14use crate::{db::HirDatabase, diagnostics::MissingUnsafe, InferenceResult, Interner, TyKind}; 14use crate::{
15 db::HirDatabase, diagnostics::MissingUnsafe, InferenceResult, Interner, TyExt, TyKind,
16};
15 17
16pub(super) struct UnsafeValidator<'a, 'b: 'a> { 18pub(super) struct UnsafeValidator<'a, 'b: 'a> {
17 owner: DefWithBodyId, 19 owner: DefWithBodyId,
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 1c3faf5cb..c63878e7a 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -42,7 +42,7 @@ use super::{
42}; 42};
43use crate::{ 43use crate::{
44 db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode, 44 db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode,
45 to_assoc_type_id, AliasEq, AliasTy, Interner, TyBuilder, TyKind, 45 to_assoc_type_id, AliasEq, AliasTy, Interner, TyBuilder, TyExt, TyKind,
46}; 46};
47 47
48// This lint has a false positive here. See the link below for details. 48// This lint has a false positive here. See the link below for details.
@@ -131,8 +131,6 @@ pub struct InferenceResult {
131 method_resolutions: FxHashMap<ExprId, FunctionId>, 131 method_resolutions: FxHashMap<ExprId, FunctionId>,
132 /// For each field access expr, records the field it resolves to. 132 /// For each field access expr, records the field it resolves to.
133 field_resolutions: FxHashMap<ExprId, FieldId>, 133 field_resolutions: FxHashMap<ExprId, FieldId>,
134 /// For each field in record literal, records the field it resolves to.
135 record_field_resolutions: FxHashMap<ExprId, FieldId>,
136 record_pat_field_resolutions: FxHashMap<PatId, FieldId>, 134 record_pat_field_resolutions: FxHashMap<PatId, FieldId>,
137 /// For each struct literal, records the variant it resolves to. 135 /// For each struct literal, records the variant it resolves to.
138 variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, 136 variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
@@ -153,9 +151,6 @@ impl InferenceResult {
153 pub fn field_resolution(&self, expr: ExprId) -> Option<FieldId> { 151 pub fn field_resolution(&self, expr: ExprId) -> Option<FieldId> {
154 self.field_resolutions.get(&expr).copied() 152 self.field_resolutions.get(&expr).copied()
155 } 153 }
156 pub fn record_field_resolution(&self, expr: ExprId) -> Option<FieldId> {
157 self.record_field_resolutions.get(&expr).copied()
158 }
159 pub fn record_pat_field_resolution(&self, pat: PatId) -> Option<FieldId> { 154 pub fn record_pat_field_resolution(&self, pat: PatId) -> Option<FieldId> {
160 self.record_pat_field_resolutions.get(&pat).copied() 155 self.record_pat_field_resolutions.get(&pat).copied()
161 } 156 }
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs
index d6c48870a..159a53a63 100644
--- a/crates/hir_ty/src/infer/coerce.rs
+++ b/crates/hir_ty/src/infer/coerce.rs
@@ -7,7 +7,7 @@
7use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; 7use chalk_ir::{cast::Cast, Mutability, TyVariableKind};
8use hir_def::lang_item::LangItemTarget; 8use hir_def::lang_item::LangItemTarget;
9 9
10use crate::{autoderef, Interner, Solution, Ty, TyBuilder, TyKind}; 10use crate::{autoderef, Interner, Solution, Ty, TyBuilder, TyExt, TyKind};
11 11
12use super::{InEnvironment, InferenceContext}; 12use super::{InEnvironment, InferenceContext};
13 13
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 53d94fd0d..9ab0fa212 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -21,9 +21,10 @@ use crate::{
21 primitive::{self, UintTy}, 21 primitive::{self, UintTy},
22 static_lifetime, to_chalk_trait_id, 22 static_lifetime, to_chalk_trait_id,
23 traits::{chalk::from_chalk, FnTrait}, 23 traits::{chalk::from_chalk, FnTrait},
24 utils::{generics, variant_data, Generics}, 24 utils::{generics, Generics},
25 AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, InEnvironment, Interner, 25 AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, InEnvironment, Interner,
26 ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeWalk, 26 ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
27 TypeWalk,
27}; 28};
28 29
29use super::{ 30use super::{
@@ -317,7 +318,13 @@ impl<'a> InferenceContext<'a> {
317 self.normalize_associated_types_in(ret_ty) 318 self.normalize_associated_types_in(ret_ty)
318 } 319 }
319 Expr::MethodCall { receiver, args, method_name, generic_args } => self 320 Expr::MethodCall { receiver, args, method_name, generic_args } => self
320 .infer_method_call(tgt_expr, *receiver, &args, &method_name, generic_args.as_ref()), 321 .infer_method_call(
322 tgt_expr,
323 *receiver,
324 &args,
325 &method_name,
326 generic_args.as_deref(),
327 ),
321 Expr::Match { expr, arms } => { 328 Expr::Match { expr, arms } => {
322 let input_ty = self.infer_expr(*expr, &Expectation::none()); 329 let input_ty = self.infer_expr(*expr, &Expectation::none());
323 330
@@ -398,7 +405,7 @@ impl<'a> InferenceContext<'a> {
398 TyKind::Never.intern(&Interner) 405 TyKind::Never.intern(&Interner)
399 } 406 }
400 Expr::RecordLit { path, fields, spread } => { 407 Expr::RecordLit { path, fields, spread } => {
401 let (ty, def_id) = self.resolve_variant(path.as_ref()); 408 let (ty, def_id) = self.resolve_variant(path.as_deref());
402 if let Some(variant) = def_id { 409 if let Some(variant) = def_id {
403 self.write_variant_resolution(tgt_expr.into(), variant); 410 self.write_variant_resolution(tgt_expr.into(), variant);
404 } 411 }
@@ -407,7 +414,7 @@ impl<'a> InferenceContext<'a> {
407 414
408 let substs = ty.substs().cloned().unwrap_or_else(|| Substitution::empty(&Interner)); 415 let substs = ty.substs().cloned().unwrap_or_else(|| Substitution::empty(&Interner));
409 let field_types = def_id.map(|it| self.db.field_types(it)).unwrap_or_default(); 416 let field_types = def_id.map(|it| self.db.field_types(it)).unwrap_or_default();
410 let variant_data = def_id.map(|it| variant_data(self.db.upcast(), it)); 417 let variant_data = def_id.map(|it| it.variant_data(self.db.upcast()));
411 for field in fields.iter() { 418 for field in fields.iter() {
412 let field_def = 419 let field_def =
413 variant_data.as_ref().and_then(|it| match it.field(&field.name) { 420 variant_data.as_ref().and_then(|it| match it.field(&field.name) {
@@ -419,9 +426,6 @@ impl<'a> InferenceContext<'a> {
419 None 426 None
420 } 427 }
421 }); 428 });
422 if let Some(field_def) = field_def {
423 self.result.record_field_resolutions.insert(field.expr, field_def);
424 }
425 let field_ty = field_def.map_or(self.err_ty(), |it| { 429 let field_ty = field_def.map_or(self.err_ty(), |it| {
426 field_types[it.local_id].clone().substitute(&Interner, &substs) 430 field_types[it.local_id].clone().substitute(&Interner, &substs)
427 }); 431 });
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index c1d7a6b76..942f70edf 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -13,8 +13,8 @@ use hir_expand::name::Name;
13 13
14use super::{BindingMode, Expectation, InferenceContext}; 14use super::{BindingMode, Expectation, InferenceContext};
15use crate::{ 15use crate::{
16 lower::lower_to_chalk_mutability, static_lifetime, utils::variant_data, Interner, Substitution, 16 lower::lower_to_chalk_mutability, static_lifetime, Interner, Substitution, Ty, TyBuilder,
17 Ty, TyBuilder, TyKind, 17 TyExt, TyKind,
18}; 18};
19 19
20impl<'a> InferenceContext<'a> { 20impl<'a> InferenceContext<'a> {
@@ -28,7 +28,7 @@ impl<'a> InferenceContext<'a> {
28 ellipsis: Option<usize>, 28 ellipsis: Option<usize>,
29 ) -> Ty { 29 ) -> Ty {
30 let (ty, def) = self.resolve_variant(path); 30 let (ty, def) = self.resolve_variant(path);
31 let var_data = def.map(|it| variant_data(self.db.upcast(), it)); 31 let var_data = def.map(|it| it.variant_data(self.db.upcast()));
32 if let Some(variant) = def { 32 if let Some(variant) = def {
33 self.write_variant_resolution(id.into(), variant); 33 self.write_variant_resolution(id.into(), variant);
34 } 34 }
@@ -68,7 +68,7 @@ impl<'a> InferenceContext<'a> {
68 id: PatId, 68 id: PatId,
69 ) -> Ty { 69 ) -> Ty {
70 let (ty, def) = self.resolve_variant(path); 70 let (ty, def) = self.resolve_variant(path);
71 let var_data = def.map(|it| variant_data(self.db.upcast(), it)); 71 let var_data = def.map(|it| it.variant_data(self.db.upcast()));
72 if let Some(variant) = def { 72 if let Some(variant) = def {
73 self.write_variant_resolution(id.into(), variant); 73 self.write_variant_resolution(id.into(), variant);
74 } 74 }
@@ -174,7 +174,7 @@ impl<'a> InferenceContext<'a> {
174 TyKind::Ref(mutability, static_lifetime(), subty).intern(&Interner) 174 TyKind::Ref(mutability, static_lifetime(), subty).intern(&Interner)
175 } 175 }
176 Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat( 176 Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat(
177 p.as_ref(), 177 p.as_deref(),
178 subpats, 178 subpats,
179 expected, 179 expected,
180 default_bm, 180 default_bm,
@@ -182,7 +182,7 @@ impl<'a> InferenceContext<'a> {
182 *ellipsis, 182 *ellipsis,
183 ), 183 ),
184 Pat::Record { path: p, args: fields, ellipsis: _ } => { 184 Pat::Record { path: p, args: fields, ellipsis: _ } => {
185 self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat) 185 self.infer_record_pat(p.as_deref(), fields, expected, default_bm, pat)
186 } 186 }
187 Pat::Path(path) => { 187 Pat::Path(path) => {
188 // FIXME use correct resolver for the surrounding expression 188 // FIXME use correct resolver for the surrounding expression
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index 14f705173..b19d67bb1 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -10,7 +10,9 @@ use hir_def::{
10}; 10};
11use hir_expand::name::Name; 11use hir_expand::name::Name;
12 12
13use crate::{method_resolution, Interner, Substitution, Ty, TyBuilder, TyKind, ValueTyDefId}; 13use crate::{
14 method_resolution, Interner, Substitution, Ty, TyBuilder, TyExt, TyKind, ValueTyDefId,
15};
14 16
15use super::{ExprOrPatId, InferenceContext, TraitRef}; 17use super::{ExprOrPatId, InferenceContext, TraitRef};
16 18
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index a2a5bcc07..f5b658cba 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -30,13 +30,11 @@ mod test_db;
30 30
31use std::sync::Arc; 31use std::sync::Arc;
32 32
33use chalk_ir::UintTy;
34use itertools::Itertools;
35
36use base_db::salsa; 33use base_db::salsa;
34use chalk_ir::UintTy;
37use hir_def::{ 35use hir_def::{
38 expr::ExprId, type_ref::Rawness, AssocContainerId, ConstParamId, FunctionId, GenericDefId, 36 expr::ExprId, type_ref::Rawness, ConstParamId, LifetimeParamId, TraitId, TypeAliasId,
39 HasModule, LifetimeParamId, Lookup, TraitId, TypeAliasId, TypeParamId, 37 TypeParamId,
40}; 38};
41 39
42use crate::{db::HirDatabase, display::HirDisplay, utils::generics}; 40use crate::{db::HirDatabase, display::HirDisplay, utils::generics};
@@ -171,65 +169,6 @@ impl CallableSig {
171} 169}
172 170
173impl Ty { 171impl Ty {
174 pub fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> {
175 match self.kind(&Interner) {
176 TyKind::Ref(mutability, lifetime, ty) => Some((ty, *lifetime, *mutability)),
177 _ => None,
178 }
179 }
180
181 pub fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> {
182 match self.kind(&Interner) {
183 TyKind::Ref(mutability, _, ty) => Some((ty, Rawness::Ref, *mutability)),
184 TyKind::Raw(mutability, ty) => Some((ty, Rawness::RawPtr, *mutability)),
185 _ => None,
186 }
187 }
188
189 pub fn strip_references(&self) -> &Ty {
190 let mut t: &Ty = self;
191
192 while let TyKind::Ref(_mutability, _lifetime, ty) = t.kind(&Interner) {
193 t = ty;
194 }
195
196 t
197 }
198
199 pub fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> {
200 match self.kind(&Interner) {
201 TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)),
202 _ => None,
203 }
204 }
205
206 pub fn as_tuple(&self) -> Option<&Substitution> {
207 match self.kind(&Interner) {
208 TyKind::Tuple(_, substs) => Some(substs),
209 _ => None,
210 }
211 }
212
213 pub fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId> {
214 match *self.kind(&Interner) {
215 TyKind::Adt(AdtId(adt), ..) => Some(adt.into()),
216 TyKind::FnDef(callable, ..) => {
217 Some(db.lookup_intern_callable_def(callable.into()).into())
218 }
219 TyKind::AssociatedType(type_alias, ..) => Some(from_assoc_type_id(type_alias).into()),
220 TyKind::Foreign(type_alias, ..) => Some(from_foreign_def_id(type_alias).into()),
221 _ => None,
222 }
223 }
224
225 pub fn is_never(&self) -> bool {
226 matches!(self.kind(&Interner), TyKind::Never)
227 }
228
229 pub fn is_unknown(&self) -> bool {
230 matches!(self.kind(&Interner), TyKind::Error)
231 }
232
233 pub fn equals_ctor(&self, other: &Ty) -> bool { 172 pub fn equals_ctor(&self, other: &Ty) -> bool {
234 match (self.kind(&Interner), other.kind(&Interner)) { 173 match (self.kind(&Interner), other.kind(&Interner)) {
235 (TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2, 174 (TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2,
@@ -260,24 +199,6 @@ impl Ty {
260 } 199 }
261 } 200 }
262 201
263 /// If this is a `dyn Trait` type, this returns the `Trait` part.
264 fn dyn_trait_ref(&self) -> Option<&TraitRef> {
265 match self.kind(&Interner) {
266 TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().get(0).and_then(|b| {
267 match b.skip_binders() {
268 WhereClause::Implemented(trait_ref) => Some(trait_ref),
269 _ => None,
270 }
271 }),
272 _ => None,
273 }
274 }
275
276 /// If this is a `dyn Trait`, returns that trait.
277 pub fn dyn_trait(&self) -> Option<TraitId> {
278 self.dyn_trait_ref().map(|it| it.trait_id).map(from_chalk_trait_id)
279 }
280
281 fn builtin_deref(&self) -> Option<Ty> { 202 fn builtin_deref(&self) -> Option<Ty> {
282 match self.kind(&Interner) { 203 match self.kind(&Interner) {
283 TyKind::Ref(.., ty) => Some(ty.clone()), 204 TyKind::Ref(.., ty) => Some(ty.clone()),
@@ -286,37 +207,6 @@ impl Ty {
286 } 207 }
287 } 208 }
288 209
289 pub fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId> {
290 match self.kind(&Interner) {
291 &TyKind::FnDef(def, ..) => Some(db.lookup_intern_callable_def(def.into())),
292 _ => None,
293 }
294 }
295
296 pub fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> {
297 if let Some(CallableDefId::FunctionId(func)) = self.callable_def(db) {
298 Some(func)
299 } else {
300 None
301 }
302 }
303
304 pub fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig> {
305 match self.kind(&Interner) {
306 TyKind::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)),
307 TyKind::FnDef(def, parameters) => {
308 let callable_def = db.lookup_intern_callable_def((*def).into());
309 let sig = db.callable_item_signature(callable_def);
310 Some(sig.substitute(&Interner, &parameters))
311 }
312 TyKind::Closure(.., substs) => {
313 let sig_param = substs.at(&Interner, 0).assert_ty_ref(&Interner);
314 sig_param.callable_sig(db)
315 }
316 _ => None,
317 }
318 }
319
320 /// Returns the type parameters of this type if it has some (i.e. is an ADT 210 /// Returns the type parameters of this type if it has some (i.e. is an ADT
321 /// or function); so if `self` is `Option<u32>`, this returns the `u32`. 211 /// or function); so if `self` is `Option<u32>`, this returns the `u32`.
322 pub fn substs(&self) -> Option<&Substitution> { 212 pub fn substs(&self) -> Option<&Substitution> {
@@ -344,104 +234,6 @@ impl Ty {
344 _ => None, 234 _ => None,
345 } 235 }
346 } 236 }
347
348 pub fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>> {
349 match self.kind(&Interner) {
350 TyKind::OpaqueType(opaque_ty_id, ..) => {
351 match db.lookup_intern_impl_trait_id((*opaque_ty_id).into()) {
352 ImplTraitId::AsyncBlockTypeImplTrait(def, _expr) => {
353 let krate = def.module(db.upcast()).krate();
354 if let Some(future_trait) = db
355 .lang_item(krate, "future_trait".into())
356 .and_then(|item| item.as_trait())
357 {
358 // This is only used by type walking.
359 // Parameters will be walked outside, and projection predicate is not used.
360 // So just provide the Future trait.
361 let impl_bound = Binders::empty(
362 &Interner,
363 WhereClause::Implemented(TraitRef {
364 trait_id: to_chalk_trait_id(future_trait),
365 substitution: Substitution::empty(&Interner),
366 }),
367 );
368 Some(vec![impl_bound])
369 } else {
370 None
371 }
372 }
373 ImplTraitId::ReturnTypeImplTrait(..) => None,
374 }
375 }
376 TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
377 let predicates = match db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into())
378 {
379 ImplTraitId::ReturnTypeImplTrait(func, idx) => {
380 db.return_type_impl_traits(func).map(|it| {
381 let data = (*it)
382 .as_ref()
383 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
384 data.substitute(&Interner, &opaque_ty.substitution)
385 })
386 }
387 // It always has an parameter for Future::Output type.
388 ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(),
389 };
390
391 predicates.map(|it| it.into_value_and_skipped_binders().0)
392 }
393 TyKind::Placeholder(idx) => {
394 let id = from_placeholder_idx(db, *idx);
395 let generic_params = db.generic_params(id.parent);
396 let param_data = &generic_params.types[id.local_id];
397 match param_data.provenance {
398 hir_def::generics::TypeParamProvenance::ArgumentImplTrait => {
399 let substs = TyBuilder::type_params_subst(db, id.parent);
400 let predicates = db
401 .generic_predicates(id.parent)
402 .into_iter()
403 .map(|pred| pred.clone().substitute(&Interner, &substs))
404 .filter(|wc| match &wc.skip_binders() {
405 WhereClause::Implemented(tr) => {
406 tr.self_type_parameter(&Interner) == self
407 }
408 WhereClause::AliasEq(AliasEq {
409 alias: AliasTy::Projection(proj),
410 ty: _,
411 }) => proj.self_type_parameter(&Interner) == self,
412 _ => false,
413 })
414 .collect_vec();
415
416 Some(predicates)
417 }
418 _ => None,
419 }
420 }
421 _ => None,
422 }
423 }
424
425 pub fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId> {
426 match self.kind(&Interner) {
427 TyKind::AssociatedType(id, ..) => {
428 match from_assoc_type_id(*id).lookup(db.upcast()).container {
429 AssocContainerId::TraitId(trait_id) => Some(trait_id),
430 _ => None,
431 }
432 }
433 TyKind::Alias(AliasTy::Projection(projection_ty)) => {
434 match from_assoc_type_id(projection_ty.associated_ty_id)
435 .lookup(db.upcast())
436 .container
437 {
438 AssocContainerId::TraitId(trait_id) => Some(trait_id),
439 _ => None,
440 }
441 }
442 _ => None,
443 }
444 }
445} 237}
446 238
447#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] 239#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 8be1bcddb..4ca6aa538 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -30,8 +30,7 @@ use crate::{
30 dummy_usize_const, static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, 30 dummy_usize_const, static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
31 traits::chalk::{Interner, ToChalk}, 31 traits::chalk::{Interner, ToChalk},
32 utils::{ 32 utils::{
33 all_super_trait_refs, associated_type_by_name_including_super_traits, generics, 33 all_super_trait_refs, associated_type_by_name_including_super_traits, generics, Generics,
34 variant_data, Generics,
35 }, 34 },
36 AliasEq, AliasTy, Binders, BoundVar, CallableSig, DebruijnIndex, DynTy, FnPointer, FnSig, 35 AliasEq, AliasTy, Binders, BoundVar, CallableSig, DebruijnIndex, DynTy, FnPointer, FnSig,
37 FnSubst, ImplTraitId, OpaqueTy, PolyFnSig, ProjectionTy, QuantifiedWhereClause, 36 FnSubst, ImplTraitId, OpaqueTy, PolyFnSig, ProjectionTy, QuantifiedWhereClause,
@@ -879,7 +878,7 @@ pub(crate) fn field_types_query(
879 db: &dyn HirDatabase, 878 db: &dyn HirDatabase,
880 variant_id: VariantId, 879 variant_id: VariantId,
881) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>> { 880) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>> {
882 let var_data = variant_data(db.upcast(), variant_id); 881 let var_data = variant_id.variant_data(db.upcast());
883 let (resolver, def): (_, GenericDefId) = match variant_id { 882 let (resolver, def): (_, GenericDefId) = match variant_id {
884 VariantId::StructId(it) => (it.resolver(db.upcast()), it.into()), 883 VariantId::StructId(it) => (it.resolver(db.upcast()), it.into()),
885 VariantId::UnionId(it) => (it.resolver(db.upcast()), it.into()), 884 VariantId::UnionId(it) => (it.resolver(db.upcast()), it.into()),
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index 5042bfbca..ee725fd46 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -22,7 +22,7 @@ use crate::{
22 static_lifetime, 22 static_lifetime,
23 utils::all_super_traits, 23 utils::all_super_traits,
24 AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, FnPointer, FnSig, ForeignDefId, 24 AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, FnPointer, FnSig, ForeignDefId,
25 InEnvironment, Interner, Scalar, Substitution, TraitEnvironment, Ty, TyBuilder, TyKind, 25 InEnvironment, Interner, Scalar, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
26 TypeWalk, 26 TypeWalk,
27}; 27};
28 28
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs
index 5a8b5cd86..f03b92422 100644
--- a/crates/hir_ty/src/traits/chalk.rs
+++ b/crates/hir_ty/src/traits/chalk.rs
@@ -22,7 +22,7 @@ use crate::{
22 to_assoc_type_id, to_chalk_trait_id, 22 to_assoc_type_id, to_chalk_trait_id,
23 utils::generics, 23 utils::generics,
24 AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId, ProjectionTy, Substitution, 24 AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId, ProjectionTy, Substitution,
25 TraitRef, Ty, TyBuilder, TyKind, WhereClause, 25 TraitRef, Ty, TyBuilder, TyExt, TyKind, WhereClause,
26}; 26};
27use mapping::{ 27use mapping::{
28 convert_where_clauses, generic_predicate_to_inline_bound, make_binders, TypeAliasAsValue, 28 convert_where_clauses, generic_predicate_to_inline_bound, make_binders, TypeAliasAsValue,
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs
index d11708299..0a424f607 100644
--- a/crates/hir_ty/src/utils.rs
+++ b/crates/hir_ty/src/utils.rs
@@ -4,7 +4,6 @@ use std::sync::Arc;
4 4
5use chalk_ir::{BoundVar, DebruijnIndex}; 5use chalk_ir::{BoundVar, DebruijnIndex};
6use hir_def::{ 6use hir_def::{
7 adt::VariantData,
8 db::DefDatabase, 7 db::DefDatabase,
9 generics::{ 8 generics::{
10 GenericParams, TypeParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, 9 GenericParams, TypeParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
@@ -13,7 +12,7 @@ use hir_def::{
13 path::Path, 12 path::Path,
14 resolver::{HasResolver, TypeNs}, 13 resolver::{HasResolver, TypeNs},
15 type_ref::TypeRef, 14 type_ref::TypeRef,
16 AssocContainerId, GenericDefId, Lookup, TraitId, TypeAliasId, TypeParamId, VariantId, 15 AssocContainerId, GenericDefId, Lookup, TraitId, TypeAliasId, TypeParamId,
17}; 16};
18use hir_expand::name::{name, Name}; 17use hir_expand::name::{name, Name};
19 18
@@ -136,16 +135,6 @@ pub(super) fn associated_type_by_name_including_super_traits(
136 }) 135 })
137} 136}
138 137
139pub(super) fn variant_data(db: &dyn DefDatabase, var: VariantId) -> Arc<VariantData> {
140 match var {
141 VariantId::StructId(it) => db.struct_data(it).variant_data.clone(),
142 VariantId::UnionId(it) => db.union_data(it).variant_data.clone(),
143 VariantId::EnumVariantId(it) => {
144 db.enum_data(it.parent).variants[it.local_id].variant_data.clone()
145 }
146 }
147}
148
149/// Helper for mutating `Arc<[T]>` (i.e. `Arc::make_mut` for Arc slices). 138/// Helper for mutating `Arc<[T]>` (i.e. `Arc::make_mut` for Arc slices).
150/// The underlying values are cloned if there are other strong references. 139/// The underlying values are cloned if there are other strong references.
151pub(crate) fn make_mut_slice<T: Clone>(a: &mut Arc<[T]>) -> &mut [T] { 140pub(crate) fn make_mut_slice<T: Clone>(a: &mut Arc<[T]>) -> &mut [T] {
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs
index 22011cb33..f09ad37e3 100644
--- a/crates/paths/src/lib.rs
+++ b/crates/paths/src/lib.rs
@@ -1,6 +1,7 @@
1//! Thin wrappers around `std::path`, distinguishing between absolute and 1//! Thin wrappers around `std::path`, distinguishing between absolute and
2//! relative paths. 2//! relative paths.
3use std::{ 3use std::{
4 borrow::Borrow,
4 convert::{TryFrom, TryInto}, 5 convert::{TryFrom, TryInto},
5 ops, 6 ops,
6 path::{Component, Path, PathBuf}, 7 path::{Component, Path, PathBuf},
@@ -35,6 +36,12 @@ impl AsRef<AbsPath> for AbsPathBuf {
35 } 36 }
36} 37}
37 38
39impl Borrow<AbsPath> for AbsPathBuf {
40 fn borrow(&self) -> &AbsPath {
41 self.as_path()
42 }
43}
44
38impl TryFrom<PathBuf> for AbsPathBuf { 45impl TryFrom<PathBuf> for AbsPathBuf {
39 type Error = PathBuf; 46 type Error = PathBuf;
40 fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> { 47 fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> {
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs
index f7050be4e..0d4d39fef 100644
--- a/crates/project_model/src/build_data.rs
+++ b/crates/project_model/src/build_data.rs
@@ -13,12 +13,12 @@ use cargo_metadata::{BuildScript, Message};
13use itertools::Itertools; 13use itertools::Itertools;
14use paths::{AbsPath, AbsPathBuf}; 14use paths::{AbsPath, AbsPathBuf};
15use rustc_hash::FxHashMap; 15use rustc_hash::FxHashMap;
16use stdx::JodChild; 16use stdx::{format_to, JodChild};
17 17
18use crate::{cfg_flag::CfgFlag, CargoConfig}; 18use crate::{cfg_flag::CfgFlag, CargoConfig};
19 19
20#[derive(Debug, Clone, Default, PartialEq, Eq)] 20#[derive(Debug, Clone, Default, PartialEq, Eq)]
21pub(crate) struct BuildData { 21pub(crate) struct PackageBuildData {
22 /// List of config flags defined by this package's build script 22 /// List of config flags defined by this package's build script
23 pub(crate) cfgs: Vec<CfgFlag>, 23 pub(crate) cfgs: Vec<CfgFlag>,
24 /// List of cargo-related environment variables with their value 24 /// List of cargo-related environment variables with their value
@@ -32,6 +32,17 @@ pub(crate) struct BuildData {
32 pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>, 32 pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
33} 33}
34 34
35#[derive(Debug, Default, PartialEq, Eq, Clone)]
36pub(crate) struct WorkspaceBuildData {
37 per_package: FxHashMap<String, PackageBuildData>,
38 error: Option<String>,
39}
40
41#[derive(Debug, Default, PartialEq, Eq, Clone)]
42pub struct BuildDataResult {
43 per_workspace: FxHashMap<AbsPathBuf, WorkspaceBuildData>,
44}
45
35#[derive(Clone, Debug)] 46#[derive(Clone, Debug)]
36pub(crate) struct BuildDataConfig { 47pub(crate) struct BuildDataConfig {
37 cargo_toml: AbsPathBuf, 48 cargo_toml: AbsPathBuf,
@@ -52,13 +63,6 @@ pub struct BuildDataCollector {
52 configs: FxHashMap<AbsPathBuf, BuildDataConfig>, 63 configs: FxHashMap<AbsPathBuf, BuildDataConfig>,
53} 64}
54 65
55#[derive(Debug, Default, PartialEq, Eq)]
56pub struct BuildDataResult {
57 data: FxHashMap<AbsPathBuf, BuildDataMap>,
58}
59
60pub(crate) type BuildDataMap = FxHashMap<String, BuildData>;
61
62impl BuildDataCollector { 66impl BuildDataCollector {
63 pub(crate) fn add_config(&mut self, workspace_root: &AbsPath, config: BuildDataConfig) { 67 pub(crate) fn add_config(&mut self, workspace_root: &AbsPath, config: BuildDataConfig) {
64 self.configs.insert(workspace_root.to_path_buf(), config); 68 self.configs.insert(workspace_root.to_path_buf(), config);
@@ -67,7 +71,7 @@ impl BuildDataCollector {
67 pub fn collect(&mut self, progress: &dyn Fn(String)) -> Result<BuildDataResult> { 71 pub fn collect(&mut self, progress: &dyn Fn(String)) -> Result<BuildDataResult> {
68 let mut res = BuildDataResult::default(); 72 let mut res = BuildDataResult::default();
69 for (path, config) in self.configs.iter() { 73 for (path, config) in self.configs.iter() {
70 res.data.insert( 74 res.per_workspace.insert(
71 path.clone(), 75 path.clone(),
72 collect_from_workspace( 76 collect_from_workspace(
73 &config.cargo_toml, 77 &config.cargo_toml,
@@ -81,9 +85,28 @@ impl BuildDataCollector {
81 } 85 }
82} 86}
83 87
88impl WorkspaceBuildData {
89 pub(crate) fn get(&self, package_id: &str) -> Option<&PackageBuildData> {
90 self.per_package.get(package_id)
91 }
92}
93
84impl BuildDataResult { 94impl BuildDataResult {
85 pub(crate) fn get(&self, root: &AbsPath) -> Option<&BuildDataMap> { 95 pub(crate) fn get(&self, workspace_root: &AbsPath) -> Option<&WorkspaceBuildData> {
86 self.data.get(&root.to_path_buf()) 96 self.per_workspace.get(workspace_root)
97 }
98 pub fn error(&self) -> Option<String> {
99 let mut buf = String::new();
100 for (_workspace_root, build_data) in &self.per_workspace {
101 if let Some(err) = &build_data.error {
102 format_to!(buf, "cargo check failed:\n{}", err);
103 }
104 }
105 if buf.is_empty() {
106 return None;
107 }
108
109 Some(buf)
87 } 110 }
88} 111}
89 112
@@ -102,7 +125,7 @@ fn collect_from_workspace(
102 cargo_features: &CargoConfig, 125 cargo_features: &CargoConfig,
103 packages: &Vec<cargo_metadata::Package>, 126 packages: &Vec<cargo_metadata::Package>,
104 progress: &dyn Fn(String), 127 progress: &dyn Fn(String),
105) -> Result<BuildDataMap> { 128) -> Result<WorkspaceBuildData> {
106 let mut cmd = Command::new(toolchain::cargo()); 129 let mut cmd = Command::new(toolchain::cargo());
107 cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"]) 130 cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"])
108 .arg(cargo_toml.as_ref()); 131 .arg(cargo_toml.as_ref());
@@ -130,13 +153,13 @@ fn collect_from_workspace(
130 } 153 }
131 } 154 }
132 155
133 cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()); 156 cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
134 157
135 let mut child = cmd.spawn().map(JodChild)?; 158 let mut child = cmd.spawn().map(JodChild)?;
136 let child_stdout = child.stdout.take().unwrap(); 159 let child_stdout = child.stdout.take().unwrap();
137 let stdout = BufReader::new(child_stdout); 160 let stdout = BufReader::new(child_stdout);
138 161
139 let mut res = BuildDataMap::default(); 162 let mut res = WorkspaceBuildData::default();
140 for message in cargo_metadata::Message::parse_stream(stdout).flatten() { 163 for message in cargo_metadata::Message::parse_stream(stdout).flatten() {
141 match message { 164 match message {
142 Message::BuildScriptExecuted(BuildScript { 165 Message::BuildScriptExecuted(BuildScript {
@@ -154,16 +177,17 @@ fn collect_from_workspace(
154 } 177 }
155 acc 178 acc
156 }; 179 };
157 let res = res.entry(package_id.repr.clone()).or_default(); 180 let package_build_data =
181 res.per_package.entry(package_id.repr.clone()).or_default();
158 // cargo_metadata crate returns default (empty) path for 182 // cargo_metadata crate returns default (empty) path for
159 // older cargos, which is not absolute, so work around that. 183 // older cargos, which is not absolute, so work around that.
160 if !out_dir.as_str().is_empty() { 184 if !out_dir.as_str().is_empty() {
161 let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string())); 185 let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string()));
162 res.out_dir = Some(out_dir); 186 package_build_data.out_dir = Some(out_dir);
163 res.cfgs = cfgs; 187 package_build_data.cfgs = cfgs;
164 } 188 }
165 189
166 res.envs = env; 190 package_build_data.envs = env;
167 } 191 }
168 Message::CompilerArtifact(message) => { 192 Message::CompilerArtifact(message) => {
169 progress(format!("metadata {}", message.target.name)); 193 progress(format!("metadata {}", message.target.name));
@@ -173,8 +197,9 @@ fn collect_from_workspace(
173 // Skip rmeta file 197 // Skip rmeta file
174 if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) { 198 if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) {
175 let filename = AbsPathBuf::assert(PathBuf::from(&filename)); 199 let filename = AbsPathBuf::assert(PathBuf::from(&filename));
176 let res = res.entry(package_id.repr.clone()).or_default(); 200 let package_build_data =
177 res.proc_macro_dylib_path = Some(filename); 201 res.per_package.entry(package_id.repr.clone()).or_default();
202 package_build_data.proc_macro_dylib_path = Some(filename);
178 } 203 }
179 } 204 }
180 } 205 }
@@ -188,16 +213,25 @@ fn collect_from_workspace(
188 } 213 }
189 214
190 for package in packages { 215 for package in packages {
191 let build_data = res.entry(package.id.repr.clone()).or_default(); 216 let package_build_data = res.per_package.entry(package.id.repr.clone()).or_default();
192 inject_cargo_env(package, build_data); 217 inject_cargo_env(package, package_build_data);
193 if let Some(out_dir) = &build_data.out_dir { 218 if let Some(out_dir) = &package_build_data.out_dir {
194 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() 219 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
195 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { 220 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
196 build_data.envs.push(("OUT_DIR".to_string(), out_dir)); 221 package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
197 } 222 }
198 } 223 }
199 } 224 }
200 225
226 let output = child.into_inner().wait_with_output()?;
227 if !output.status.success() {
228 let mut stderr = String::from_utf8(output.stderr).unwrap_or_default();
229 if stderr.is_empty() {
230 stderr = "cargo check failed".to_string();
231 }
232 res.error = Some(stderr)
233 }
234
201 Ok(res) 235 Ok(res)
202} 236}
203 237
@@ -212,7 +246,7 @@ fn is_dylib(path: &Utf8Path) -> bool {
212/// Recreates the compile-time environment variables that Cargo sets. 246/// Recreates the compile-time environment variables that Cargo sets.
213/// 247///
214/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates> 248/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
215fn inject_cargo_env(package: &cargo_metadata::Package, build_data: &mut BuildData) { 249fn inject_cargo_env(package: &cargo_metadata::Package, build_data: &mut PackageBuildData) {
216 let env = &mut build_data.envs; 250 let env = &mut build_data.envs;
217 251
218 // FIXME: Missing variables: 252 // FIXME: Missing variables:
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 1b53fcc30..2fcd0f8fa 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -12,7 +12,7 @@ use proc_macro_api::ProcMacroClient;
12use rustc_hash::{FxHashMap, FxHashSet}; 12use rustc_hash::{FxHashMap, FxHashSet};
13 13
14use crate::{ 14use crate::{
15 build_data::{BuildData, BuildDataMap, BuildDataResult}, 15 build_data::{BuildDataResult, PackageBuildData, WorkspaceBuildData},
16 cargo_workspace, 16 cargo_workspace,
17 cfg_flag::CfgFlag, 17 cfg_flag::CfgFlag,
18 rustc_cfg, 18 rustc_cfg,
@@ -354,10 +354,10 @@ fn cargo_to_crate_graph(
354 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 354 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
355 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 355 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
356 cargo: &CargoWorkspace, 356 cargo: &CargoWorkspace,
357 build_data_map: Option<&BuildDataMap>, 357 build_data_map: Option<&WorkspaceBuildData>,
358 sysroot: &Sysroot, 358 sysroot: &Sysroot,
359 rustc: &Option<CargoWorkspace>, 359 rustc: &Option<CargoWorkspace>,
360 rustc_build_data_map: Option<&BuildDataMap>, 360 rustc_build_data_map: Option<&WorkspaceBuildData>,
361) -> CrateGraph { 361) -> CrateGraph {
362 let _p = profile::span("cargo_to_crate_graph"); 362 let _p = profile::span("cargo_to_crate_graph");
363 let mut crate_graph = CrateGraph::default(); 363 let mut crate_graph = CrateGraph::default();
@@ -464,7 +464,7 @@ fn handle_rustc_crates(
464 rustc_workspace: &CargoWorkspace, 464 rustc_workspace: &CargoWorkspace,
465 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 465 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
466 crate_graph: &mut CrateGraph, 466 crate_graph: &mut CrateGraph,
467 rustc_build_data_map: Option<&FxHashMap<String, BuildData>>, 467 rustc_build_data_map: Option<&WorkspaceBuildData>,
468 cfg_options: &CfgOptions, 468 cfg_options: &CfgOptions,
469 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 469 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
470 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>, 470 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
@@ -555,7 +555,7 @@ fn handle_rustc_crates(
555fn add_target_crate_root( 555fn add_target_crate_root(
556 crate_graph: &mut CrateGraph, 556 crate_graph: &mut CrateGraph,
557 pkg: &cargo_workspace::PackageData, 557 pkg: &cargo_workspace::PackageData,
558 build_data: Option<&BuildData>, 558 build_data: Option<&PackageBuildData>,
559 cfg_options: &CfgOptions, 559 cfg_options: &CfgOptions,
560 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 560 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
561 file_id: FileId, 561 file_id: FileId,
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 18fd7ea74..fe9f273b0 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -12,7 +12,7 @@ use hir::{
12 AssocItem, Crate, Function, HasSource, HirDisplay, ModuleDef, 12 AssocItem, Crate, Function, HasSource, HirDisplay, ModuleDef,
13}; 13};
14use hir_def::FunctionId; 14use hir_def::FunctionId;
15use hir_ty::TypeWalk; 15use hir_ty::{TyExt, TypeWalk};
16use ide::{AnalysisHost, RootDatabase}; 16use ide::{AnalysisHost, RootDatabase};
17use ide_db::base_db::{ 17use ide_db::base_db::{
18 salsa::{self, ParallelDatabase}, 18 salsa::{self, ParallelDatabase},
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index cda272fd4..e012b4452 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -445,8 +445,8 @@ impl Config {
445 pub fn hover_actions(&self) -> bool { 445 pub fn hover_actions(&self) -> bool {
446 self.experimental("hoverActions") 446 self.experimental("hoverActions")
447 } 447 }
448 pub fn status_notification(&self) -> bool { 448 pub fn server_status_notification(&self) -> bool {
449 self.experimental("statusNotification") 449 self.experimental("serverStatusNotification")
450 } 450 }
451 451
452 pub fn publish_diagnostics(&self) -> bool { 452 pub fn publish_diagnostics(&self) -> bool {
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 8679c8599..adeb7a97e 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -23,6 +23,7 @@ use crate::{
23 document::DocumentData, 23 document::DocumentData,
24 from_proto, 24 from_proto,
25 line_index::{LineEndings, LineIndex}, 25 line_index::{LineEndings, LineIndex},
26 lsp_ext,
26 main_loop::Task, 27 main_loop::Task,
27 op_queue::OpQueue, 28 op_queue::OpQueue,
28 reload::SourceRootConfig, 29 reload::SourceRootConfig,
@@ -32,20 +33,6 @@ use crate::{
32 Result, 33 Result,
33}; 34};
34 35
35#[derive(Eq, PartialEq, Copy, Clone)]
36pub(crate) enum Status {
37 Loading,
38 Ready { partial: bool },
39 Invalid,
40 NeedsReload,
41}
42
43impl Default for Status {
44 fn default() -> Self {
45 Status::Loading
46 }
47}
48
49// Enforces drop order 36// Enforces drop order
50pub(crate) struct Handle<H, C> { 37pub(crate) struct Handle<H, C> {
51 pub(crate) handle: H, 38 pub(crate) handle: H,
@@ -67,26 +54,36 @@ pub(crate) struct GlobalState {
67 req_queue: ReqQueue, 54 req_queue: ReqQueue,
68 pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>, 55 pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
69 pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>, 56 pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
70 pub(crate) vfs_config_version: u32,
71 pub(crate) flycheck: Vec<FlycheckHandle>,
72 pub(crate) flycheck_sender: Sender<flycheck::Message>,
73 pub(crate) flycheck_receiver: Receiver<flycheck::Message>,
74 pub(crate) config: Arc<Config>, 57 pub(crate) config: Arc<Config>,
75 pub(crate) analysis_host: AnalysisHost, 58 pub(crate) analysis_host: AnalysisHost,
76 pub(crate) diagnostics: DiagnosticCollection, 59 pub(crate) diagnostics: DiagnosticCollection,
77 pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>, 60 pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>,
78 pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>, 61 pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
79 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
80 pub(crate) shutdown_requested: bool, 62 pub(crate) shutdown_requested: bool,
81 pub(crate) status: Status, 63 pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
82 pub(crate) source_root_config: SourceRootConfig, 64 pub(crate) source_root_config: SourceRootConfig,
83 pub(crate) proc_macro_client: Option<ProcMacroClient>, 65 pub(crate) proc_macro_client: Option<ProcMacroClient>,
84 66
85 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, 67 pub(crate) flycheck: Vec<FlycheckHandle>,
86 pub(crate) fetch_workspaces_queue: OpQueue<(), ()>, 68 pub(crate) flycheck_sender: Sender<flycheck::Message>,
69 pub(crate) flycheck_receiver: Receiver<flycheck::Message>,
87 70
71 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
72 pub(crate) vfs_config_version: u32,
73 pub(crate) vfs_progress_config_version: u32,
74 pub(crate) vfs_progress_n_total: usize,
75 pub(crate) vfs_progress_n_done: usize,
76
77 /// For both `workspaces` and `workspace_build_data`, the field stores the
78 /// data we actually use, while the `OpQueue` stores the result of the last
79 /// fetch.
80 ///
81 /// If the fetch (partially) fails, we do not update the values.
82 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
83 pub(crate) fetch_workspaces_queue: OpQueue<(), Vec<anyhow::Result<ProjectWorkspace>>>,
88 pub(crate) workspace_build_data: Option<BuildDataResult>, 84 pub(crate) workspace_build_data: Option<BuildDataResult>,
89 pub(crate) fetch_build_data_queue: OpQueue<BuildDataCollector, ()>, 85 pub(crate) fetch_build_data_queue:
86 OpQueue<BuildDataCollector, Option<anyhow::Result<BuildDataResult>>>,
90 87
91 latest_requests: Arc<RwLock<LatestRequests>>, 88 latest_requests: Arc<RwLock<LatestRequests>>,
92} 89}
@@ -124,25 +121,32 @@ impl GlobalState {
124 GlobalState { 121 GlobalState {
125 sender, 122 sender,
126 req_queue: ReqQueue::default(), 123 req_queue: ReqQueue::default(),
127 vfs_config_version: 0,
128 task_pool, 124 task_pool,
129 loader, 125 loader,
130 flycheck: Vec::new(),
131 flycheck_sender,
132 flycheck_receiver,
133 config: Arc::new(config), 126 config: Arc::new(config),
134 analysis_host, 127 analysis_host,
135 diagnostics: Default::default(), 128 diagnostics: Default::default(),
136 mem_docs: FxHashMap::default(), 129 mem_docs: FxHashMap::default(),
137 semantic_tokens_cache: Arc::new(Default::default()), 130 semantic_tokens_cache: Arc::new(Default::default()),
138 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
139 shutdown_requested: false, 131 shutdown_requested: false,
140 status: Status::default(), 132 last_reported_status: None,
141 source_root_config: SourceRootConfig::default(), 133 source_root_config: SourceRootConfig::default(),
142 proc_macro_client: None, 134 proc_macro_client: None,
135
136 flycheck: Vec::new(),
137 flycheck_sender,
138 flycheck_receiver,
139
140 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
141 vfs_config_version: 0,
142 vfs_progress_config_version: 0,
143 vfs_progress_n_total: 0,
144 vfs_progress_n_done: 0,
145
143 workspaces: Arc::new(Vec::new()), 146 workspaces: Arc::new(Vec::new()),
144 fetch_workspaces_queue: OpQueue::default(), 147 fetch_workspaces_queue: OpQueue::default(),
145 workspace_build_data: None, 148 workspace_build_data: None,
149
146 fetch_build_data_queue: OpQueue::default(), 150 fetch_build_data_queue: OpQueue::default(),
147 latest_requests: Default::default(), 151 latest_requests: Default::default(),
148 } 152 }
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 0e1fec209..81a6f22f1 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -241,26 +241,26 @@ pub struct SsrParams {
241 pub selections: Vec<lsp_types::Range>, 241 pub selections: Vec<lsp_types::Range>,
242} 242}
243 243
244pub enum StatusNotification {} 244pub enum ServerStatusNotification {}
245 245
246#[derive(Serialize, Deserialize)] 246impl Notification for ServerStatusNotification {
247#[serde(rename_all = "camelCase")] 247 type Params = ServerStatusParams;
248pub enum Status { 248 const METHOD: &'static str = "experimental/serverStatus";
249 Loading,
250 ReadyPartial,
251 Ready,
252 NeedsReload,
253 Invalid,
254} 249}
255 250
256#[derive(Deserialize, Serialize)] 251#[derive(Deserialize, Serialize, PartialEq, Eq, Clone)]
257pub struct StatusParams { 252pub struct ServerStatusParams {
258 pub status: Status, 253 pub health: Health,
254 pub quiescent: bool,
255 pub message: Option<String>,
259} 256}
260 257
261impl Notification for StatusNotification { 258#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
262 type Params = StatusParams; 259#[serde(rename_all = "camelCase")]
263 const METHOD: &'static str = "rust-analyzer/status"; 260pub enum Health {
261 Ok,
262 Warning,
263 Error,
264} 264}
265 265
266pub enum CodeActionRequest {} 266pub enum CodeActionRequest {}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index e88f16cc1..a5655116b 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -2,6 +2,7 @@
2//! requests/replies and notifications back to the client. 2//! requests/replies and notifications back to the client.
3use std::{ 3use std::{
4 env, fmt, 4 env, fmt,
5 sync::Arc,
5 time::{Duration, Instant}, 6 time::{Duration, Instant},
6}; 7};
7 8
@@ -12,6 +13,7 @@ use ide::{Canceled, FileId};
12use ide_db::base_db::VfsPath; 13use ide_db::base_db::VfsPath;
13use lsp_server::{Connection, Notification, Request, Response}; 14use lsp_server::{Connection, Notification, Request, Response};
14use lsp_types::notification::Notification as _; 15use lsp_types::notification::Notification as _;
16use project_model::BuildDataCollector;
15use vfs::ChangeKind; 17use vfs::ChangeKind;
16 18
17use crate::{ 19use crate::{
@@ -19,7 +21,7 @@ use crate::{
19 dispatch::{NotificationDispatcher, RequestDispatcher}, 21 dispatch::{NotificationDispatcher, RequestDispatcher},
20 document::DocumentData, 22 document::DocumentData,
21 from_proto, 23 from_proto,
22 global_state::{file_id_to_url, url_to_file_id, GlobalState, Status}, 24 global_state::{file_id_to_url, url_to_file_id, GlobalState},
23 handlers, lsp_ext, 25 handlers, lsp_ext,
24 lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress}, 26 lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress},
25 reload::{BuildDataProgress, ProjectWorkspaceProgress}, 27 reload::{BuildDataProgress, ProjectWorkspaceProgress},
@@ -187,7 +189,7 @@ impl GlobalState {
187 log::info!("task queue len: {}", task_queue_len); 189 log::info!("task queue len: {}", task_queue_len);
188 } 190 }
189 191
190 let mut new_status = self.status; 192 let was_quiescent = self.is_quiescent();
191 match event { 193 match event {
192 Event::Lsp(msg) => match msg { 194 Event::Lsp(msg) => match msg {
193 lsp_server::Message::Request(req) => self.on_request(loop_start, req)?, 195 lsp_server::Message::Request(req) => self.on_request(loop_start, req)?,
@@ -227,11 +229,24 @@ impl GlobalState {
227 (Progress::Report, Some(msg)) 229 (Progress::Report, Some(msg))
228 } 230 }
229 ProjectWorkspaceProgress::End(workspaces) => { 231 ProjectWorkspaceProgress::End(workspaces) => {
230 self.fetch_workspaces_completed(); 232 self.fetch_workspaces_completed(workspaces);
231 self.switch_workspaces(workspaces, None); 233
234 let old = Arc::clone(&self.workspaces);
235 self.switch_workspaces();
236 let workspaces_updated = !Arc::ptr_eq(&old, &self.workspaces);
237
238 if self.config.run_build_scripts() && workspaces_updated {
239 let mut collector = BuildDataCollector::default();
240 for ws in self.workspaces.iter() {
241 ws.collect_build_data_configs(&mut collector);
242 }
243 self.fetch_build_data_request(collector)
244 }
245
232 (Progress::End, None) 246 (Progress::End, None)
233 } 247 }
234 }; 248 };
249
235 self.report_progress("fetching", state, msg, None); 250 self.report_progress("fetching", state, msg, None);
236 } 251 }
237 Task::FetchBuildData(progress) => { 252 Task::FetchBuildData(progress) => {
@@ -240,19 +255,21 @@ impl GlobalState {
240 BuildDataProgress::Report(msg) => { 255 BuildDataProgress::Report(msg) => {
241 (Some(Progress::Report), Some(msg)) 256 (Some(Progress::Report), Some(msg))
242 } 257 }
243 BuildDataProgress::End(collector) => { 258 BuildDataProgress::End(build_data_result) => {
244 self.fetch_build_data_completed(); 259 self.fetch_build_data_completed(build_data_result);
245 let workspaces = 260
246 (*self.workspaces).clone().into_iter().map(Ok).collect(); 261 self.switch_workspaces();
247 self.switch_workspaces(workspaces, Some(collector)); 262
248 (Some(Progress::End), None) 263 (Some(Progress::End), None)
249 } 264 }
250 }; 265 };
266
251 if let Some(state) = state { 267 if let Some(state) = state {
252 self.report_progress("loading", state, msg, None); 268 self.report_progress("loading", state, msg, None);
253 } 269 }
254 } 270 }
255 } 271 }
272
256 // Coalesce multiple task events into one loop turn 273 // Coalesce multiple task events into one loop turn
257 task = match self.task_pool.receiver.try_recv() { 274 task = match self.task_pool.receiver.try_recv() {
258 Ok(task) => task, 275 Ok(task) => task,
@@ -298,30 +315,25 @@ impl GlobalState {
298 } 315 }
299 vfs::loader::Message::Progress { n_total, n_done, config_version } => { 316 vfs::loader::Message::Progress { n_total, n_done, config_version } => {
300 always!(config_version <= self.vfs_config_version); 317 always!(config_version <= self.vfs_config_version);
301 if n_total == 0 { 318
302 new_status = Status::Invalid; 319 self.vfs_progress_config_version = config_version;
320 self.vfs_progress_n_total = n_total;
321 self.vfs_progress_n_done = n_done;
322
323 let state = if n_done == 0 {
324 Progress::Begin
325 } else if n_done < n_total {
326 Progress::Report
303 } else { 327 } else {
304 let state = if n_done == 0 { 328 assert_eq!(n_done, n_total);
305 new_status = Status::Loading; 329 Progress::End
306 Progress::Begin 330 };
307 } else if n_done < n_total { 331 self.report_progress(
308 Progress::Report 332 "roots scanned",
309 } else { 333 state,
310 assert_eq!(n_done, n_total); 334 Some(format!("{}/{}", n_done, n_total)),
311 new_status = Status::Ready { 335 Some(Progress::fraction(n_done, n_total)),
312 partial: self.config.run_build_scripts() 336 )
313 && self.workspace_build_data.is_none()
314 || config_version < self.vfs_config_version,
315 };
316 Progress::End
317 };
318 self.report_progress(
319 "roots scanned",
320 state,
321 Some(format!("{}/{}", n_done, n_total)),
322 Some(Progress::fraction(n_done, n_total)),
323 )
324 }
325 } 337 }
326 } 338 }
327 // Coalesce many VFS event into a single loop turn 339 // Coalesce many VFS event into a single loop turn
@@ -397,18 +409,14 @@ impl GlobalState {
397 } 409 }
398 410
399 let state_changed = self.process_changes(); 411 let state_changed = self.process_changes();
400 let prev_status = self.status; 412
401 if prev_status != new_status { 413 if self.is_quiescent() && !was_quiescent {
402 self.transition(new_status);
403 }
404 let is_ready = matches!(self.status, Status::Ready { .. });
405 if prev_status == Status::Loading && is_ready {
406 for flycheck in &self.flycheck { 414 for flycheck in &self.flycheck {
407 flycheck.update(); 415 flycheck.update();
408 } 416 }
409 } 417 }
410 418
411 if is_ready && (state_changed || prev_status == Status::Loading) { 419 if self.is_quiescent() && (!was_quiescent || state_changed) {
412 self.update_file_notifications_on_threadpool(); 420 self.update_file_notifications_on_threadpool();
413 421
414 // Refresh semantic tokens if the client supports it. 422 // Refresh semantic tokens if the client supports it.
@@ -437,9 +445,13 @@ impl GlobalState {
437 } 445 }
438 } 446 }
439 447
440 self.fetch_workspaces_if_needed(); 448 if self.config.cargo_autoreload() {
449 self.fetch_workspaces_if_needed();
450 }
441 self.fetch_build_data_if_needed(); 451 self.fetch_build_data_if_needed();
442 452
453 self.report_new_status_if_needed();
454
443 let loop_duration = loop_start.elapsed(); 455 let loop_duration = loop_start.elapsed();
444 if loop_duration > Duration::from_millis(100) { 456 if loop_duration > Duration::from_millis(100) {
445 log::warn!("overly long loop turn: {:?}", loop_duration); 457 log::warn!("overly long loop turn: {:?}", loop_duration);
@@ -466,7 +478,8 @@ impl GlobalState {
466 return Ok(()); 478 return Ok(());
467 } 479 }
468 480
469 if self.status == Status::Loading && req.method != "shutdown" { 481 // Avoid flashing a bunch of unresolved references during initial load.
482 if self.workspaces.is_empty() && !self.is_quiescent() {
470 self.respond(lsp_server::Response::new_err( 483 self.respond(lsp_server::Response::new_err(
471 req.id, 484 req.id,
472 // FIXME: i32 should impl From<ErrorCode> (from() guarantees lossless conversion) 485 // FIXME: i32 should impl From<ErrorCode> (from() guarantees lossless conversion)
@@ -477,7 +490,11 @@ impl GlobalState {
477 } 490 }
478 491
479 RequestDispatcher { req: Some(req), global_state: self } 492 RequestDispatcher { req: Some(req), global_state: self }
480 .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces_request()))? 493 .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| {
494 s.fetch_workspaces_request();
495 s.fetch_workspaces_if_needed();
496 Ok(())
497 })?
481 .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? 498 .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))?
482 .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? 499 .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))?
483 .on_sync::<lsp_types::request::Shutdown>(|s, ()| { 500 .on_sync::<lsp_types::request::Shutdown>(|s, ()| {
diff --git a/crates/rust-analyzer/src/op_queue.rs b/crates/rust-analyzer/src/op_queue.rs
index f71b718bc..1d612a933 100644
--- a/crates/rust-analyzer/src/op_queue.rs
+++ b/crates/rust-analyzer/src/op_queue.rs
@@ -2,27 +2,27 @@
2//! at a time. 2//! at a time.
3 3
4pub(crate) struct OpQueue<Args, Output> { 4pub(crate) struct OpQueue<Args, Output> {
5 op_scheduled: Option<Args>, 5 op_requested: Option<Args>,
6 op_in_progress: bool, 6 op_in_progress: bool,
7 last_op_result: Output, 7 last_op_result: Output,
8} 8}
9 9
10impl<Args, Output: Default> Default for OpQueue<Args, Output> { 10impl<Args, Output: Default> Default for OpQueue<Args, Output> {
11 fn default() -> Self { 11 fn default() -> Self {
12 Self { op_scheduled: None, op_in_progress: false, last_op_result: Default::default() } 12 Self { op_requested: None, op_in_progress: false, last_op_result: Default::default() }
13 } 13 }
14} 14}
15 15
16impl<Args, Output> OpQueue<Args, Output> { 16impl<Args, Output> OpQueue<Args, Output> {
17 pub(crate) fn request_op(&mut self, data: Args) { 17 pub(crate) fn request_op(&mut self, data: Args) {
18 self.op_scheduled = Some(data); 18 self.op_requested = Some(data);
19 } 19 }
20 pub(crate) fn should_start_op(&mut self) -> Option<Args> { 20 pub(crate) fn should_start_op(&mut self) -> Option<Args> {
21 if self.op_in_progress { 21 if self.op_in_progress {
22 return None; 22 return None;
23 } 23 }
24 self.op_in_progress = self.op_scheduled.is_some(); 24 self.op_in_progress = self.op_requested.is_some();
25 self.op_scheduled.take() 25 self.op_requested.take()
26 } 26 }
27 pub(crate) fn op_completed(&mut self, result: Output) { 27 pub(crate) fn op_completed(&mut self, result: Output) {
28 assert!(self.op_in_progress); 28 assert!(self.op_in_progress);
@@ -34,4 +34,10 @@ impl<Args, Output> OpQueue<Args, Output> {
34 pub(crate) fn last_op_result(&self) -> &Output { 34 pub(crate) fn last_op_result(&self) -> &Output {
35 &self.last_op_result 35 &self.last_op_result
36 } 36 }
37 pub(crate) fn op_in_progress(&self) -> bool {
38 self.op_in_progress
39 }
40 pub(crate) fn op_requested(&self) -> bool {
41 self.op_requested.is_some()
42 }
37} 43}
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index d12f891bb..d0cc1b61a 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -9,11 +9,10 @@ use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};
9 9
10use crate::{ 10use crate::{
11 config::{Config, FilesWatcher, LinkedProject}, 11 config::{Config, FilesWatcher, LinkedProject},
12 global_state::{GlobalState, Status}, 12 global_state::GlobalState,
13 lsp_ext, 13 lsp_ext,
14 main_loop::Task, 14 main_loop::Task,
15}; 15};
16use lsp_ext::StatusParams;
17 16
18#[derive(Debug)] 17#[derive(Debug)]
19pub(crate) enum ProjectWorkspaceProgress { 18pub(crate) enum ProjectWorkspaceProgress {
@@ -30,6 +29,13 @@ pub(crate) enum BuildDataProgress {
30} 29}
31 30
32impl GlobalState { 31impl GlobalState {
32 pub(crate) fn is_quiescent(&self) -> bool {
33 !(self.fetch_workspaces_queue.op_in_progress()
34 || self.fetch_build_data_queue.op_in_progress()
35 || self.vfs_progress_config_version < self.vfs_config_version
36 || self.vfs_progress_n_done < self.vfs_progress_n_total)
37 }
38
33 pub(crate) fn update_configuration(&mut self, config: Config) { 39 pub(crate) fn update_configuration(&mut self, config: Config) {
34 let _p = profile::span("GlobalState::update_configuration"); 40 let _p = profile::span("GlobalState::update_configuration");
35 let old_config = mem::replace(&mut self.config, Arc::new(config)); 41 let old_config = mem::replace(&mut self.config, Arc::new(config));
@@ -46,25 +52,17 @@ impl GlobalState {
46 if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) { 52 if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) {
47 return; 53 return;
48 } 54 }
49 match self.status {
50 Status::Loading | Status::NeedsReload => return,
51 Status::Ready { .. } | Status::Invalid => (),
52 }
53 log::info!( 55 log::info!(
54 "Reloading workspace because of the following changes: {}", 56 "Requesting workspace reload because of the following changes: {}",
55 itertools::join( 57 itertools::join(
56 changes 58 changes
57 .iter() 59 .iter()
58 .filter(|(path, kind)| is_interesting(path, *kind)) 60 .filter(|(path, kind)| is_interesting(path, *kind))
59 .map(|(path, kind)| format!("{}/{:?}", path.display(), kind)), 61 .map(|(path, kind)| format!("{}: {:?}", path.display(), kind)),
60 ", " 62 ", "
61 ) 63 )
62 ); 64 );
63 if self.config.cargo_autoreload() { 65 self.fetch_workspaces_request();
64 self.fetch_workspaces_request();
65 } else {
66 self.transition(Status::NeedsReload);
67 }
68 66
69 fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool { 67 fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool {
70 const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"]; 68 const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
@@ -101,46 +99,38 @@ impl GlobalState {
101 false 99 false
102 } 100 }
103 } 101 }
104 pub(crate) fn transition(&mut self, new_status: Status) { 102 pub(crate) fn report_new_status_if_needed(&mut self) {
105 self.status = new_status; 103 if !self.config.server_status_notification() {
106 if self.config.status_notification() { 104 return;
107 let lsp_status = match new_status {
108 Status::Loading => lsp_ext::Status::Loading,
109 Status::Ready { partial: true } => lsp_ext::Status::ReadyPartial,
110 Status::Ready { partial: false } => lsp_ext::Status::Ready,
111 Status::Invalid => lsp_ext::Status::Invalid,
112 Status::NeedsReload => lsp_ext::Status::NeedsReload,
113 };
114 self.send_notification::<lsp_ext::StatusNotification>(StatusParams {
115 status: lsp_status,
116 });
117 } 105 }
118 }
119
120 pub(crate) fn fetch_build_data_request(&mut self, build_data_collector: BuildDataCollector) {
121 self.fetch_build_data_queue.request_op(build_data_collector);
122 }
123 106
124 pub(crate) fn fetch_build_data_if_needed(&mut self) { 107 let mut status = lsp_ext::ServerStatusParams {
125 let mut build_data_collector = match self.fetch_build_data_queue.should_start_op() { 108 health: lsp_ext::Health::Ok,
126 Some(it) => it, 109 quiescent: self.is_quiescent(),
127 None => return, 110 message: None,
128 }; 111 };
129 self.task_pool.handle.spawn_with_sender(move |sender| {
130 sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap();
131 112
132 let progress = { 113 if let Some(error) = self.build_data_error() {
133 let sender = sender.clone(); 114 status.health = lsp_ext::Health::Warning;
134 move |msg| { 115 status.message = Some(error)
135 sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap() 116 }
136 } 117 if !self.config.cargo_autoreload()
137 }; 118 && self.is_quiescent()
138 let res = build_data_collector.collect(&progress); 119 && self.fetch_workspaces_queue.op_requested()
139 sender.send(Task::FetchBuildData(BuildDataProgress::End(res))).unwrap(); 120 {
140 }); 121 status.health = lsp_ext::Health::Warning;
141 } 122 status.message = Some("Workspace reload required".to_string())
142 pub(crate) fn fetch_build_data_completed(&mut self) { 123 }
143 self.fetch_build_data_queue.op_completed(()) 124
125 if let Some(error) = self.fetch_workspace_error() {
126 status.health = lsp_ext::Health::Error;
127 status.message = Some(error)
128 }
129
130 if self.last_reported_status.as_ref() != Some(&status) {
131 self.last_reported_status = Some(status.clone());
132 self.send_notification::<lsp_ext::ServerStatusNotification>(status);
133 }
144 } 134 }
145 135
146 pub(crate) fn fetch_workspaces_request(&mut self) { 136 pub(crate) fn fetch_workspaces_request(&mut self) {
@@ -194,54 +184,71 @@ impl GlobalState {
194 } 184 }
195 }); 185 });
196 } 186 }
197 pub(crate) fn fetch_workspaces_completed(&mut self) { 187 pub(crate) fn fetch_workspaces_completed(
198 self.fetch_workspaces_queue.op_completed(()) 188 &mut self,
189 workspaces: Vec<anyhow::Result<ProjectWorkspace>>,
190 ) {
191 self.fetch_workspaces_queue.op_completed(workspaces)
192 }
193
194 pub(crate) fn fetch_build_data_request(&mut self, build_data_collector: BuildDataCollector) {
195 self.fetch_build_data_queue.request_op(build_data_collector);
199 } 196 }
197 pub(crate) fn fetch_build_data_if_needed(&mut self) {
198 let mut build_data_collector = match self.fetch_build_data_queue.should_start_op() {
199 Some(it) => it,
200 None => return,
201 };
202 self.task_pool.handle.spawn_with_sender(move |sender| {
203 sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap();
200 204
201 pub(crate) fn switch_workspaces( 205 let progress = {
206 let sender = sender.clone();
207 move |msg| {
208 sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap()
209 }
210 };
211 let res = build_data_collector.collect(&progress);
212 sender.send(Task::FetchBuildData(BuildDataProgress::End(res))).unwrap();
213 });
214 }
215 pub(crate) fn fetch_build_data_completed(
202 &mut self, 216 &mut self,
203 workspaces: Vec<anyhow::Result<ProjectWorkspace>>, 217 build_data: anyhow::Result<BuildDataResult>,
204 workspace_build_data: Option<anyhow::Result<BuildDataResult>>,
205 ) { 218 ) {
206 let _p = profile::span("GlobalState::switch_workspaces"); 219 self.fetch_build_data_queue.op_completed(Some(build_data))
207 log::info!("will switch workspaces: {:?}", workspaces); 220 }
208 221
209 let mut has_errors = false; 222 pub(crate) fn switch_workspaces(&mut self) {
210 let workspaces = workspaces 223 let _p = profile::span("GlobalState::switch_workspaces");
211 .into_iter() 224 log::info!("will switch workspaces");
212 .filter_map(|res| {
213 res.map_err(|err| {
214 has_errors = true;
215 log::error!("failed to load workspace: {:#}", err);
216 if self.workspaces.is_empty() {
217 self.show_message(
218 lsp_types::MessageType::Error,
219 format!("rust-analyzer failed to load workspace: {:#}", err),
220 );
221 }
222 })
223 .ok()
224 })
225 .collect::<Vec<_>>();
226 225
227 let workspace_build_data = match workspace_build_data { 226 if let Some(error_message) = self.fetch_workspace_error() {
228 Some(Ok(it)) => Some(it), 227 log::error!("failed to switch workspaces: {}", error_message);
229 Some(Err(err)) => { 228 self.show_message(lsp_types::MessageType::Error, error_message);
230 log::error!("failed to fetch build data: {:#}", err); 229 if !self.workspaces.is_empty() {
231 self.show_message(
232 lsp_types::MessageType::Error,
233 format!("rust-analyzer failed to fetch build data: {:#}", err),
234 );
235 return; 230 return;
236 } 231 }
237 None => None, 232 }
238 };
239 233
240 if *self.workspaces == workspaces && self.workspace_build_data == workspace_build_data { 234 if let Some(error_message) = self.build_data_error() {
241 return; 235 log::error!("failed to switch build data: {}", error_message);
236 self.show_message(lsp_types::MessageType::Error, error_message);
242 } 237 }
243 238
244 if !self.workspaces.is_empty() && has_errors { 239 let workspaces = self
240 .fetch_workspaces_queue
241 .last_op_result()
242 .iter()
243 .filter_map(|res| res.as_ref().ok().cloned())
244 .collect::<Vec<_>>();
245
246 let workspace_build_data = match self.fetch_build_data_queue.last_op_result() {
247 Some(Ok(it)) => Some(it.clone()),
248 None | Some(Err(_)) => None,
249 };
250
251 if *self.workspaces == workspaces && self.workspace_build_data == workspace_build_data {
245 return; 252 return;
246 } 253 }
247 254
@@ -337,14 +344,6 @@ impl GlobalState {
337 }; 344 };
338 change.set_crate_graph(crate_graph); 345 change.set_crate_graph(crate_graph);
339 346
340 if self.config.run_build_scripts() && workspace_build_data.is_none() {
341 let mut collector = BuildDataCollector::default();
342 for ws in &workspaces {
343 ws.collect_build_data_configs(&mut collector);
344 }
345 self.fetch_build_data_request(collector)
346 }
347
348 self.source_root_config = project_folders.source_root_config; 347 self.source_root_config = project_folders.source_root_config;
349 self.workspaces = Arc::new(workspaces); 348 self.workspaces = Arc::new(workspaces);
350 self.workspace_build_data = workspace_build_data; 349 self.workspace_build_data = workspace_build_data;
@@ -355,6 +354,32 @@ impl GlobalState {
355 log::info!("did switch workspaces"); 354 log::info!("did switch workspaces");
356 } 355 }
357 356
357 fn fetch_workspace_error(&self) -> Option<String> {
358 let mut buf = String::new();
359
360 for ws in self.fetch_workspaces_queue.last_op_result() {
361 if let Err(err) = ws {
362 stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err);
363 }
364 }
365
366 if buf.is_empty() {
367 return None;
368 }
369
370 Some(buf)
371 }
372
373 fn build_data_error(&self) -> Option<String> {
374 match self.fetch_build_data_queue.last_op_result() {
375 Some(Err(err)) => {
376 Some(format!("rust-analyzer failed to fetch build data: {:#}\n", err))
377 }
378 Some(Ok(data)) => data.error(),
379 None => None,
380 }
381 }
382
358 fn reload_flycheck(&mut self) { 383 fn reload_flycheck(&mut self) {
359 let _p = profile::span("GlobalState::reload_flycheck"); 384 let _p = profile::span("GlobalState::reload_flycheck");
360 let config = match self.config.flycheck() { 385 let config = match self.config.flycheck() {
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs
index 95bf26f01..8d68f1b7d 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/support.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs
@@ -103,7 +103,7 @@ impl<'a> Project<'a> {
103 ..Default::default() 103 ..Default::default()
104 }), 104 }),
105 experimental: Some(json!({ 105 experimental: Some(json!({
106 "statusNotification": true, 106 "serverStatusNotification": true,
107 })), 107 })),
108 ..Default::default() 108 ..Default::default()
109 }, 109 },
@@ -213,13 +213,12 @@ impl Server {
213 } 213 }
214 pub(crate) fn wait_until_workspace_is_loaded(self) -> Server { 214 pub(crate) fn wait_until_workspace_is_loaded(self) -> Server {
215 self.wait_for_message_cond(1, &|msg: &Message| match msg { 215 self.wait_for_message_cond(1, &|msg: &Message| match msg {
216 Message::Notification(n) if n.method == "rust-analyzer/status" => { 216 Message::Notification(n) if n.method == "experimental/serverStatus" => {
217 let status = n 217 let status = n
218 .clone() 218 .clone()
219 .extract::<lsp_ext::StatusParams>("rust-analyzer/status") 219 .extract::<lsp_ext::ServerStatusParams>("experimental/serverStatus")
220 .unwrap() 220 .unwrap();
221 .status; 221 status.quiescent
222 matches!(status, lsp_ext::Status::Ready)
223 } 222 }
224 _ => false, 223 _ => false,
225 }) 224 })
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index d26be4853..b0a18d58d 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -178,6 +178,7 @@ where
178 start..start + len 178 start..start + len
179} 179}
180 180
181#[repr(transparent)]
181pub struct JodChild(pub process::Child); 182pub struct JodChild(pub process::Child);
182 183
183impl ops::Deref for JodChild { 184impl ops::Deref for JodChild {
@@ -200,6 +201,13 @@ impl Drop for JodChild {
200 } 201 }
201} 202}
202 203
204impl JodChild {
205 pub fn into_inner(self) -> process::Child {
206 // SAFETY: repr transparent
207 unsafe { std::mem::transmute::<JodChild, process::Child>(self) }
208 }
209}
210
203#[cfg(test)] 211#[cfg(test)]
204mod tests { 212mod tests {
205 use super::*; 213 use super::*;
diff --git a/crates/vfs/Cargo.toml b/crates/vfs/Cargo.toml
index c318a68f7..894944b18 100644
--- a/crates/vfs/Cargo.toml
+++ b/crates/vfs/Cargo.toml
@@ -14,3 +14,4 @@ rustc-hash = "1.0"
14fst = "0.4" 14fst = "0.4"
15 15
16paths = { path = "../paths", version = "0.0.0" } 16paths = { path = "../paths", version = "0.0.0" }
17indexmap = "1.6.2"
diff --git a/crates/vfs/src/path_interner.rs b/crates/vfs/src/path_interner.rs
index 2189e5e25..6e049f0d4 100644
--- a/crates/vfs/src/path_interner.rs
+++ b/crates/vfs/src/path_interner.rs
@@ -1,15 +1,22 @@
1//! Maps paths to compact integer ids. We don't care about clearings paths which 1//! Maps paths to compact integer ids. We don't care about clearings paths which
2//! no longer exist -- the assumption is total size of paths we ever look at is 2//! no longer exist -- the assumption is total size of paths we ever look at is
3//! not too big. 3//! not too big.
4use rustc_hash::FxHashMap; 4use std::hash::BuildHasherDefault;
5
6use indexmap::IndexSet;
7use rustc_hash::FxHasher;
5 8
6use crate::{FileId, VfsPath}; 9use crate::{FileId, VfsPath};
7 10
8/// Structure to map between [`VfsPath`] and [`FileId`]. 11/// Structure to map between [`VfsPath`] and [`FileId`].
9#[derive(Default)]
10pub(crate) struct PathInterner { 12pub(crate) struct PathInterner {
11 map: FxHashMap<VfsPath, FileId>, 13 map: IndexSet<VfsPath, BuildHasherDefault<FxHasher>>,
12 vec: Vec<VfsPath>, 14}
15
16impl Default for PathInterner {
17 fn default() -> Self {
18 Self { map: IndexSet::default() }
19 }
13} 20}
14 21
15impl PathInterner { 22impl PathInterner {
@@ -17,7 +24,7 @@ impl PathInterner {
17 /// 24 ///
18 /// If `path` does not exists in `self`, returns [`None`]. 25 /// If `path` does not exists in `self`, returns [`None`].
19 pub(crate) fn get(&self, path: &VfsPath) -> Option<FileId> { 26 pub(crate) fn get(&self, path: &VfsPath) -> Option<FileId> {
20 self.map.get(path).copied() 27 self.map.get_index_of(path).map(|i| FileId(i as u32))
21 } 28 }
22 29
23 /// Insert `path` in `self`. 30 /// Insert `path` in `self`.
@@ -25,13 +32,9 @@ impl PathInterner {
25 /// - If `path` already exists in `self`, returns its associated id; 32 /// - If `path` already exists in `self`, returns its associated id;
26 /// - Else, returns a newly allocated id. 33 /// - Else, returns a newly allocated id.
27 pub(crate) fn intern(&mut self, path: VfsPath) -> FileId { 34 pub(crate) fn intern(&mut self, path: VfsPath) -> FileId {
28 if let Some(id) = self.get(&path) { 35 let (id, _added) = self.map.insert_full(path);
29 return id; 36 assert!(id < u32::MAX as usize);
30 } 37 FileId(id as u32)
31 let id = FileId(self.vec.len() as u32);
32 self.map.insert(path.clone(), id);
33 self.vec.push(path);
34 id
35 } 38 }
36 39
37 /// Returns the path corresponding to `id`. 40 /// Returns the path corresponding to `id`.
@@ -40,6 +43,6 @@ impl PathInterner {
40 /// 43 ///
41 /// Panics if `id` does not exists in `self`. 44 /// Panics if `id` does not exists in `self`.
42 pub(crate) fn lookup(&self, id: FileId) -> &VfsPath { 45 pub(crate) fn lookup(&self, id: FileId) -> &VfsPath {
43 &self.vec[id.0 as usize] 46 self.map.get_index(id.0 as usize).unwrap()
44 } 47 }
45} 48}
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 73be59a82..989771ac6 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
1<!--- 1<!---
2lsp_ext.rs hash: e8a7502bd2b2c2f5 2lsp_ext.rs hash: faae991334a151d0
3 3
4If you need to change the above hash to make the test pass, please check if you 4If you need to change the above hash to make the test pass, please check if you
5need to adjust this doc as well and ping this issue: 5need to adjust this doc as well and ping this issue:
@@ -419,23 +419,37 @@ Returns internal status message, mostly for debugging purposes.
419 419
420Reloads project information (that is, re-executes `cargo metadata`). 420Reloads project information (that is, re-executes `cargo metadata`).
421 421
422## Status Notification 422## Server Status
423 423
424**Experimental Client Capability:** `{ "statusNotification": boolean }` 424**Experimental Client Capability:** `{ "serverStatus": boolean }`
425 425
426**Method:** `rust-analyzer/status` 426**Method:** `experimental/serverStatus`
427 427
428**Notification:** 428**Notification:**
429 429
430```typescript 430```typescript
431interface StatusParams { 431interface ServerStatusParams {
432 status: "loading" | "readyPartial" | "ready" | "invalid" | "needsReload", 432 /// `ok` means that the server is completely functional.
433 ///
434 /// `warning` means that the server is partially functional.
435 /// It can server requests, but some results might be wrong due to,
436 /// for example, some missing dependencies.
437 ///
438 /// `error` means that the server is not functional. For example,
439 /// there's a fatal build configuration problem.
440 health: "ok" | "warning" | "error",
441 /// Is there any pending background work which might change the status?
442 /// For example, are dependencies being downloaded?
443 quiescent: bool,
444 /// Explanatory message to show on hover.
445 message?: string,
433} 446}
434``` 447```
435 448
436This notification is sent from server to client. 449This notification is sent from server to client.
437The client can use it to display persistent status to the user (in modline). 450The client can use it to display *persistent* status to the user (in modline).
438For `needsReload` state, the client can provide a context-menu action to run `rust-analyzer/reloadWorkspace` request. 451It is similar to the `showMessage`, but is intended for stares rather than point-in-time events.
452
439 453
440## Syntax Tree 454## Syntax Tree
441 455
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 0771ca3b6..116f41df6 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -159,7 +159,7 @@ class ExperimentalFeatures implements lc.StaticFeature {
159 caps.snippetTextEdit = true; 159 caps.snippetTextEdit = true;
160 caps.codeActionGroup = true; 160 caps.codeActionGroup = true;
161 caps.hoverActions = true; 161 caps.hoverActions = true;
162 caps.statusNotification = true; 162 caps.serverStatusNotification = true;
163 capabilities.experimental = caps; 163 capabilities.experimental = caps;
164 } 164 }
165 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { 165 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index c07583cfa..bd023f803 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -5,7 +5,7 @@ import * as ra from './lsp_ext';
5import { Config } from './config'; 5import { Config } from './config';
6import { createClient } from './client'; 6import { createClient } from './client';
7import { isRustEditor, RustEditor } from './util'; 7import { isRustEditor, RustEditor } from './util';
8import { Status } from './lsp_ext'; 8import { ServerStatusParams } from './lsp_ext';
9 9
10export class Ctx { 10export class Ctx {
11 private constructor( 11 private constructor(
@@ -36,7 +36,7 @@ export class Ctx {
36 36
37 res.pushCleanup(client.start()); 37 res.pushCleanup(client.start());
38 await client.onReady(); 38 await client.onReady();
39 client.onNotification(ra.status, (params) => res.setStatus(params.status)); 39 client.onNotification(ra.serverStatus, (params) => res.setServerStatus(params));
40 return res; 40 return res;
41 } 41 }
42 42
@@ -66,39 +66,28 @@ export class Ctx {
66 return this.extCtx.subscriptions; 66 return this.extCtx.subscriptions;
67 } 67 }
68 68
69 setStatus(status: Status) { 69 setServerStatus(status: ServerStatusParams) {
70 switch (status) { 70 this.statusBar.tooltip = status.message ?? "Ready";
71 case "loading": 71 let icon = "";
72 this.statusBar.text = "$(sync~spin) rust-analyzer"; 72 switch (status.health) {
73 this.statusBar.tooltip = "Loading the project"; 73 case "ok":
74 this.statusBar.command = undefined;
75 this.statusBar.color = undefined; 74 this.statusBar.color = undefined;
76 break; 75 break;
77 case "readyPartial": 76 case "warning":
78 this.statusBar.text = "rust-analyzer"; 77 this.statusBar.tooltip += "\nClick to reload.";
79 this.statusBar.tooltip = "Ready (Partial)";
80 this.statusBar.command = undefined;
81 this.statusBar.color = undefined;
82 break;
83 case "ready":
84 this.statusBar.text = "rust-analyzer";
85 this.statusBar.tooltip = "Ready";
86 this.statusBar.command = undefined;
87 this.statusBar.color = undefined;
88 break;
89 case "invalid":
90 this.statusBar.text = "$(error) rust-analyzer";
91 this.statusBar.tooltip = "Failed to load the project";
92 this.statusBar.command = undefined;
93 this.statusBar.color = new vscode.ThemeColor("notificationsErrorIcon.foreground");
94 break;
95 case "needsReload":
96 this.statusBar.text = "$(warning) rust-analyzer";
97 this.statusBar.tooltip = "Click to reload";
98 this.statusBar.command = "rust-analyzer.reloadWorkspace"; 78 this.statusBar.command = "rust-analyzer.reloadWorkspace";
99 this.statusBar.color = new vscode.ThemeColor("notificationsWarningIcon.foreground"); 79 this.statusBar.color = new vscode.ThemeColor("notificationsWarningIcon.foreground");
80 icon = "$(warning) ";
81 break;
82 case "error":
83 this.statusBar.tooltip += "\nClick to reload.";
84 this.statusBar.command = "rust-analyzer.reloadWorkspace";
85 this.statusBar.color = new vscode.ThemeColor("notificationsErrorIcon.foreground");
86 icon = "$(error) ";
100 break; 87 break;
101 } 88 }
89 if (!status.quiescent) icon = "$(sync~spin) ";
90 this.statusBar.text = `${icon} rust-analyzer`;
102 } 91 }
103 92
104 pushCleanup(d: Disposable) { 93 pushCleanup(d: Disposable) {
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index 00e128b8c..e453bb9e0 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -10,11 +10,12 @@ export interface AnalyzerStatusParams {
10export const analyzerStatus = new lc.RequestType<AnalyzerStatusParams, string, void>("rust-analyzer/analyzerStatus"); 10export const analyzerStatus = new lc.RequestType<AnalyzerStatusParams, string, void>("rust-analyzer/analyzerStatus");
11export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage"); 11export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage");
12 12
13export type Status = "loading" | "ready" | "readyPartial" | "invalid" | "needsReload"; 13export interface ServerStatusParams {
14export interface StatusParams { 14 health: "ok" | "warning" | "error";
15 status: Status; 15 quiescent: boolean;
16 message?: string;
16} 17}
17export const status = new lc.NotificationType<StatusParams>("rust-analyzer/status"); 18export const serverStatus = new lc.NotificationType<ServerStatusParams>("experimental/serverStatus");
18 19
19export const reloadWorkspace = new lc.RequestType0<null, void>("rust-analyzer/reloadWorkspace"); 20export const reloadWorkspace = new lc.RequestType0<null, void>("rust-analyzer/reloadWorkspace");
20 21