diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/src/code_model.rs | 10 | ||||
-rw-r--r-- | crates/ra_hir/src/semantics.rs | 42 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/lib.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/lower.rs | 143 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_qualified_path.rs | 272 | ||||
-rw-r--r-- | crates/ra_ide/src/display/function_signature.rs | 40 | ||||
-rw-r--r-- | crates/ra_ide/src/hover.rs | 25 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/generated/nodes.rs | 1 | ||||
-rw-r--r-- | crates/ra_syntax/src/validation.rs | 39 | ||||
-rw-r--r-- | crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast | 76 | ||||
-rw-r--r-- | crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs | 4 |
11 files changed, 587 insertions, 68 deletions
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 3fb419571..af59aa1b6 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -953,6 +953,16 @@ impl TypeParam { | |||
953 | pub fn module(self, db: &dyn HirDatabase) -> Module { | 953 | pub fn module(self, db: &dyn HirDatabase) -> Module { |
954 | self.id.parent.module(db.upcast()).into() | 954 | self.id.parent.module(db.upcast()).into() |
955 | } | 955 | } |
956 | |||
957 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
958 | let resolver = self.id.parent.resolver(db.upcast()); | ||
959 | let environment = TraitEnvironment::lower(db, &resolver); | ||
960 | let ty = Ty::Placeholder(self.id); | ||
961 | Type { | ||
962 | krate: self.id.parent.module(db.upcast()).krate, | ||
963 | ty: InEnvironment { value: ty, environment }, | ||
964 | } | ||
965 | } | ||
956 | } | 966 | } |
957 | 967 | ||
958 | // FIXME: rename from `ImplDef` to `Impl` | 968 | // FIXME: rename from `ImplDef` to `Impl` |
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 86bfb416c..a0a0f234b 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -9,6 +9,7 @@ use hir_def::{ | |||
9 | AsMacroCall, TraitId, | 9 | AsMacroCall, TraitId, |
10 | }; | 10 | }; |
11 | use hir_expand::ExpansionInfo; | 11 | use hir_expand::ExpansionInfo; |
12 | use hir_ty::associated_type_shorthand_candidates; | ||
12 | use itertools::Itertools; | 13 | use itertools::Itertools; |
13 | use ra_db::{FileId, FileRange}; | 14 | use ra_db::{FileId, FileRange}; |
14 | use ra_prof::profile; | 15 | use ra_prof::profile; |
@@ -24,8 +25,9 @@ use crate::{ | |||
24 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, | 25 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, |
25 | source_analyzer::{resolve_hir_path, SourceAnalyzer}, | 26 | source_analyzer::{resolve_hir_path, SourceAnalyzer}, |
26 | AssocItem, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, | 27 | AssocItem, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, |
27 | Name, Origin, Path, ScopeDef, Trait, Type, TypeParam, | 28 | Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, |
28 | }; | 29 | }; |
30 | use resolver::TypeNs; | ||
29 | 31 | ||
30 | #[derive(Debug, Clone, PartialEq, Eq)] | 32 | #[derive(Debug, Clone, PartialEq, Eq)] |
31 | pub enum PathResolution { | 33 | pub enum PathResolution { |
@@ -40,6 +42,44 @@ pub enum PathResolution { | |||
40 | AssocItem(AssocItem), | 42 | AssocItem(AssocItem), |
41 | } | 43 | } |
42 | 44 | ||
45 | impl PathResolution { | ||
46 | fn in_type_ns(&self) -> Option<TypeNs> { | ||
47 | match self { | ||
48 | PathResolution::Def(ModuleDef::Adt(adt)) => Some(TypeNs::AdtId((*adt).into())), | ||
49 | PathResolution::Def(ModuleDef::BuiltinType(builtin)) => { | ||
50 | Some(TypeNs::BuiltinType(*builtin)) | ||
51 | } | ||
52 | PathResolution::Def(ModuleDef::Const(_)) | ||
53 | | PathResolution::Def(ModuleDef::EnumVariant(_)) | ||
54 | | PathResolution::Def(ModuleDef::Function(_)) | ||
55 | | PathResolution::Def(ModuleDef::Module(_)) | ||
56 | | PathResolution::Def(ModuleDef::Static(_)) | ||
57 | | PathResolution::Def(ModuleDef::Trait(_)) => None, | ||
58 | PathResolution::Def(ModuleDef::TypeAlias(alias)) => { | ||
59 | Some(TypeNs::TypeAliasId((*alias).into())) | ||
60 | } | ||
61 | PathResolution::Local(_) | PathResolution::Macro(_) => None, | ||
62 | PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())), | ||
63 | PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())), | ||
64 | PathResolution::AssocItem(AssocItem::Const(_)) | ||
65 | | PathResolution::AssocItem(AssocItem::Function(_)) => None, | ||
66 | PathResolution::AssocItem(AssocItem::TypeAlias(alias)) => { | ||
67 | Some(TypeNs::TypeAliasId((*alias).into())) | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | |||
72 | /// Returns an iterator over associated types that may be specified after this path (using | ||
73 | /// `Ty::Assoc` syntax). | ||
74 | pub fn assoc_type_shorthand_candidates<R>( | ||
75 | &self, | ||
76 | db: &dyn HirDatabase, | ||
77 | mut cb: impl FnMut(TypeAlias) -> Option<R>, | ||
78 | ) -> Option<R> { | ||
79 | associated_type_shorthand_candidates(db, self.in_type_ns()?, |_, _, id| cb(id.into())) | ||
80 | } | ||
81 | } | ||
82 | |||
43 | /// Primary API to get semantic information, like types, from syntax trees. | 83 | /// Primary API to get semantic information, like types, from syntax trees. |
44 | pub struct Semantics<'db, DB> { | 84 | pub struct Semantics<'db, DB> { |
45 | pub db: &'db DB, | 85 | pub db: &'db DB, |
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index a8ef32ec5..a6f56c661 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -66,7 +66,8 @@ pub use autoderef::autoderef; | |||
66 | pub use infer::{InferTy, InferenceResult}; | 66 | pub use infer::{InferTy, InferenceResult}; |
67 | pub use lower::CallableDef; | 67 | pub use lower::CallableDef; |
68 | pub use lower::{ | 68 | pub use lower::{ |
69 | callable_item_sig, ImplTraitLoweringMode, TyDefId, TyLoweringContext, ValueTyDefId, | 69 | associated_type_shorthand_candidates, callable_item_sig, ImplTraitLoweringMode, TyDefId, |
70 | TyLoweringContext, ValueTyDefId, | ||
70 | }; | 71 | }; |
71 | pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; | 72 | pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; |
72 | 73 | ||
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index a6f893037..9ad6dbe07 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs | |||
@@ -17,9 +17,9 @@ use hir_def::{ | |||
17 | path::{GenericArg, Path, PathSegment, PathSegments}, | 17 | path::{GenericArg, Path, PathSegment, PathSegments}, |
18 | resolver::{HasResolver, Resolver, TypeNs}, | 18 | resolver::{HasResolver, Resolver, TypeNs}, |
19 | type_ref::{TypeBound, TypeRef}, | 19 | type_ref::{TypeBound, TypeRef}, |
20 | AdtId, AssocContainerId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule, | 20 | AdtId, AssocContainerId, AssocItemId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, |
21 | ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, | 21 | HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, |
22 | VariantId, | 22 | UnionId, VariantId, |
23 | }; | 23 | }; |
24 | use ra_arena::map::ArenaMap; | 24 | use ra_arena::map::ArenaMap; |
25 | use ra_db::CrateId; | 25 | use ra_db::CrateId; |
@@ -34,6 +34,7 @@ use crate::{ | |||
34 | Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate, | 34 | Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate, |
35 | ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, | 35 | ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, |
36 | }; | 36 | }; |
37 | use hir_expand::name::Name; | ||
37 | 38 | ||
38 | #[derive(Debug)] | 39 | #[derive(Debug)] |
39 | pub struct TyLoweringContext<'a> { | 40 | pub struct TyLoweringContext<'a> { |
@@ -383,61 +384,38 @@ impl Ty { | |||
383 | res: Option<TypeNs>, | 384 | res: Option<TypeNs>, |
384 | segment: PathSegment<'_>, | 385 | segment: PathSegment<'_>, |
385 | ) -> Ty { | 386 | ) -> Ty { |
386 | let traits_from_env: Vec<_> = match res { | 387 | if let Some(res) = res { |
387 | Some(TypeNs::SelfType(impl_id)) => match ctx.db.impl_trait(impl_id) { | 388 | let ty = |
388 | None => return Ty::Unknown, | 389 | associated_type_shorthand_candidates(ctx.db, res, move |name, t, associated_ty| { |
389 | Some(trait_ref) => vec![trait_ref.value], | 390 | if name == segment.name { |
390 | }, | 391 | let substs = match ctx.type_param_mode { |
391 | Some(TypeNs::GenericParam(param_id)) => { | 392 | TypeParamLoweringMode::Placeholder => { |
392 | let predicates = ctx.db.generic_predicates_for_param(param_id); | 393 | // if we're lowering to placeholders, we have to put |
393 | let mut traits_: Vec<_> = predicates | 394 | // them in now |
394 | .iter() | 395 | let s = Substs::type_params( |
395 | .filter_map(|pred| match &pred.value { | 396 | ctx.db, |
396 | GenericPredicate::Implemented(tr) => Some(tr.clone()), | 397 | ctx.resolver.generic_def().expect( |
397 | _ => None, | 398 | "there should be generics if there's a generic param", |
398 | }) | 399 | ), |
399 | .collect(); | 400 | ); |
400 | // Handle `Self::Type` referring to own associated type in trait definitions | 401 | t.substs.clone().subst_bound_vars(&s) |
401 | if let GenericDefId::TraitId(trait_id) = param_id.parent { | 402 | } |
402 | let generics = generics(ctx.db.upcast(), trait_id.into()); | 403 | TypeParamLoweringMode::Variable => t.substs.clone(), |
403 | if generics.params.types[param_id.local_id].provenance | ||
404 | == TypeParamProvenance::TraitSelf | ||
405 | { | ||
406 | let trait_ref = TraitRef { | ||
407 | trait_: trait_id, | ||
408 | substs: Substs::bound_vars(&generics, DebruijnIndex::INNERMOST), | ||
409 | }; | 404 | }; |
410 | traits_.push(trait_ref); | 405 | // FIXME handle type parameters on the segment |
406 | return Some(Ty::Projection(ProjectionTy { | ||
407 | associated_ty, | ||
408 | parameters: substs, | ||
409 | })); | ||
411 | } | 410 | } |
412 | } | 411 | |
413 | traits_ | 412 | None |
414 | } | 413 | }); |
415 | _ => return Ty::Unknown, | 414 | |
416 | }; | 415 | ty.unwrap_or(Ty::Unknown) |
417 | let traits = traits_from_env.into_iter().flat_map(|t| all_super_trait_refs(ctx.db, t)); | 416 | } else { |
418 | for t in traits { | 417 | Ty::Unknown |
419 | if let Some(associated_ty) = | ||
420 | ctx.db.trait_data(t.trait_).associated_type_by_name(&segment.name) | ||
421 | { | ||
422 | let substs = match ctx.type_param_mode { | ||
423 | TypeParamLoweringMode::Placeholder => { | ||
424 | // if we're lowering to placeholders, we have to put | ||
425 | // them in now | ||
426 | let s = Substs::type_params( | ||
427 | ctx.db, | ||
428 | ctx.resolver | ||
429 | .generic_def() | ||
430 | .expect("there should be generics if there's a generic param"), | ||
431 | ); | ||
432 | t.substs.subst_bound_vars(&s) | ||
433 | } | ||
434 | TypeParamLoweringMode::Variable => t.substs, | ||
435 | }; | ||
436 | // FIXME handle (forbid) type parameters on the segment | ||
437 | return Ty::Projection(ProjectionTy { associated_ty, parameters: substs }); | ||
438 | } | ||
439 | } | 418 | } |
440 | Ty::Unknown | ||
441 | } | 419 | } |
442 | 420 | ||
443 | fn from_hir_path_inner( | 421 | fn from_hir_path_inner( |
@@ -694,6 +672,61 @@ pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> PolyFnSig { | |||
694 | } | 672 | } |
695 | } | 673 | } |
696 | 674 | ||
675 | pub fn associated_type_shorthand_candidates<R>( | ||
676 | db: &dyn HirDatabase, | ||
677 | res: TypeNs, | ||
678 | mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>, | ||
679 | ) -> Option<R> { | ||
680 | let traits_from_env: Vec<_> = match res { | ||
681 | TypeNs::SelfType(impl_id) => match db.impl_trait(impl_id) { | ||
682 | None => vec![], | ||
683 | Some(trait_ref) => vec![trait_ref.value], | ||
684 | }, | ||
685 | TypeNs::GenericParam(param_id) => { | ||
686 | let predicates = db.generic_predicates_for_param(param_id); | ||
687 | let mut traits_: Vec<_> = predicates | ||
688 | .iter() | ||
689 | .filter_map(|pred| match &pred.value { | ||
690 | GenericPredicate::Implemented(tr) => Some(tr.clone()), | ||
691 | _ => None, | ||
692 | }) | ||
693 | .collect(); | ||
694 | // Handle `Self::Type` referring to own associated type in trait definitions | ||
695 | if let GenericDefId::TraitId(trait_id) = param_id.parent { | ||
696 | let generics = generics(db.upcast(), trait_id.into()); | ||
697 | if generics.params.types[param_id.local_id].provenance | ||
698 | == TypeParamProvenance::TraitSelf | ||
699 | { | ||
700 | let trait_ref = TraitRef { | ||
701 | trait_: trait_id, | ||
702 | substs: Substs::bound_vars(&generics, DebruijnIndex::INNERMOST), | ||
703 | }; | ||
704 | traits_.push(trait_ref); | ||
705 | } | ||
706 | } | ||
707 | traits_ | ||
708 | } | ||
709 | _ => vec![], | ||
710 | }; | ||
711 | |||
712 | for t in traits_from_env.into_iter().flat_map(move |t| all_super_trait_refs(db, t)) { | ||
713 | let data = db.trait_data(t.trait_); | ||
714 | |||
715 | for (name, assoc_id) in &data.items { | ||
716 | match assoc_id { | ||
717 | AssocItemId::TypeAliasId(alias) => { | ||
718 | if let Some(result) = cb(name, &t, *alias) { | ||
719 | return Some(result); | ||
720 | } | ||
721 | } | ||
722 | AssocItemId::FunctionId(_) | AssocItemId::ConstId(_) => {} | ||
723 | } | ||
724 | } | ||
725 | } | ||
726 | |||
727 | None | ||
728 | } | ||
729 | |||
697 | /// Build the type of all specific fields of a struct or enum variant. | 730 | /// Build the type of all specific fields of a struct or enum variant. |
698 | pub(crate) fn field_types_query( | 731 | pub(crate) fn field_types_query( |
699 | db: &dyn HirDatabase, | 732 | db: &dyn HirDatabase, |
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs index dd10f74e6..aa56a5cd8 100644 --- a/crates/ra_ide/src/completion/complete_qualified_path.rs +++ b/crates/ra_ide/src/completion/complete_qualified_path.rs | |||
@@ -5,19 +5,29 @@ use ra_syntax::AstNode; | |||
5 | use test_utils::tested_by; | 5 | use test_utils::tested_by; |
6 | 6 | ||
7 | use crate::completion::{CompletionContext, Completions}; | 7 | use crate::completion::{CompletionContext, Completions}; |
8 | use rustc_hash::FxHashSet; | ||
8 | 9 | ||
9 | pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 10 | pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { |
10 | let path = match &ctx.path_prefix { | 11 | let path = match &ctx.path_prefix { |
11 | Some(path) => path.clone(), | 12 | Some(path) => path.clone(), |
12 | _ => return, | 13 | _ => return, |
13 | }; | 14 | }; |
14 | let def = match ctx.scope().resolve_hir_path(&path) { | 15 | let scope = ctx.scope(); |
15 | Some(PathResolution::Def(def)) => def, | 16 | let context_module = scope.module(); |
16 | _ => return, | 17 | |
18 | let res = match scope.resolve_hir_path(&path) { | ||
19 | Some(res) => res, | ||
20 | None => return, | ||
17 | }; | 21 | }; |
18 | let context_module = ctx.scope().module(); | 22 | |
19 | match def { | 23 | // Add associated types on type parameters and `Self`. |
20 | hir::ModuleDef::Module(module) => { | 24 | res.assoc_type_shorthand_candidates(ctx.db, |alias| { |
25 | acc.add_type_alias(ctx, alias); | ||
26 | None::<()> | ||
27 | }); | ||
28 | |||
29 | match res { | ||
30 | PathResolution::Def(hir::ModuleDef::Module(module)) => { | ||
21 | let module_scope = module.scope(ctx.db, context_module); | 31 | let module_scope = module.scope(ctx.db, context_module); |
22 | for (name, def) in module_scope { | 32 | for (name, def) in module_scope { |
23 | if ctx.use_item_syntax.is_some() { | 33 | if ctx.use_item_syntax.is_some() { |
@@ -35,7 +45,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
35 | acc.add_resolution(ctx, name.to_string(), &def); | 45 | acc.add_resolution(ctx, name.to_string(), &def); |
36 | } | 46 | } |
37 | } | 47 | } |
38 | hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => { | 48 | PathResolution::Def(def @ hir::ModuleDef::Adt(_)) |
49 | | PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) => { | ||
39 | if let hir::ModuleDef::Adt(Adt::Enum(e)) = def { | 50 | if let hir::ModuleDef::Adt(Adt::Enum(e)) = def { |
40 | for variant in e.variants(ctx.db) { | 51 | for variant in e.variants(ctx.db) { |
41 | acc.add_enum_variant(ctx, variant, None); | 52 | acc.add_enum_variant(ctx, variant, None); |
@@ -46,8 +57,10 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
46 | hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), | 57 | hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), |
47 | _ => unreachable!(), | 58 | _ => unreachable!(), |
48 | }; | 59 | }; |
49 | // Iterate assoc types separately | 60 | |
50 | // FIXME: complete T::AssocType | 61 | // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType. |
62 | // (where AssocType is defined on a trait, not an inherent impl) | ||
63 | |||
51 | let krate = ctx.krate; | 64 | let krate = ctx.krate; |
52 | if let Some(krate) = krate { | 65 | if let Some(krate) = krate { |
53 | let traits_in_scope = ctx.scope().traits_in_scope(); | 66 | let traits_in_scope = ctx.scope().traits_in_scope(); |
@@ -65,6 +78,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
65 | None::<()> | 78 | None::<()> |
66 | }); | 79 | }); |
67 | 80 | ||
81 | // Iterate assoc types separately | ||
68 | ty.iterate_impl_items(ctx.db, krate, |item| { | 82 | ty.iterate_impl_items(ctx.db, krate, |item| { |
69 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | 83 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { |
70 | return None; | 84 | return None; |
@@ -77,7 +91,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
77 | }); | 91 | }); |
78 | } | 92 | } |
79 | } | 93 | } |
80 | hir::ModuleDef::Trait(t) => { | 94 | PathResolution::Def(hir::ModuleDef::Trait(t)) => { |
95 | // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`. | ||
81 | for item in t.items(ctx.db) { | 96 | for item in t.items(ctx.db) { |
82 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | 97 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { |
83 | continue; | 98 | continue; |
@@ -91,8 +106,38 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
91 | } | 106 | } |
92 | } | 107 | } |
93 | } | 108 | } |
109 | PathResolution::TypeParam(_) | PathResolution::SelfType(_) => { | ||
110 | if let Some(krate) = ctx.krate { | ||
111 | let ty = match res { | ||
112 | PathResolution::TypeParam(param) => param.ty(ctx.db), | ||
113 | PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db), | ||
114 | _ => return, | ||
115 | }; | ||
116 | |||
117 | let traits_in_scope = ctx.scope().traits_in_scope(); | ||
118 | let mut seen = FxHashSet::default(); | ||
119 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { | ||
120 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | ||
121 | return None; | ||
122 | } | ||
123 | |||
124 | // We might iterate candidates of a trait multiple times here, so deduplicate | ||
125 | // them. | ||
126 | if seen.insert(item) { | ||
127 | match item { | ||
128 | hir::AssocItem::Function(func) => { | ||
129 | acc.add_function(ctx, func, None); | ||
130 | } | ||
131 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), | ||
132 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
133 | } | ||
134 | } | ||
135 | None::<()> | ||
136 | }); | ||
137 | } | ||
138 | } | ||
94 | _ => {} | 139 | _ => {} |
95 | }; | 140 | } |
96 | } | 141 | } |
97 | 142 | ||
98 | #[cfg(test)] | 143 | #[cfg(test)] |
@@ -844,6 +889,211 @@ mod tests { | |||
844 | } | 889 | } |
845 | 890 | ||
846 | #[test] | 891 | #[test] |
892 | fn completes_ty_param_assoc_ty() { | ||
893 | assert_debug_snapshot!( | ||
894 | do_reference_completion( | ||
895 | " | ||
896 | //- /lib.rs | ||
897 | trait Super { | ||
898 | type Ty; | ||
899 | const CONST: u8; | ||
900 | fn func() {} | ||
901 | fn method(&self) {} | ||
902 | } | ||
903 | |||
904 | trait Sub: Super { | ||
905 | type SubTy; | ||
906 | const C2: (); | ||
907 | fn subfunc() {} | ||
908 | fn submethod(&self) {} | ||
909 | } | ||
910 | |||
911 | fn foo<T: Sub>() { | ||
912 | T::<|> | ||
913 | } | ||
914 | " | ||
915 | ), | ||
916 | @r###" | ||
917 | [ | ||
918 | CompletionItem { | ||
919 | label: "C2", | ||
920 | source_range: 219..219, | ||
921 | delete: 219..219, | ||
922 | insert: "C2", | ||
923 | kind: Const, | ||
924 | detail: "const C2: ();", | ||
925 | }, | ||
926 | CompletionItem { | ||
927 | label: "CONST", | ||
928 | source_range: 219..219, | ||
929 | delete: 219..219, | ||
930 | insert: "CONST", | ||
931 | kind: Const, | ||
932 | detail: "const CONST: u8;", | ||
933 | }, | ||
934 | CompletionItem { | ||
935 | label: "SubTy", | ||
936 | source_range: 219..219, | ||
937 | delete: 219..219, | ||
938 | insert: "SubTy", | ||
939 | kind: TypeAlias, | ||
940 | detail: "type SubTy;", | ||
941 | }, | ||
942 | CompletionItem { | ||
943 | label: "Ty", | ||
944 | source_range: 219..219, | ||
945 | delete: 219..219, | ||
946 | insert: "Ty", | ||
947 | kind: TypeAlias, | ||
948 | detail: "type Ty;", | ||
949 | }, | ||
950 | CompletionItem { | ||
951 | label: "func()", | ||
952 | source_range: 219..219, | ||
953 | delete: 219..219, | ||
954 | insert: "func()$0", | ||
955 | kind: Function, | ||
956 | lookup: "func", | ||
957 | detail: "fn func()", | ||
958 | }, | ||
959 | CompletionItem { | ||
960 | label: "method()", | ||
961 | source_range: 219..219, | ||
962 | delete: 219..219, | ||
963 | insert: "method()$0", | ||
964 | kind: Method, | ||
965 | lookup: "method", | ||
966 | detail: "fn method(&self)", | ||
967 | }, | ||
968 | CompletionItem { | ||
969 | label: "subfunc()", | ||
970 | source_range: 219..219, | ||
971 | delete: 219..219, | ||
972 | insert: "subfunc()$0", | ||
973 | kind: Function, | ||
974 | lookup: "subfunc", | ||
975 | detail: "fn subfunc()", | ||
976 | }, | ||
977 | CompletionItem { | ||
978 | label: "submethod()", | ||
979 | source_range: 219..219, | ||
980 | delete: 219..219, | ||
981 | insert: "submethod()$0", | ||
982 | kind: Method, | ||
983 | lookup: "submethod", | ||
984 | detail: "fn submethod(&self)", | ||
985 | }, | ||
986 | ] | ||
987 | "### | ||
988 | ); | ||
989 | } | ||
990 | |||
991 | #[test] | ||
992 | fn completes_self_param_assoc_ty() { | ||
993 | assert_debug_snapshot!( | ||
994 | do_reference_completion( | ||
995 | " | ||
996 | //- /lib.rs | ||
997 | trait Super { | ||
998 | type Ty; | ||
999 | const CONST: u8 = 0; | ||
1000 | fn func() {} | ||
1001 | fn method(&self) {} | ||
1002 | } | ||
1003 | |||
1004 | trait Sub: Super { | ||
1005 | type SubTy; | ||
1006 | const C2: () = (); | ||
1007 | fn subfunc() {} | ||
1008 | fn submethod(&self) {} | ||
1009 | } | ||
1010 | |||
1011 | struct Wrap<T>(T); | ||
1012 | impl<T> Super for Wrap<T> {} | ||
1013 | impl<T> Sub for Wrap<T> { | ||
1014 | fn subfunc() { | ||
1015 | // Should be able to assume `Self: Sub + Super` | ||
1016 | Self::<|> | ||
1017 | } | ||
1018 | } | ||
1019 | " | ||
1020 | ), | ||
1021 | @r###" | ||
1022 | [ | ||
1023 | CompletionItem { | ||
1024 | label: "C2", | ||
1025 | source_range: 365..365, | ||
1026 | delete: 365..365, | ||
1027 | insert: "C2", | ||
1028 | kind: Const, | ||
1029 | detail: "const C2: () = ();", | ||
1030 | }, | ||
1031 | CompletionItem { | ||
1032 | label: "CONST", | ||
1033 | source_range: 365..365, | ||
1034 | delete: 365..365, | ||
1035 | insert: "CONST", | ||
1036 | kind: Const, | ||
1037 | detail: "const CONST: u8 = 0;", | ||
1038 | }, | ||
1039 | CompletionItem { | ||
1040 | label: "SubTy", | ||
1041 | source_range: 365..365, | ||
1042 | delete: 365..365, | ||
1043 | insert: "SubTy", | ||
1044 | kind: TypeAlias, | ||
1045 | detail: "type SubTy;", | ||
1046 | }, | ||
1047 | CompletionItem { | ||
1048 | label: "Ty", | ||
1049 | source_range: 365..365, | ||
1050 | delete: 365..365, | ||
1051 | insert: "Ty", | ||
1052 | kind: TypeAlias, | ||
1053 | detail: "type Ty;", | ||
1054 | }, | ||
1055 | CompletionItem { | ||
1056 | label: "func()", | ||
1057 | source_range: 365..365, | ||
1058 | delete: 365..365, | ||
1059 | insert: "func()$0", | ||
1060 | kind: Function, | ||
1061 | lookup: "func", | ||
1062 | detail: "fn func()", | ||
1063 | }, | ||
1064 | CompletionItem { | ||
1065 | label: "method()", | ||
1066 | source_range: 365..365, | ||
1067 | delete: 365..365, | ||
1068 | insert: "method()$0", | ||
1069 | kind: Method, | ||
1070 | lookup: "method", | ||
1071 | detail: "fn method(&self)", | ||
1072 | }, | ||
1073 | CompletionItem { | ||
1074 | label: "subfunc()", | ||
1075 | source_range: 365..365, | ||
1076 | delete: 365..365, | ||
1077 | insert: "subfunc()$0", | ||
1078 | kind: Function, | ||
1079 | lookup: "subfunc", | ||
1080 | detail: "fn subfunc()", | ||
1081 | }, | ||
1082 | CompletionItem { | ||
1083 | label: "submethod()", | ||
1084 | source_range: 365..365, | ||
1085 | delete: 365..365, | ||
1086 | insert: "submethod()$0", | ||
1087 | kind: Method, | ||
1088 | lookup: "submethod", | ||
1089 | detail: "fn submethod(&self)", | ||
1090 | }, | ||
1091 | ] | ||
1092 | "### | ||
1093 | ); | ||
1094 | } | ||
1095 | |||
1096 | #[test] | ||
847 | fn completes_type_alias() { | 1097 | fn completes_type_alias() { |
848 | assert_debug_snapshot!( | 1098 | assert_debug_snapshot!( |
849 | do_reference_completion( | 1099 | do_reference_completion( |
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index b5e2785fe..db3907fe6 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs | |||
@@ -26,6 +26,8 @@ pub struct FunctionSignature { | |||
26 | pub kind: CallableKind, | 26 | pub kind: CallableKind, |
27 | /// Optional visibility | 27 | /// Optional visibility |
28 | pub visibility: Option<String>, | 28 | pub visibility: Option<String>, |
29 | /// Qualifiers like `async`, `unsafe`, ... | ||
30 | pub qualifier: FunctionQualifier, | ||
29 | /// Name of the function | 31 | /// Name of the function |
30 | pub name: Option<String>, | 32 | pub name: Option<String>, |
31 | /// Documentation for the function | 33 | /// Documentation for the function |
@@ -46,6 +48,16 @@ pub struct FunctionSignature { | |||
46 | pub has_self_param: bool, | 48 | pub has_self_param: bool, |
47 | } | 49 | } |
48 | 50 | ||
51 | #[derive(Debug, Default)] | ||
52 | pub struct FunctionQualifier { | ||
53 | // `async` and `const` are mutually exclusive. Do we need to enforcing it here? | ||
54 | pub is_async: bool, | ||
55 | pub is_const: bool, | ||
56 | pub is_unsafe: bool, | ||
57 | /// The string `extern ".."` | ||
58 | pub extern_abi: Option<String>, | ||
59 | } | ||
60 | |||
49 | impl FunctionSignature { | 61 | impl FunctionSignature { |
50 | pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self { | 62 | pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self { |
51 | self.doc = doc; | 63 | self.doc = doc; |
@@ -83,6 +95,8 @@ impl FunctionSignature { | |||
83 | FunctionSignature { | 95 | FunctionSignature { |
84 | kind: CallableKind::StructConstructor, | 96 | kind: CallableKind::StructConstructor, |
85 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), | 97 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), |
98 | // Do we need `const`? | ||
99 | qualifier: Default::default(), | ||
86 | name: node.name().map(|n| n.text().to_string()), | 100 | name: node.name().map(|n| n.text().to_string()), |
87 | ret_type: node.name().map(|n| n.text().to_string()), | 101 | ret_type: node.name().map(|n| n.text().to_string()), |
88 | parameters: params, | 102 | parameters: params, |
@@ -128,6 +142,8 @@ impl FunctionSignature { | |||
128 | FunctionSignature { | 142 | FunctionSignature { |
129 | kind: CallableKind::VariantConstructor, | 143 | kind: CallableKind::VariantConstructor, |
130 | visibility: None, | 144 | visibility: None, |
145 | // Do we need `const`? | ||
146 | qualifier: Default::default(), | ||
131 | name: Some(name), | 147 | name: Some(name), |
132 | ret_type: None, | 148 | ret_type: None, |
133 | parameters: params, | 149 | parameters: params, |
@@ -151,6 +167,7 @@ impl FunctionSignature { | |||
151 | FunctionSignature { | 167 | FunctionSignature { |
152 | kind: CallableKind::Macro, | 168 | kind: CallableKind::Macro, |
153 | visibility: None, | 169 | visibility: None, |
170 | qualifier: Default::default(), | ||
154 | name: node.name().map(|n| n.text().to_string()), | 171 | name: node.name().map(|n| n.text().to_string()), |
155 | ret_type: None, | 172 | ret_type: None, |
156 | parameters: params, | 173 | parameters: params, |
@@ -223,6 +240,12 @@ impl From<&'_ ast::FnDef> for FunctionSignature { | |||
223 | FunctionSignature { | 240 | FunctionSignature { |
224 | kind: CallableKind::Function, | 241 | kind: CallableKind::Function, |
225 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), | 242 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), |
243 | qualifier: FunctionQualifier { | ||
244 | is_async: node.async_token().is_some(), | ||
245 | is_const: node.const_token().is_some(), | ||
246 | is_unsafe: node.unsafe_token().is_some(), | ||
247 | extern_abi: node.abi().map(|n| n.to_string()), | ||
248 | }, | ||
226 | name: node.name().map(|n| n.text().to_string()), | 249 | name: node.name().map(|n| n.text().to_string()), |
227 | ret_type: node | 250 | ret_type: node |
228 | .ret_type() | 251 | .ret_type() |
@@ -246,6 +269,23 @@ impl Display for FunctionSignature { | |||
246 | write!(f, "{} ", t)?; | 269 | write!(f, "{} ", t)?; |
247 | } | 270 | } |
248 | 271 | ||
272 | if self.qualifier.is_async { | ||
273 | write!(f, "async ")?; | ||
274 | } | ||
275 | |||
276 | if self.qualifier.is_const { | ||
277 | write!(f, "const ")?; | ||
278 | } | ||
279 | |||
280 | if self.qualifier.is_unsafe { | ||
281 | write!(f, "unsafe ")?; | ||
282 | } | ||
283 | |||
284 | if let Some(extern_abi) = &self.qualifier.extern_abi { | ||
285 | // Keyword `extern` is included in the string. | ||
286 | write!(f, "{} ", extern_abi)?; | ||
287 | } | ||
288 | |||
249 | if let Some(name) = &self.name { | 289 | if let Some(name) = &self.name { |
250 | match self.kind { | 290 | match self.kind { |
251 | CallableKind::Function => write!(f, "fn {}", name)?, | 291 | CallableKind::Function => write!(f, "fn {}", name)?, |
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 58c799eca..a62f598f0 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -844,4 +844,29 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
844 | &["fn foo()\n```\n\n<- `\u{3000}` here"], | 844 | &["fn foo()\n```\n\n<- `\u{3000}` here"], |
845 | ); | 845 | ); |
846 | } | 846 | } |
847 | |||
848 | #[test] | ||
849 | fn test_hover_function_show_qualifiers() { | ||
850 | check_hover_result( | ||
851 | " | ||
852 | //- /lib.rs | ||
853 | async fn foo<|>() {} | ||
854 | ", | ||
855 | &["async fn foo()"], | ||
856 | ); | ||
857 | check_hover_result( | ||
858 | " | ||
859 | //- /lib.rs | ||
860 | pub const unsafe fn foo<|>() {} | ||
861 | ", | ||
862 | &["pub const unsafe fn foo()"], | ||
863 | ); | ||
864 | check_hover_result( | ||
865 | r#" | ||
866 | //- /lib.rs | ||
867 | pub(crate) async unsafe extern "C" fn foo<|>() {} | ||
868 | "#, | ||
869 | &[r#"pub(crate) async unsafe extern "C" fn foo()"#], | ||
870 | ); | ||
871 | } | ||
847 | } | 872 | } |
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs index 2cb3ad011..3b5e05af9 100644 --- a/crates/ra_syntax/src/ast/generated/nodes.rs +++ b/crates/ra_syntax/src/ast/generated/nodes.rs | |||
@@ -1249,6 +1249,7 @@ pub struct PathSegment { | |||
1249 | } | 1249 | } |
1250 | impl PathSegment { | 1250 | impl PathSegment { |
1251 | pub fn coloncolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![::]) } | 1251 | pub fn coloncolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![::]) } |
1252 | pub fn crate_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![crate]) } | ||
1252 | pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) } | 1253 | pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) } |
1253 | pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) } | 1254 | pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) } |
1254 | pub fn type_arg_list(&self) -> Option<TypeArgList> { support::child(&self.syntax) } | 1255 | pub fn type_arg_list(&self) -> Option<TypeArgList> { support::child(&self.syntax) } |
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index 5e93895ec..a30bc97bb 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -96,6 +96,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { | |||
96 | ast::RecordField(it) => validate_numeric_name(it.name_ref(), &mut errors), | 96 | ast::RecordField(it) => validate_numeric_name(it.name_ref(), &mut errors), |
97 | ast::Visibility(it) => validate_visibility(it, &mut errors), | 97 | ast::Visibility(it) => validate_visibility(it, &mut errors), |
98 | ast::RangeExpr(it) => validate_range_expr(it, &mut errors), | 98 | ast::RangeExpr(it) => validate_range_expr(it, &mut errors), |
99 | ast::PathSegment(it) => validate_crate_keyword_in_path_segment(it, &mut errors), | ||
99 | _ => (), | 100 | _ => (), |
100 | } | 101 | } |
101 | } | 102 | } |
@@ -222,3 +223,41 @@ fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec<SyntaxError>) { | |||
222 | )); | 223 | )); |
223 | } | 224 | } |
224 | } | 225 | } |
226 | |||
227 | fn validate_crate_keyword_in_path_segment( | ||
228 | segment: ast::PathSegment, | ||
229 | errors: &mut Vec<SyntaxError>, | ||
230 | ) { | ||
231 | const ERR_MSG: &str = "The `crate` keyword is only allowed as the first segment of a path"; | ||
232 | |||
233 | let crate_token = match segment.crate_token() { | ||
234 | None => return, | ||
235 | Some(it) => it, | ||
236 | }; | ||
237 | |||
238 | // Disallow both ::crate and foo::crate | ||
239 | let path = segment.parent_path(); | ||
240 | if segment.coloncolon_token().is_some() || path.qualifier().is_some() { | ||
241 | errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range())); | ||
242 | return; | ||
243 | } | ||
244 | |||
245 | // We now know that the path variable describes a complete path. | ||
246 | // For expressions and types, validation is complete, but we still have | ||
247 | // to handle UseItems like this: | ||
248 | // use foo:{crate}; | ||
249 | // so we crawl upwards looking for any preceding paths on `UseTree`s | ||
250 | for node in path.syntax().ancestors().skip(1) { | ||
251 | match_ast! { | ||
252 | match node { | ||
253 | ast::UseTree(it) => if let Some(tree_path) = it.path() { | ||
254 | if tree_path != path { | ||
255 | errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range())); | ||
256 | } | ||
257 | }, | ||
258 | ast::UseTreeList(_it) => continue, | ||
259 | _ => return, | ||
260 | } | ||
261 | }; | ||
262 | } | ||
263 | } | ||
diff --git a/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast new file mode 100644 index 000000000..8306f7361 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast | |||
@@ -0,0 +1,76 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "use" | ||
4 | [email protected] " " | ||
5 | [email protected] | ||
6 | [email protected] | ||
7 | [email protected] | ||
8 | [email protected] "::" | ||
9 | [email protected] "crate" | ||
10 | [email protected] ";" | ||
11 | [email protected] "\n" | ||
12 | [email protected] | ||
13 | [email protected] "use" | ||
14 | [email protected] " " | ||
15 | [email protected] | ||
16 | [email protected] | ||
17 | [email protected] "{" | ||
18 | [email protected] | ||
19 | [email protected] | ||
20 | [email protected] | ||
21 | [email protected] "crate" | ||
22 | [email protected] "," | ||
23 | [email protected] " " | ||
24 | [email protected] | ||
25 | [email protected] | ||
26 | [email protected] | ||
27 | [email protected] | ||
28 | [email protected] "foo" | ||
29 | [email protected] "::" | ||
30 | [email protected] | ||
31 | [email protected] "{" | ||
32 | [email protected] | ||
33 | [email protected] | ||
34 | [email protected] | ||
35 | [email protected] "crate" | ||
36 | [email protected] "}" | ||
37 | [email protected] "}" | ||
38 | [email protected] ";" | ||
39 | [email protected] "\n" | ||
40 | [email protected] | ||
41 | [email protected] "use" | ||
42 | [email protected] " " | ||
43 | [email protected] | ||
44 | [email protected] | ||
45 | [email protected] | ||
46 | [email protected] | ||
47 | [email protected] | ||
48 | [email protected] "hello" | ||
49 | [email protected] "::" | ||
50 | [email protected] | ||
51 | [email protected] "crate" | ||
52 | [email protected] ";" | ||
53 | [email protected] "\n" | ||
54 | [email protected] | ||
55 | [email protected] "use" | ||
56 | [email protected] " " | ||
57 | [email protected] | ||
58 | [email protected] | ||
59 | [email protected] | ||
60 | [email protected] | ||
61 | [email protected] | ||
62 | [email protected] | ||
63 | [email protected] "hello" | ||
64 | [email protected] "::" | ||
65 | [email protected] | ||
66 | [email protected] "crate" | ||
67 | [email protected] "::" | ||
68 | [email protected] | ||
69 | [email protected] | ||
70 | [email protected] "there" | ||
71 | [email protected] ";" | ||
72 | [email protected] "\n" | ||
73 | error 6..11: The `crate` keyword is only allowed as the first segment of a path | ||
74 | error 31..36: The `crate` keyword is only allowed as the first segment of a path | ||
75 | error 51..56: The `crate` keyword is only allowed as the first segment of a path | ||
76 | error 69..74: The `crate` keyword is only allowed as the first segment of a path | ||
diff --git a/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs new file mode 100644 index 000000000..bead4c0b6 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs | |||
@@ -0,0 +1,4 @@ | |||
1 | use ::crate; | ||
2 | use {crate, foo::{crate}}; | ||
3 | use hello::crate; | ||
4 | use hello::crate::there; | ||