aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir/src/semantics.rs6
-rw-r--r--crates/hir/src/source_analyzer.rs30
-rw-r--r--crates/hir_ty/src/infer.rs20
-rw-r--r--crates/hir_ty/src/infer/expr.rs6
-rw-r--r--crates/hir_ty/src/infer/path.rs2
-rw-r--r--crates/hir_ty/src/infer/unify.rs8
-rw-r--r--crates/hir_ty/src/lower.rs71
-rw-r--r--crates/ide/src/hover.rs63
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs2
-rw-r--r--crates/rust-analyzer/src/bin/main.rs2
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt138
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs101
-rw-r--r--crates/rust-analyzer/src/handlers.rs3
-rw-r--r--docs/dev/style.md45
14 files changed, 394 insertions, 103 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index d3caeef4e..3bf722d2a 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -76,9 +76,11 @@ impl PathResolution {
76 pub fn assoc_type_shorthand_candidates<R>( 76 pub fn assoc_type_shorthand_candidates<R>(
77 &self, 77 &self,
78 db: &dyn HirDatabase, 78 db: &dyn HirDatabase,
79 mut cb: impl FnMut(TypeAlias) -> Option<R>, 79 mut cb: impl FnMut(&Name, TypeAlias) -> Option<R>,
80 ) -> Option<R> { 80 ) -> Option<R> {
81 associated_type_shorthand_candidates(db, self.in_type_ns()?, |_, _, id| cb(id.into())) 81 associated_type_shorthand_candidates(db, self.in_type_ns()?, |name, _, id| {
82 cb(name, id.into())
83 })
82 } 84 }
83} 85}
84 86
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 37d162b32..8e9ea0a03 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -20,7 +20,7 @@ use hir_def::{
20use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; 20use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
21use hir_ty::{ 21use hir_ty::{
22 diagnostics::{record_literal_missing_fields, record_pattern_missing_fields}, 22 diagnostics::{record_literal_missing_fields, record_pattern_missing_fields},
23 InferenceResult, Substitution, 23 InferenceResult, Substitution, TyLoweringContext,
24}; 24};
25use syntax::{ 25use syntax::{
26 ast::{self, AstNode}, 26 ast::{self, AstNode},
@@ -466,7 +466,21 @@ fn resolve_hir_path_(
466 prefer_value_ns: bool, 466 prefer_value_ns: bool,
467) -> Option<PathResolution> { 467) -> Option<PathResolution> {
468 let types = || { 468 let types = || {
469 resolver.resolve_path_in_type_ns_fully(db.upcast(), path.mod_path()).map(|ty| match ty { 469 let (ty, unresolved) = match path.type_anchor() {
470 Some(type_ref) => {
471 let (_, res) = TyLoweringContext::new(db, resolver).lower_ty_ext(type_ref);
472 res.map(|ty_ns| (ty_ns, path.segments().first()))
473 }
474 None => {
475 let (ty, remaining) =
476 resolver.resolve_path_in_type_ns(db.upcast(), path.mod_path())?;
477 match remaining {
478 Some(remaining) if remaining > 1 => None,
479 _ => Some((ty, path.segments().get(1))),
480 }
481 }
482 }?;
483 let res = match ty {
470 TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), 484 TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
471 TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }), 485 TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }),
472 TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => { 486 TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => {
@@ -476,7 +490,17 @@ fn resolve_hir_path_(
476 TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), 490 TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()),
477 TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()), 491 TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()),
478 TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), 492 TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
479 }) 493 };
494 match unresolved {
495 Some(unresolved) => res
496 .assoc_type_shorthand_candidates(db, |name, alias| {
497 (name == unresolved.name).then(|| alias)
498 })
499 .map(TypeAlias::from)
500 .map(Into::into)
501 .map(PathResolution::Def),
502 None => Some(res),
503 }
480 }; 504 };
481 505
482 let body_owner = resolver.body_owner(); 506 let body_owner = resolver.body_owner();
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index e4407ff50..674e9e6f9 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -210,6 +210,7 @@ struct InferenceContext<'a> {
210 table: unify::InferenceTable, 210 table: unify::InferenceTable,
211 trait_env: Arc<TraitEnvironment>, 211 trait_env: Arc<TraitEnvironment>,
212 obligations: Vec<DomainGoal>, 212 obligations: Vec<DomainGoal>,
213 last_obligations_check: Option<u32>,
213 result: InferenceResult, 214 result: InferenceResult,
214 /// The return type of the function being inferred, or the closure if we're 215 /// The return type of the function being inferred, or the closure if we're
215 /// currently within one. 216 /// currently within one.
@@ -245,6 +246,7 @@ impl<'a> InferenceContext<'a> {
245 result: InferenceResult::default(), 246 result: InferenceResult::default(),
246 table: unify::InferenceTable::new(), 247 table: unify::InferenceTable::new(),
247 obligations: Vec::default(), 248 obligations: Vec::default(),
249 last_obligations_check: None,
248 return_ty: TyKind::Unknown.intern(&Interner), // set in collect_fn_signature 250 return_ty: TyKind::Unknown.intern(&Interner), // set in collect_fn_signature
249 trait_env: owner 251 trait_env: owner
250 .as_generic_def_id() 252 .as_generic_def_id()
@@ -334,6 +336,13 @@ impl<'a> InferenceContext<'a> {
334 } 336 }
335 337
336 fn resolve_obligations_as_possible(&mut self) { 338 fn resolve_obligations_as_possible(&mut self) {
339 if self.last_obligations_check == Some(self.table.revision) {
340 // no change
341 return;
342 }
343 let _span = profile::span("resolve_obligations_as_possible");
344
345 self.last_obligations_check = Some(self.table.revision);
337 let obligations = mem::replace(&mut self.obligations, Vec::new()); 346 let obligations = mem::replace(&mut self.obligations, Vec::new());
338 for obligation in obligations { 347 for obligation in obligations {
339 let in_env = InEnvironment::new(self.trait_env.env.clone(), obligation.clone()); 348 let in_env = InEnvironment::new(self.trait_env.env.clone(), obligation.clone());
@@ -360,6 +369,11 @@ impl<'a> InferenceContext<'a> {
360 } 369 }
361 } 370 }
362 371
372 fn push_obligation(&mut self, o: DomainGoal) {
373 self.obligations.push(o);
374 self.last_obligations_check = None;
375 }
376
363 fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { 377 fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
364 self.table.unify(ty1, ty2) 378 self.table.unify(ty1, ty2)
365 } 379 }
@@ -408,8 +422,8 @@ impl<'a> InferenceContext<'a> {
408 }), 422 }),
409 ty: ty.clone(), 423 ty: ty.clone(),
410 }; 424 };
411 self.obligations.push(trait_ref.cast(&Interner)); 425 self.push_obligation(trait_ref.cast(&Interner));
412 self.obligations.push(alias_eq.cast(&Interner)); 426 self.push_obligation(alias_eq.cast(&Interner));
413 self.resolve_ty_as_possible(ty) 427 self.resolve_ty_as_possible(ty)
414 } 428 }
415 None => self.err_ty(), 429 None => self.err_ty(),
@@ -436,7 +450,7 @@ impl<'a> InferenceContext<'a> {
436 let var = self.table.new_type_var(); 450 let var = self.table.new_type_var();
437 let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() }; 451 let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() };
438 let obligation = alias_eq.cast(&Interner); 452 let obligation = alias_eq.cast(&Interner);
439 self.obligations.push(obligation); 453 self.push_obligation(obligation);
440 var 454 var
441 } 455 }
442 456
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 6279aa572..25ab3ea4c 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -99,7 +99,7 @@ impl<'a> InferenceContext<'a> {
99 environment: trait_env, 99 environment: trait_env,
100 }); 100 });
101 if self.db.trait_solve(krate, goal.value).is_some() { 101 if self.db.trait_solve(krate, goal.value).is_some() {
102 self.obligations.push(implements_fn_trait); 102 self.push_obligation(implements_fn_trait);
103 let output_proj_ty = crate::ProjectionTy { 103 let output_proj_ty = crate::ProjectionTy {
104 associated_ty_id: to_assoc_type_id(output_assoc_type), 104 associated_ty_id: to_assoc_type_id(output_assoc_type),
105 substitution: substs, 105 substitution: substs,
@@ -964,7 +964,7 @@ impl<'a> InferenceContext<'a> {
964 let (predicate, binders) = 964 let (predicate, binders) =
965 predicate.clone().subst(parameters).into_value_and_skipped_binders(); 965 predicate.clone().subst(parameters).into_value_and_skipped_binders();
966 always!(binders == 0); // quantified where clauses not yet handled 966 always!(binders == 0); // quantified where clauses not yet handled
967 self.obligations.push(predicate.cast(&Interner)); 967 self.push_obligation(predicate.cast(&Interner));
968 } 968 }
969 // add obligation for trait implementation, if this is a trait method 969 // add obligation for trait implementation, if this is a trait method
970 match def { 970 match def {
@@ -974,7 +974,7 @@ impl<'a> InferenceContext<'a> {
974 // construct a TraitRef 974 // construct a TraitRef
975 let substs = 975 let substs =
976 parameters.prefix(generics(self.db.upcast(), trait_.into()).len()); 976 parameters.prefix(generics(self.db.upcast(), trait_.into()).len());
977 self.obligations.push( 977 self.push_obligation(
978 TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs } 978 TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs }
979 .cast(&Interner), 979 .cast(&Interner),
980 ); 980 );
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index cefa38509..717738789 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -258,7 +258,7 @@ impl<'a> InferenceContext<'a> {
258 .push(ty.clone()) 258 .push(ty.clone())
259 .fill(std::iter::repeat_with(|| self.table.new_type_var())) 259 .fill(std::iter::repeat_with(|| self.table.new_type_var()))
260 .build(); 260 .build();
261 self.obligations.push( 261 self.push_obligation(
262 TraitRef { 262 TraitRef {
263 trait_id: to_chalk_trait_id(trait_), 263 trait_id: to_chalk_trait_id(trait_),
264 substitution: trait_substs.clone(), 264 substitution: trait_substs.clone(),
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs
index 6e7b0f5a6..5ea4b7481 100644
--- a/crates/hir_ty/src/infer/unify.rs
+++ b/crates/hir_ty/src/infer/unify.rs
@@ -231,6 +231,7 @@ pub(crate) struct TypeVariableData {
231pub(crate) struct InferenceTable { 231pub(crate) struct InferenceTable {
232 pub(super) var_unification_table: InPlaceUnificationTable<TypeVarId>, 232 pub(super) var_unification_table: InPlaceUnificationTable<TypeVarId>,
233 pub(super) type_variable_table: TypeVariableTable, 233 pub(super) type_variable_table: TypeVariableTable,
234 pub(super) revision: u32,
234} 235}
235 236
236impl InferenceTable { 237impl InferenceTable {
@@ -238,6 +239,7 @@ impl InferenceTable {
238 InferenceTable { 239 InferenceTable {
239 var_unification_table: InPlaceUnificationTable::new(), 240 var_unification_table: InPlaceUnificationTable::new(),
240 type_variable_table: TypeVariableTable { inner: Vec::new() }, 241 type_variable_table: TypeVariableTable { inner: Vec::new() },
242 revision: 0,
241 } 243 }
242 } 244 }
243 245
@@ -360,7 +362,10 @@ impl InferenceTable {
360 == self.type_variable_table.is_diverging(*tv2) => 362 == self.type_variable_table.is_diverging(*tv2) =>
361 { 363 {
362 // both type vars are unknown since we tried to resolve them 364 // both type vars are unknown since we tried to resolve them
363 self.var_unification_table.union(tv1.to_inner(), tv2.to_inner()); 365 if !self.var_unification_table.unioned(tv1.to_inner(), tv2.to_inner()) {
366 self.var_unification_table.union(tv1.to_inner(), tv2.to_inner());
367 self.revision += 1;
368 }
364 true 369 true
365 } 370 }
366 371
@@ -398,6 +403,7 @@ impl InferenceTable {
398 tv.to_inner(), 403 tv.to_inner(),
399 TypeVarValue::Known(other.clone().intern(&Interner)), 404 TypeVarValue::Known(other.clone().intern(&Interner)),
400 ); 405 );
406 self.revision += 1;
401 true 407 true
402 } 408 }
403 409
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index a08f694d9..72ee060e0 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -146,7 +146,7 @@ impl<'a> TyLoweringContext<'a> {
146 self.lower_ty_ext(type_ref).0 146 self.lower_ty_ext(type_ref).0
147 } 147 }
148 148
149 fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) { 149 pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) {
150 let mut res = None; 150 let mut res = None;
151 let ty = match type_ref { 151 let ty = match type_ref {
152 TypeRef::Never => TyKind::Never.intern(&Interner), 152 TypeRef::Never => TyKind::Never.intern(&Interner),
@@ -821,24 +821,38 @@ pub fn associated_type_shorthand_candidates<R>(
821 res: TypeNs, 821 res: TypeNs,
822 mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>, 822 mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>,
823) -> Option<R> { 823) -> Option<R> {
824 let traits_from_env: Vec<_> = match res { 824 let mut search = |t| {
825 TypeNs::SelfType(impl_id) => match db.impl_trait(impl_id) { 825 for t in all_super_trait_refs(db, t) {
826 None => vec![], 826 let data = db.trait_data(t.hir_trait_id());
827 // FIXME: how to correctly handle higher-ranked bounds here? 827
828 Some(trait_ref) => vec![trait_ref.value.shift_bound_vars_out(DebruijnIndex::ONE)], 828 for (name, assoc_id) in &data.items {
829 }, 829 if let AssocItemId::TypeAliasId(alias) = assoc_id {
830 if let Some(result) = cb(name, &t, *alias) {
831 return Some(result);
832 }
833 }
834 }
835 }
836 None
837 };
838
839 match res {
840 // FIXME: how to correctly handle higher-ranked bounds here?
841 TypeNs::SelfType(impl_id) => {
842 search(db.impl_trait(impl_id)?.value.shift_bound_vars_out(DebruijnIndex::ONE))
843 }
830 TypeNs::GenericParam(param_id) => { 844 TypeNs::GenericParam(param_id) => {
831 let predicates = db.generic_predicates_for_param(param_id); 845 let predicates = db.generic_predicates_for_param(param_id);
832 let mut traits_: Vec<_> = predicates 846 let res = predicates.iter().find_map(|pred| match &pred.value.value {
833 .iter() 847 // FIXME: how to correctly handle higher-ranked bounds here?
834 .filter_map(|pred| match &pred.value.value { 848 WhereClause::Implemented(tr) => {
835 // FIXME: how to correctly handle higher-ranked bounds here? 849 search(tr.clone().shift_bound_vars_out(DebruijnIndex::ONE))
836 WhereClause::Implemented(tr) => { 850 }
837 Some(tr.clone().shift_bound_vars_out(DebruijnIndex::ONE)) 851 _ => None,
838 } 852 });
839 _ => None, 853 if let res @ Some(_) = res {
840 }) 854 return res;
841 .collect(); 855 }
842 // Handle `Self::Type` referring to own associated type in trait definitions 856 // Handle `Self::Type` referring to own associated type in trait definitions
843 if let GenericDefId::TraitId(trait_id) = param_id.parent { 857 if let GenericDefId::TraitId(trait_id) = param_id.parent {
844 let generics = generics(db.upcast(), trait_id.into()); 858 let generics = generics(db.upcast(), trait_id.into());
@@ -849,30 +863,13 @@ pub fn associated_type_shorthand_candidates<R>(
849 trait_id: to_chalk_trait_id(trait_id), 863 trait_id: to_chalk_trait_id(trait_id),
850 substitution: Substitution::bound_vars(&generics, DebruijnIndex::INNERMOST), 864 substitution: Substitution::bound_vars(&generics, DebruijnIndex::INNERMOST),
851 }; 865 };
852 traits_.push(trait_ref); 866 return search(trait_ref);
853 } 867 }
854 } 868 }
855 traits_ 869 None
856 }
857 _ => vec![],
858 };
859
860 for t in traits_from_env.into_iter().flat_map(move |t| all_super_trait_refs(db, t)) {
861 let data = db.trait_data(t.hir_trait_id());
862
863 for (name, assoc_id) in &data.items {
864 match assoc_id {
865 AssocItemId::TypeAliasId(alias) => {
866 if let Some(result) = cb(name, &t, *alias) {
867 return Some(result);
868 }
869 }
870 AssocItemId::FunctionId(_) | AssocItemId::ConstId(_) => {}
871 }
872 } 870 }
871 _ => None,
873 } 872 }
874
875 None
876} 873}
877 874
878/// Build the type of all specific fields of a struct or enum variant. 875/// Build the type of all specific fields of a struct or enum variant.
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 5f9edb476..28e2e17dc 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -3834,4 +3834,67 @@ fn foo() {}
3834 "#]], 3834 "#]],
3835 ); 3835 );
3836 } 3836 }
3837
3838 #[test]
3839 fn hover_generic_assoc() {
3840 check(
3841 r#"
3842fn foo<T: A>() where T::Assoc$0: {}
3843
3844trait A {
3845 type Assoc;
3846}"#,
3847 expect![[r#"
3848 *Assoc*
3849
3850 ```rust
3851 test
3852 ```
3853
3854 ```rust
3855 type Assoc
3856 ```
3857 "#]],
3858 );
3859 check(
3860 r#"
3861fn foo<T: A>() {
3862 let _: <T>::Assoc$0;
3863}
3864
3865trait A {
3866 type Assoc;
3867}"#,
3868 expect![[r#"
3869 *Assoc*
3870
3871 ```rust
3872 test
3873 ```
3874
3875 ```rust
3876 type Assoc
3877 ```
3878 "#]],
3879 );
3880 check(
3881 r#"
3882trait A where
3883 Self::Assoc$0: ,
3884{
3885 type Assoc;
3886}"#,
3887 expect![[r#"
3888 *Assoc*
3889
3890 ```rust
3891 test
3892 ```
3893
3894 ```rust
3895 type Assoc
3896 ```
3897 "#]],
3898 );
3899 }
3837} 3900}
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index 1891eb5b3..969249df6 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -24,7 +24,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
24 }; 24 };
25 25
26 // Add associated types on type parameters and `Self`. 26 // Add associated types on type parameters and `Self`.
27 resolution.assoc_type_shorthand_candidates(ctx.db, |alias| { 27 resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| {
28 acc.add_type_alias(ctx, alias); 28 acc.add_type_alias(ctx, alias);
29 None::<()> 29 None::<()>
30 }); 30 });
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index ae99eefe3..3b9b9e8b4 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -133,7 +133,7 @@ mod tracing_setup {
133} 133}
134 134
135fn run_server() -> Result<()> { 135fn run_server() -> Result<()> {
136 log::info!("server will start"); 136 log::info!("server version {} will start", env!("REV"));
137 137
138 let (connection, io_threads) = Connection::stdio(); 138 let (connection, io_threads) = Connection::stdio();
139 139
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
index f999848a7..c847bbb35 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
@@ -13,16 +13,16 @@
13 diagnostic: Diagnostic { 13 diagnostic: Diagnostic {
14 range: Range { 14 range: Range {
15 start: Position { 15 start: Position {
16 line: 264, 16 line: 271,
17 character: 8, 17 character: 8,
18 }, 18 },
19 end: Position { 19 end: Position {
20 line: 264, 20 line: 271,
21 character: 76, 21 character: 50,
22 }, 22 },
23 }, 23 },
24 severity: Some( 24 severity: Some(
25 Error, 25 Hint,
26 ), 26 ),
27 code: None, 27 code: None,
28 code_description: None, 28 code_description: None,
@@ -40,18 +40,18 @@
40 password: None, 40 password: None,
41 host: None, 41 host: None,
42 port: None, 42 port: None,
43 path: "/test/crates/hir_def/src/data.rs", 43 path: "/test/crates/hir_def/src/path.rs",
44 query: None, 44 query: None,
45 fragment: None, 45 fragment: None,
46 }, 46 },
47 range: Range { 47 range: Range {
48 start: Position { 48 start: Position {
49 line: 79, 49 line: 264,
50 character: 15, 50 character: 8,
51 }, 51 },
52 end: Position { 52 end: Position {
53 line: 79, 53 line: 264,
54 character: 41, 54 character: 76,
55 }, 55 },
56 }, 56 },
57 }, 57 },
@@ -87,6 +87,71 @@
87 }, 87 },
88 }, 88 },
89 severity: Some( 89 severity: Some(
90 Hint,
91 ),
92 code: None,
93 code_description: None,
94 source: Some(
95 "rustc",
96 ),
97 message: "Please register your known path in the path module",
98 related_information: Some(
99 [
100 DiagnosticRelatedInformation {
101 location: Location {
102 uri: Url {
103 scheme: "file",
104 username: "",
105 password: None,
106 host: None,
107 port: None,
108 path: "/test/crates/hir_def/src/path.rs",
109 query: None,
110 fragment: None,
111 },
112 range: Range {
113 start: Position {
114 line: 264,
115 character: 8,
116 },
117 end: Position {
118 line: 264,
119 character: 76,
120 },
121 },
122 },
123 message: "Exact error occurred here",
124 },
125 ],
126 ),
127 tags: None,
128 data: None,
129 },
130 fixes: [],
131 },
132 MappedRustDiagnostic {
133 url: Url {
134 scheme: "file",
135 username: "",
136 password: None,
137 host: None,
138 port: None,
139 path: "/test/crates/hir_def/src/path.rs",
140 query: None,
141 fragment: None,
142 },
143 diagnostic: Diagnostic {
144 range: Range {
145 start: Position {
146 line: 264,
147 character: 8,
148 },
149 end: Position {
150 line: 264,
151 character: 76,
152 },
153 },
154 severity: Some(
90 Error, 155 Error,
91 ), 156 ),
92 code: None, 157 code: None,
@@ -95,7 +160,60 @@
95 "rustc", 160 "rustc",
96 ), 161 ),
97 message: "Please register your known path in the path module", 162 message: "Please register your known path in the path module",
98 related_information: None, 163 related_information: Some(
164 [
165 DiagnosticRelatedInformation {
166 location: Location {
167 uri: Url {
168 scheme: "file",
169 username: "",
170 password: None,
171 host: None,
172 port: None,
173 path: "/test/crates/hir_def/src/path.rs",
174 query: None,
175 fragment: None,
176 },
177 range: Range {
178 start: Position {
179 line: 271,
180 character: 8,
181 },
182 end: Position {
183 line: 271,
184 character: 50,
185 },
186 },
187 },
188 message: "Error originated from macro call here",
189 },
190 DiagnosticRelatedInformation {
191 location: Location {
192 uri: Url {
193 scheme: "file",
194 username: "",
195 password: None,
196 host: None,
197 port: None,
198 path: "/test/crates/hir_def/src/data.rs",
199 query: None,
200 fragment: None,
201 },
202 range: Range {
203 start: Position {
204 line: 79,
205 character: 15,
206 },
207 end: Position {
208 line: 79,
209 character: 41,
210 },
211 },
212 },
213 message: "Error originated from macro call here",
214 },
215 ],
216 ),
99 tags: None, 217 tags: None,
100 data: None, 218 data: None,
101 }, 219 },
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 76994de71..e2f319f6b 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -34,22 +34,14 @@ fn diagnostic_severity(
34 Some(res) 34 Some(res)
35} 35}
36 36
37/// Check whether a file name is from macro invocation 37/// Checks whether a file name is from macro invocation and does not refer to an actual file.
38fn is_from_macro(file_name: &str) -> bool { 38fn is_dummy_macro_file(file_name: &str) -> bool {
39 // FIXME: current rustc does not seem to emit `<macro file>` files anymore?
39 file_name.starts_with('<') && file_name.ends_with('>') 40 file_name.starts_with('<') && file_name.ends_with('>')
40} 41}
41 42
42/// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary
43fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
44 let mut span = span.clone();
45 while let Some(expansion) = span.expansion {
46 span = expansion.span;
47 }
48 return location_naive(workspace_root, &span);
49}
50
51/// Converts a Rust span to a LSP location 43/// Converts a Rust span to a LSP location
52fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { 44fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
53 let file_name = workspace_root.join(&span.file_name); 45 let file_name = workspace_root.join(&span.file_name);
54 let uri = url_from_abs_path(&file_name); 46 let uri = url_from_abs_path(&file_name);
55 47
@@ -62,7 +54,25 @@ fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Lo
62 lsp_types::Location { uri, range } 54 lsp_types::Location { uri, range }
63} 55}
64 56
65/// Converts a secondary Rust span to a LSP related inflocation(ormation 57/// Extracts a suitable "primary" location from a rustc diagnostic.
58///
59/// This takes locations pointing into the standard library, or generally outside the current
60/// workspace into account and tries to avoid those, in case macros are involved.
61fn primary_location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
62 let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span));
63 for span in span_stack.clone() {
64 let abs_path = workspace_root.join(&span.file_name);
65 if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) {
66 return location(workspace_root, span);
67 }
68 }
69
70 // Fall back to the outermost macro invocation if no suitable span comes up.
71 let last_span = span_stack.last().unwrap();
72 location(workspace_root, last_span)
73}
74
75/// Converts a secondary Rust span to a LSP related information
66/// 76///
67/// If the span is unlabelled this will return `None`. 77/// If the span is unlabelled this will return `None`.
68fn diagnostic_related_information( 78fn diagnostic_related_information(
@@ -231,7 +241,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
231 primary_spans 241 primary_spans
232 .iter() 242 .iter()
233 .flat_map(|primary_span| { 243 .flat_map(|primary_span| {
234 let location = location(workspace_root, &primary_span); 244 let primary_location = primary_location(workspace_root, &primary_span);
235 245
236 let mut message = message.clone(); 246 let mut message = message.clone();
237 if needs_primary_span_label { 247 if needs_primary_span_label {
@@ -243,31 +253,47 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
243 // Each primary diagnostic span may result in multiple LSP diagnostics. 253 // Each primary diagnostic span may result in multiple LSP diagnostics.
244 let mut diagnostics = Vec::new(); 254 let mut diagnostics = Vec::new();
245 255
246 let mut related_macro_info = None; 256 let mut related_info_macro_calls = vec![];
247 257
248 // If error occurs from macro expansion, add related info pointing to 258 // If error occurs from macro expansion, add related info pointing to
249 // where the error originated 259 // where the error originated
250 // Also, we would generate an additional diagnostic, so that exact place of macro 260 // Also, we would generate an additional diagnostic, so that exact place of macro
251 // will be highlighted in the error origin place. 261 // will be highlighted in the error origin place.
252 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() { 262 let span_stack = std::iter::successors(Some(*primary_span), |span| {
253 let in_macro_location = location_naive(workspace_root, &primary_span); 263 Some(&span.expansion.as_ref()?.span)
264 });
265 for (i, span) in span_stack.enumerate() {
266 if is_dummy_macro_file(&span.file_name) {
267 continue;
268 }
254 269
255 // Add related information for the main disagnostic. 270 // First span is the original diagnostic, others are macro call locations that
256 related_macro_info = Some(lsp_types::DiagnosticRelatedInformation { 271 // generated that code.
257 location: in_macro_location.clone(), 272 let is_in_macro_call = i != 0;
258 message: "Error originated from macro here".to_string(),
259 });
260 273
274 let secondary_location = location(workspace_root, &span);
275 if secondary_location == primary_location {
276 continue;
277 }
278 related_info_macro_calls.push(lsp_types::DiagnosticRelatedInformation {
279 location: secondary_location.clone(),
280 message: if is_in_macro_call {
281 "Error originated from macro call here".to_string()
282 } else {
283 "Actual error occurred here".to_string()
284 },
285 });
261 // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code. 286 // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code.
262 let information_for_additional_diagnostic = 287 let information_for_additional_diagnostic =
263 vec![lsp_types::DiagnosticRelatedInformation { 288 vec![lsp_types::DiagnosticRelatedInformation {
264 location: location.clone(), 289 location: primary_location.clone(),
265 message: "Exact error occurred here".to_string(), 290 message: "Exact error occurred here".to_string(),
266 }]; 291 }];
267 292
268 let diagnostic = lsp_types::Diagnostic { 293 let diagnostic = lsp_types::Diagnostic {
269 range: in_macro_location.range, 294 range: secondary_location.range,
270 severity, 295 // downgrade to hint if we're pointing at the macro
296 severity: Some(lsp_types::DiagnosticSeverity::Hint),
271 code: code.clone().map(lsp_types::NumberOrString::String), 297 code: code.clone().map(lsp_types::NumberOrString::String),
272 code_description: code_description.clone(), 298 code_description: code_description.clone(),
273 source: Some(source.clone()), 299 source: Some(source.clone()),
@@ -276,9 +302,8 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
276 tags: if tags.is_empty() { None } else { Some(tags.clone()) }, 302 tags: if tags.is_empty() { None } else { Some(tags.clone()) },
277 data: None, 303 data: None,
278 }; 304 };
279
280 diagnostics.push(MappedRustDiagnostic { 305 diagnostics.push(MappedRustDiagnostic {
281 url: in_macro_location.uri, 306 url: secondary_location.uri,
282 diagnostic, 307 diagnostic,
283 fixes: Vec::new(), 308 fixes: Vec::new(),
284 }); 309 });
@@ -286,23 +311,25 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
286 311
287 // Emit the primary diagnostic. 312 // Emit the primary diagnostic.
288 diagnostics.push(MappedRustDiagnostic { 313 diagnostics.push(MappedRustDiagnostic {
289 url: location.uri.clone(), 314 url: primary_location.uri.clone(),
290 diagnostic: lsp_types::Diagnostic { 315 diagnostic: lsp_types::Diagnostic {
291 range: location.range, 316 range: primary_location.range,
292 severity, 317 severity,
293 code: code.clone().map(lsp_types::NumberOrString::String), 318 code: code.clone().map(lsp_types::NumberOrString::String),
294 code_description: code_description.clone(), 319 code_description: code_description.clone(),
295 source: Some(source.clone()), 320 source: Some(source.clone()),
296 message, 321 message,
297 related_information: if subdiagnostics.is_empty() { 322 related_information: {
298 None 323 let info = related_info_macro_calls
299 } else {
300 let mut related = subdiagnostics
301 .iter() 324 .iter()
302 .map(|sub| sub.related.clone()) 325 .cloned()
326 .chain(subdiagnostics.iter().map(|sub| sub.related.clone()))
303 .collect::<Vec<_>>(); 327 .collect::<Vec<_>>();
304 related.extend(related_macro_info); 328 if info.is_empty() {
305 Some(related) 329 None
330 } else {
331 Some(info)
332 }
306 }, 333 },
307 tags: if tags.is_empty() { None } else { Some(tags.clone()) }, 334 tags: if tags.is_empty() { None } else { Some(tags.clone()) },
308 data: None, 335 data: None,
@@ -314,7 +341,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
314 // This is useful because they will show up in the user's editor, unlike 341 // This is useful because they will show up in the user's editor, unlike
315 // `related_information`, which just produces hard-to-read links, at least in VS Code. 342 // `related_information`, which just produces hard-to-read links, at least in VS Code.
316 let back_ref = lsp_types::DiagnosticRelatedInformation { 343 let back_ref = lsp_types::DiagnosticRelatedInformation {
317 location, 344 location: primary_location,
318 message: "original diagnostic".to_string(), 345 message: "original diagnostic".to_string(),
319 }; 346 };
320 for sub in &subdiagnostics { 347 for sub in &subdiagnostics {
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 85e67554c..53d29ddfc 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -84,7 +84,8 @@ pub(crate) fn handle_analyzer_status(
84 84
85pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> Result<String> { 85pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> Result<String> {
86 let _p = profile::span("handle_memory_usage"); 86 let _p = profile::span("handle_memory_usage");
87 let mem = state.analysis_host.per_query_memory_usage(); 87 let mut mem = state.analysis_host.per_query_memory_usage();
88 mem.push(("Remaining".into(), profile::memory_usage().allocated));
88 89
89 let mut out = String::new(); 90 let mut out = String::new();
90 for (name, bytes) in mem { 91 for (name, bytes) in mem {
diff --git a/docs/dev/style.md b/docs/dev/style.md
index e4a1672ca..c594946be 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -55,9 +55,9 @@ https://www.tedinski.com/2018/02/06/system-boundaries.html
55We try to be very conservative with usage of crates.io dependencies. 55We try to be very conservative with usage of crates.io dependencies.
56Don't use small "helper" crates (exception: `itertools` is allowed). 56Don't use small "helper" crates (exception: `itertools` is allowed).
57If there's some general reusable bit of code you need, consider adding it to the `stdx` crate. 57If there's some general reusable bit of code you need, consider adding it to the `stdx` crate.
58A useful exercise is to read Cargo.lock and see if some of the *transitive* dependencies do not make sense for rust-analyzer.
58 59
59**Rationale:** keep compile times low, create ecosystem pressure for faster 60**Rationale:** keep compile times low, create ecosystem pressure for faster compiles, reduce the number of things which might break.
60compiles, reduce the number of things which might break.
61 61
62## Commit Style 62## Commit Style
63 63
@@ -806,9 +806,48 @@ if let Some(expected_type) = ctx.expected_type.as_ref() {
806} 806}
807``` 807```
808 808
809**Rational:** `match` is almost always more compact. 809**Rationale:** `match` is almost always more compact.
810The `else` branch can get a more precise pattern: `None` or `Err(_)` instead of `_`. 810The `else` branch can get a more precise pattern: `None` or `Err(_)` instead of `_`.
811 811
812## Helper Functions
813
814Avoid creating singe-use helper functions:
815
816```rust
817// GOOD
818let buf = {
819 let mut buf = get_empty_buf(&mut arena);
820 buf.add_item(item);
821 buf
822};
823
824// BAD
825
826let buf = prepare_buf(&mut arena, item);
827
828...
829
830fn prepare_buf(arena: &mut Arena, item: Item) -> ItemBuf {
831 let mut res = get_empty_buf(&mut arena);
832 res.add_item(item);
833 res
834}
835```
836
837Exception: if you want to make use of `return` or `?`.
838
839**Rationale:** single-use functions change frequently, adding or removing parameters adds churn.
840A block serves just as well to delineate a bit of logic, but has access to all the context.
841Re-using originally single-purpose function often leads to bad coupling.
842
843## Helper Variables
844
845Introduce helper variables freely, especially for multiline conditions.
846
847**Rationale:** like blocks, single-use variables are a cognitively cheap abstraction, as they have access to all the context.
848Extra variables help during debugging, they make it easy to print/view important intermediate results.
849Giving a name to a condition in `if` expression often improves clarity and leads to a nicer formatted code.
850
812## Token names 851## Token names
813 852
814Use `T![foo]` instead of `SyntaxKind::FOO_KW`. 853Use `T![foo]` instead of `SyntaxKind::FOO_KW`.