diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/hir/src/semantics.rs | 6 | ||||
-rw-r--r-- | crates/hir/src/source_analyzer.rs | 30 | ||||
-rw-r--r-- | crates/hir_ty/src/infer.rs | 20 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/expr.rs | 6 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/path.rs | 2 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/unify.rs | 8 | ||||
-rw-r--r-- | crates/hir_ty/src/lower.rs | 71 | ||||
-rw-r--r-- | crates/ide/src/hover.rs | 63 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/qualified_path.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/bin/main.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt | 138 | ||||
-rw-r--r-- | crates/rust-analyzer/src/diagnostics/to_proto.rs | 101 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 3 |
13 files changed, 352 insertions, 100 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::{ | |||
20 | use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; | 20 | use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; |
21 | use hir_ty::{ | 21 | use 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 | }; |
25 | use syntax::{ | 25 | use 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 { | |||
231 | pub(crate) struct InferenceTable { | 231 | pub(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 | ||
236 | impl InferenceTable { | 237 | impl 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#" | ||
3842 | fn foo<T: A>() where T::Assoc$0: {} | ||
3843 | |||
3844 | trait 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#" | ||
3861 | fn foo<T: A>() { | ||
3862 | let _: <T>::Assoc$0; | ||
3863 | } | ||
3864 | |||
3865 | trait 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#" | ||
3882 | trait 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 | ||
135 | fn run_server() -> Result<()> { | 135 | fn 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. |
38 | fn is_from_macro(file_name: &str) -> bool { | 38 | fn 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 | ||
43 | fn 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 |
52 | fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { | 44 | fn 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. | ||
61 | fn 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`. |
68 | fn diagnostic_related_information( | 78 | fn 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 | ||
85 | pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> Result<String> { | 85 | pub(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 { |