aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/code_model.rs10
-rw-r--r--crates/ra_hir/src/semantics.rs42
-rw-r--r--crates/ra_hir_ty/src/lib.rs3
-rw-r--r--crates/ra_hir_ty/src/lower.rs143
-rw-r--r--crates/ra_ide/src/completion/complete_qualified_path.rs272
-rw-r--r--crates/ra_ide/src/display/function_signature.rs40
-rw-r--r--crates/ra_ide/src/hover.rs25
-rw-r--r--crates/ra_syntax/src/ast/generated/nodes.rs1
-rw-r--r--crates/ra_syntax/src/validation.rs39
-rw-r--r--crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast76
-rw-r--r--crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs4
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};
11use hir_expand::ExpansionInfo; 11use hir_expand::ExpansionInfo;
12use hir_ty::associated_type_shorthand_candidates;
12use itertools::Itertools; 13use itertools::Itertools;
13use ra_db::{FileId, FileRange}; 14use ra_db::{FileId, FileRange};
14use ra_prof::profile; 15use 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};
30use resolver::TypeNs;
29 31
30#[derive(Debug, Clone, PartialEq, Eq)] 32#[derive(Debug, Clone, PartialEq, Eq)]
31pub enum PathResolution { 33pub enum PathResolution {
@@ -40,6 +42,44 @@ pub enum PathResolution {
40 AssocItem(AssocItem), 42 AssocItem(AssocItem),
41} 43}
42 44
45impl 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.
44pub struct Semantics<'db, DB> { 84pub 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;
66pub use infer::{InferTy, InferenceResult}; 66pub use infer::{InferTy, InferenceResult};
67pub use lower::CallableDef; 67pub use lower::CallableDef;
68pub use lower::{ 68pub 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};
71pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; 72pub 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};
24use ra_arena::map::ArenaMap; 24use ra_arena::map::ArenaMap;
25use ra_db::CrateId; 25use 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};
37use hir_expand::name::Name;
37 38
38#[derive(Debug)] 39#[derive(Debug)]
39pub struct TyLoweringContext<'a> { 40pub 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
675pub 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.
698pub(crate) fn field_types_query( 731pub(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;
5use test_utils::tested_by; 5use test_utils::tested_by;
6 6
7use crate::completion::{CompletionContext, Completions}; 7use crate::completion::{CompletionContext, Completions};
8use rustc_hash::FxHashSet;
8 9
9pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { 10pub(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)]
52pub 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
49impl FunctionSignature { 61impl 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}
1250impl PathSegment { 1250impl 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
227fn 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"
73error 6..11: The `crate` keyword is only allowed as the first segment of a path
74error 31..36: The `crate` keyword is only allowed as the first segment of a path
75error 51..56: The `crate` keyword is only allowed as the first segment of a path
76error 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 @@
1use ::crate;
2use {crate, foo::{crate}};
3use hello::crate;
4use hello::crate::there;