diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-04-20 17:13:50 +0100 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-04-20 17:13:50 +0100 |
commit | 4ad2e4ce4e88c809abc7a8006d306fb038eb2d18 (patch) | |
tree | 21cc19a5b4fd27b42e09fa12642254227275e88f | |
parent | 526a6aba104a32eb9f0f5a65232783d5570c35d5 (diff) | |
parent | 8ac3d1f9aa892fc891b69c7d8d00d39b9371d246 (diff) |
Merge #1154
1154: Initial support for lang items (and str completion) r=flodiebold a=marcogroppo
This PR adds partial support for lang items.
For now, the only supported lang items are the ones that target an impl block.
Lang items are now resolved during type inference - this means that `str` completion now works.
Fixes #1139.
(thanks Florian Diebold for the help!)
Co-authored-by: Marco Groppo <[email protected]>
-rw-r--r-- | crates/ra_hir/src/db.rs | 9 | ||||
-rw-r--r-- | crates/ra_hir/src/lang_item.rs | 102 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir/src/resolve.rs | 8 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/infer.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/method_resolution.rs | 23 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_path.rs | 25 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/extensions.rs | 14 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/generated.rs | 1 | ||||
-rw-r--r-- | crates/ra_syntax/src/grammar.ron | 2 |
11 files changed, 174 insertions, 19 deletions
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 18627bbc2..8af0a3176 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use ra_syntax::{SyntaxNode, TreeArc, SourceFile, ast}; | 3 | use ra_syntax::{SyntaxNode, TreeArc, SourceFile, SmolStr, ast}; |
4 | use ra_db::{SourceDatabase, salsa}; | 4 | use ra_db::{SourceDatabase, salsa}; |
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
@@ -16,6 +16,7 @@ use crate::{ | |||
16 | generics::{GenericParams, GenericDef}, | 16 | generics::{GenericParams, GenericDef}, |
17 | type_ref::TypeRef, | 17 | type_ref::TypeRef, |
18 | traits::TraitData, Trait, ty::TraitRef, | 18 | traits::TraitData, Trait, ty::TraitRef, |
19 | lang_item::{LangItems, LangItemTarget}, | ||
19 | ids | 20 | ids |
20 | }; | 21 | }; |
21 | 22 | ||
@@ -100,6 +101,12 @@ pub trait DefDatabase: SourceDatabase { | |||
100 | 101 | ||
101 | #[salsa::invoke(crate::ConstSignature::static_signature_query)] | 102 | #[salsa::invoke(crate::ConstSignature::static_signature_query)] |
102 | fn static_signature(&self, konst: Static) -> Arc<ConstSignature>; | 103 | fn static_signature(&self, konst: Static) -> Arc<ConstSignature>; |
104 | |||
105 | #[salsa::invoke(crate::lang_item::LangItems::lang_items_query)] | ||
106 | fn lang_items(&self, krate: Crate) -> Arc<LangItems>; | ||
107 | |||
108 | #[salsa::invoke(crate::lang_item::LangItems::lang_item_query)] | ||
109 | fn lang_item(&self, start_crate: Crate, item: SmolStr) -> Option<LangItemTarget>; | ||
103 | } | 110 | } |
104 | 111 | ||
105 | #[salsa::query_group(HirDatabaseStorage)] | 112 | #[salsa::query_group(HirDatabaseStorage)] |
diff --git a/crates/ra_hir/src/lang_item.rs b/crates/ra_hir/src/lang_item.rs new file mode 100644 index 000000000..5f3f91cba --- /dev/null +++ b/crates/ra_hir/src/lang_item.rs | |||
@@ -0,0 +1,102 @@ | |||
1 | use std::sync::Arc; | ||
2 | use rustc_hash::FxHashMap; | ||
3 | |||
4 | use ra_syntax::{SmolStr, ast::AttrsOwner}; | ||
5 | |||
6 | use crate::{ | ||
7 | Crate, DefDatabase, Enum, Function, HirDatabase, ImplBlock, Module, Static, Struct, Trait | ||
8 | }; | ||
9 | |||
10 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
11 | pub enum LangItemTarget { | ||
12 | Enum(Enum), | ||
13 | Function(Function), | ||
14 | ImplBlock(ImplBlock), | ||
15 | Static(Static), | ||
16 | Struct(Struct), | ||
17 | Trait(Trait), | ||
18 | } | ||
19 | |||
20 | impl LangItemTarget { | ||
21 | pub(crate) fn krate(&self, db: &impl HirDatabase) -> Option<Crate> { | ||
22 | match self { | ||
23 | LangItemTarget::Enum(e) => e.module(db).krate(db), | ||
24 | LangItemTarget::Function(f) => f.module(db).krate(db), | ||
25 | LangItemTarget::ImplBlock(i) => i.module().krate(db), | ||
26 | LangItemTarget::Static(s) => s.module(db).krate(db), | ||
27 | LangItemTarget::Struct(s) => s.module(db).krate(db), | ||
28 | LangItemTarget::Trait(t) => t.module(db).krate(db), | ||
29 | } | ||
30 | } | ||
31 | } | ||
32 | |||
33 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
34 | pub struct LangItems { | ||
35 | items: FxHashMap<SmolStr, LangItemTarget>, | ||
36 | } | ||
37 | |||
38 | impl LangItems { | ||
39 | pub fn target<'a>(&'a self, item: &str) -> Option<&'a LangItemTarget> { | ||
40 | self.items.get(item) | ||
41 | } | ||
42 | |||
43 | /// Salsa query. This will look for lang items in a specific crate. | ||
44 | pub(crate) fn lang_items_query(db: &impl DefDatabase, krate: Crate) -> Arc<LangItems> { | ||
45 | let mut lang_items = LangItems { items: FxHashMap::default() }; | ||
46 | |||
47 | if let Some(module) = krate.root_module(db) { | ||
48 | lang_items.collect_lang_items_recursive(db, &module); | ||
49 | } | ||
50 | |||
51 | Arc::new(lang_items) | ||
52 | } | ||
53 | |||
54 | /// Salsa query. Look for a lang item, starting from the specified crate and recursively | ||
55 | /// traversing its dependencies. | ||
56 | pub(crate) fn lang_item_query( | ||
57 | db: &impl DefDatabase, | ||
58 | start_crate: Crate, | ||
59 | item: SmolStr, | ||
60 | ) -> Option<LangItemTarget> { | ||
61 | let lang_items = db.lang_items(start_crate); | ||
62 | let start_crate_target = lang_items.items.get(&item); | ||
63 | if let Some(target) = start_crate_target { | ||
64 | Some(*target) | ||
65 | } else { | ||
66 | for dep in start_crate.dependencies(db) { | ||
67 | let dep_crate = dep.krate; | ||
68 | let dep_target = db.lang_item(dep_crate, item.clone()); | ||
69 | if dep_target.is_some() { | ||
70 | return dep_target; | ||
71 | } | ||
72 | } | ||
73 | None | ||
74 | } | ||
75 | } | ||
76 | |||
77 | fn collect_lang_items_recursive(&mut self, db: &impl DefDatabase, module: &Module) { | ||
78 | // Look for impl targets | ||
79 | let (impl_blocks, source_map) = db.impls_in_module_with_source_map(module.clone()); | ||
80 | let source = module.definition_source(db).1; | ||
81 | for (impl_id, _) in impl_blocks.impls.iter() { | ||
82 | let impl_block = source_map.get(&source, impl_id); | ||
83 | let lang_item_name = impl_block | ||
84 | .attrs() | ||
85 | .filter_map(|a| a.as_key_value()) | ||
86 | .filter(|(key, _)| key == "lang") | ||
87 | .map(|(_, val)| val) | ||
88 | .nth(0); | ||
89 | if let Some(lang_item_name) = lang_item_name { | ||
90 | let imp = ImplBlock::from_id(*module, impl_id); | ||
91 | self.items.entry(lang_item_name).or_insert(LangItemTarget::ImplBlock(imp)); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | // FIXME we should look for the other lang item targets (traits, structs, ...) | ||
96 | |||
97 | // Look for lang items in the children | ||
98 | for child in module.children(db) { | ||
99 | self.collect_lang_items_recursive(db, &child); | ||
100 | } | ||
101 | } | ||
102 | } | ||
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 9292de1b5..4411715de 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -36,6 +36,7 @@ mod type_ref; | |||
36 | mod ty; | 36 | mod ty; |
37 | mod impl_block; | 37 | mod impl_block; |
38 | mod expr; | 38 | mod expr; |
39 | mod lang_item; | ||
39 | mod generics; | 40 | mod generics; |
40 | mod docs; | 41 | mod docs; |
41 | mod resolve; | 42 | mod resolve; |
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 0eddfab12..39152360c 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs | |||
@@ -202,6 +202,10 @@ impl CrateDefMap { | |||
202 | Arc::new(def_map) | 202 | Arc::new(def_map) |
203 | } | 203 | } |
204 | 204 | ||
205 | pub(crate) fn krate(&self) -> Crate { | ||
206 | self.krate | ||
207 | } | ||
208 | |||
205 | pub(crate) fn root(&self) -> CrateModuleId { | 209 | pub(crate) fn root(&self) -> CrateModuleId { |
206 | self.root | 210 | self.root |
207 | } | 211 | } |
diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index 685f4b8b1..f2c85eb66 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs | |||
@@ -5,13 +5,15 @@ use rustc_hash::FxHashMap; | |||
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
7 | ModuleDef, | 7 | ModuleDef, |
8 | code_model_api::Crate, | ||
8 | db::HirDatabase, | 9 | db::HirDatabase, |
9 | name::{Name, KnownName}, | 10 | name::{Name, KnownName}, |
10 | nameres::{PerNs, CrateDefMap, CrateModuleId}, | 11 | nameres::{PerNs, CrateDefMap, CrateModuleId}, |
11 | generics::GenericParams, | 12 | generics::GenericParams, |
12 | expr::{scope::{ExprScopes, ScopeId}, PatId}, | 13 | expr::{scope::{ExprScopes, ScopeId}, PatId}, |
13 | impl_block::ImplBlock, | 14 | impl_block::ImplBlock, |
14 | path::Path, Trait | 15 | path::Path, |
16 | Trait | ||
15 | }; | 17 | }; |
16 | 18 | ||
17 | #[derive(Debug, Clone, Default)] | 19 | #[derive(Debug, Clone, Default)] |
@@ -197,6 +199,10 @@ impl Resolver { | |||
197 | _ => None, | 199 | _ => None, |
198 | }) | 200 | }) |
199 | } | 201 | } |
202 | |||
203 | pub(crate) fn krate(&self) -> Option<Crate> { | ||
204 | self.module().map(|t| t.0.krate()) | ||
205 | } | ||
200 | } | 206 | } |
201 | 207 | ||
202 | impl Resolver { | 208 | impl Resolver { |
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 7ca1ff595..c7772a7f6 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs | |||
@@ -462,6 +462,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
462 | let remaining_index = remaining_index.unwrap_or(path.segments.len()); | 462 | let remaining_index = remaining_index.unwrap_or(path.segments.len()); |
463 | let mut actual_def_ty: Option<Ty> = None; | 463 | let mut actual_def_ty: Option<Ty> = None; |
464 | 464 | ||
465 | let krate = resolver.krate()?; | ||
465 | // resolve intermediate segments | 466 | // resolve intermediate segments |
466 | for (i, segment) in path.segments[remaining_index..].iter().enumerate() { | 467 | for (i, segment) in path.segments[remaining_index..].iter().enumerate() { |
467 | let ty = match resolved { | 468 | let ty = match resolved { |
@@ -500,9 +501,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
500 | // Attempt to find an impl_item for the type which has a name matching | 501 | // Attempt to find an impl_item for the type which has a name matching |
501 | // the current segment | 502 | // the current segment |
502 | log::debug!("looking for path segment: {:?}", segment); | 503 | log::debug!("looking for path segment: {:?}", segment); |
504 | |||
503 | actual_def_ty = Some(ty.clone()); | 505 | actual_def_ty = Some(ty.clone()); |
504 | 506 | ||
505 | let item: crate::ModuleDef = ty.iterate_impl_items(self.db, |item| { | 507 | let item: crate::ModuleDef = ty.iterate_impl_items(self.db, krate, |item| { |
506 | let matching_def: Option<crate::ModuleDef> = match item { | 508 | let matching_def: Option<crate::ModuleDef> = match item { |
507 | crate::ImplItem::Method(func) => { | 509 | crate::ImplItem::Method(func) => { |
508 | let sig = func.signature(self.db); | 510 | let sig = func.signature(self.db); |
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 667b66095..ea6e0dc0f 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs | |||
@@ -14,6 +14,7 @@ use crate::{ | |||
14 | resolve::Resolver, | 14 | resolve::Resolver, |
15 | traits::TraitItem, | 15 | traits::TraitItem, |
16 | generics::HasGenericParams, | 16 | generics::HasGenericParams, |
17 | ty::primitive::{UncertainIntTy, UncertainFloatTy} | ||
17 | }; | 18 | }; |
18 | use super::{TraitRef, Substs}; | 19 | use super::{TraitRef, Substs}; |
19 | 20 | ||
@@ -110,10 +111,19 @@ impl CrateImplBlocks { | |||
110 | } | 111 | } |
111 | } | 112 | } |
112 | 113 | ||
113 | fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option<Crate> { | 114 | fn def_crate(db: &impl HirDatabase, cur_crate: Crate, ty: &Ty) -> Option<Crate> { |
114 | match ty { | 115 | match ty { |
115 | Ty::Apply(a_ty) => match a_ty.ctor { | 116 | Ty::Apply(a_ty) => match a_ty.ctor { |
116 | TypeCtor::Adt(def_id) => def_id.krate(db), | 117 | TypeCtor::Adt(def_id) => def_id.krate(db), |
118 | TypeCtor::Bool => db.lang_item(cur_crate, "bool".into())?.krate(db), | ||
119 | TypeCtor::Char => db.lang_item(cur_crate, "char".into())?.krate(db), | ||
120 | TypeCtor::Float(UncertainFloatTy::Known(f)) => { | ||
121 | db.lang_item(cur_crate, f.ty_to_string().into())?.krate(db) | ||
122 | } | ||
123 | TypeCtor::Int(UncertainIntTy::Known(i)) => { | ||
124 | db.lang_item(cur_crate, i.ty_to_string().into())?.krate(db) | ||
125 | } | ||
126 | TypeCtor::Str => db.lang_item(cur_crate, "str".into())?.krate(db), | ||
117 | _ => None, | 127 | _ => None, |
118 | }, | 128 | }, |
119 | _ => None, | 129 | _ => None, |
@@ -150,8 +160,11 @@ impl Ty { | |||
150 | // find in the end takes &self, we still do the autoderef step (just as | 160 | // find in the end takes &self, we still do the autoderef step (just as |
151 | // rustc does an autoderef and then autoref again). | 161 | // rustc does an autoderef and then autoref again). |
152 | 162 | ||
163 | let krate = resolver.krate()?; | ||
153 | for derefed_ty in self.autoderef(db) { | 164 | for derefed_ty in self.autoderef(db) { |
154 | if let Some(result) = derefed_ty.iterate_inherent_methods(db, name, &mut callback) { | 165 | if let Some(result) = |
166 | derefed_ty.iterate_inherent_methods(db, name, krate, &mut callback) | ||
167 | { | ||
155 | return Some(result); | 168 | return Some(result); |
156 | } | 169 | } |
157 | if let Some(result) = | 170 | if let Some(result) = |
@@ -208,9 +221,10 @@ impl Ty { | |||
208 | &self, | 221 | &self, |
209 | db: &impl HirDatabase, | 222 | db: &impl HirDatabase, |
210 | name: Option<&Name>, | 223 | name: Option<&Name>, |
224 | krate: Crate, | ||
211 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | 225 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, |
212 | ) -> Option<T> { | 226 | ) -> Option<T> { |
213 | let krate = match def_crate(db, self) { | 227 | let krate = match def_crate(db, krate, self) { |
214 | Some(krate) => krate, | 228 | Some(krate) => krate, |
215 | None => return None, | 229 | None => return None, |
216 | }; | 230 | }; |
@@ -239,9 +253,10 @@ impl Ty { | |||
239 | pub fn iterate_impl_items<T>( | 253 | pub fn iterate_impl_items<T>( |
240 | self, | 254 | self, |
241 | db: &impl HirDatabase, | 255 | db: &impl HirDatabase, |
256 | krate: Crate, | ||
242 | mut callback: impl FnMut(ImplItem) -> Option<T>, | 257 | mut callback: impl FnMut(ImplItem) -> Option<T>, |
243 | ) -> Option<T> { | 258 | ) -> Option<T> { |
244 | let krate = def_crate(db, &self)?; | 259 | let krate = def_crate(db, krate, &self)?; |
245 | let impls = db.impls_in_crate(krate); | 260 | let impls = db.impls_in_crate(krate); |
246 | 261 | ||
247 | for impl_block in impls.lookup_impl_blocks(&self) { | 262 | for impl_block in impls.lookup_impl_blocks(&self) { |
diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs index bc03a7095..c41752ae7 100644 --- a/crates/ra_ide_api/src/completion/complete_path.rs +++ b/crates/ra_ide_api/src/completion/complete_path.rs | |||
@@ -38,19 +38,22 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
38 | } | 38 | } |
39 | hir::ModuleDef::Struct(s) => { | 39 | hir::ModuleDef::Struct(s) => { |
40 | let ty = s.ty(ctx.db); | 40 | let ty = s.ty(ctx.db); |
41 | ty.iterate_impl_items(ctx.db, |item| { | 41 | let krate = ctx.module.and_then(|m| m.krate(ctx.db)); |
42 | match item { | 42 | if let Some(krate) = krate { |
43 | hir::ImplItem::Method(func) => { | 43 | ty.iterate_impl_items(ctx.db, krate, |item| { |
44 | let sig = func.signature(ctx.db); | 44 | match item { |
45 | if !sig.has_self_param() { | 45 | hir::ImplItem::Method(func) => { |
46 | acc.add_function(ctx, func); | 46 | let sig = func.signature(ctx.db); |
47 | if !sig.has_self_param() { | ||
48 | acc.add_function(ctx, func); | ||
49 | } | ||
47 | } | 50 | } |
51 | hir::ImplItem::Const(ct) => acc.add_const(ctx, ct), | ||
52 | hir::ImplItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
48 | } | 53 | } |
49 | hir::ImplItem::Const(ct) => acc.add_const(ctx, ct), | 54 | None::<()> |
50 | hir::ImplItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | 55 | }); |
51 | } | 56 | } |
52 | None::<()> | ||
53 | }); | ||
54 | } | 57 | } |
55 | _ => return, | 58 | _ => return, |
56 | }; | 59 | }; |
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs index ca33b43e7..5c4c0ffc1 100644 --- a/crates/ra_syntax/src/ast/extensions.rs +++ b/crates/ra_syntax/src/ast/extensions.rs | |||
@@ -65,6 +65,20 @@ impl ast::Attr { | |||
65 | None | 65 | None |
66 | } | 66 | } |
67 | } | 67 | } |
68 | |||
69 | pub fn as_key_value(&self) -> Option<(SmolStr, SmolStr)> { | ||
70 | let tt = self.value()?; | ||
71 | let tt_node = tt.syntax(); | ||
72 | let attr = tt_node.children_with_tokens().nth(1)?; | ||
73 | if attr.kind() == IDENT { | ||
74 | let key = attr.as_token()?.text().clone(); | ||
75 | let val_node = tt_node.children_with_tokens().find(|t| t.kind() == STRING)?; | ||
76 | let val = val_node.as_token()?.text().trim_start_matches("\"").trim_end_matches("\""); | ||
77 | Some((key, SmolStr::new(val))) | ||
78 | } else { | ||
79 | None | ||
80 | } | ||
81 | } | ||
68 | } | 82 | } |
69 | 83 | ||
70 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 84 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 17de4f058..fae371509 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -1325,6 +1325,7 @@ impl ToOwned for ImplBlock { | |||
1325 | 1325 | ||
1326 | 1326 | ||
1327 | impl ast::TypeParamsOwner for ImplBlock {} | 1327 | impl ast::TypeParamsOwner for ImplBlock {} |
1328 | impl ast::AttrsOwner for ImplBlock {} | ||
1328 | impl ImplBlock { | 1329 | impl ImplBlock { |
1329 | pub fn item_list(&self) -> Option<&ItemList> { | 1330 | pub fn item_list(&self) -> Option<&ItemList> { |
1330 | super::child_opt(self) | 1331 | super::child_opt(self) |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 663e3a2f9..5bdcf9c84 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -345,7 +345,7 @@ Grammar( | |||
345 | ], | 345 | ], |
346 | options: ["TypeRef"] | 346 | options: ["TypeRef"] |
347 | ), | 347 | ), |
348 | "ImplBlock": (options: ["ItemList"], traits: ["TypeParamsOwner"]), | 348 | "ImplBlock": (options: ["ItemList"], traits: ["TypeParamsOwner", "AttrsOwner"]), |
349 | 349 | ||
350 | "ParenType": (options: ["TypeRef"]), | 350 | "ParenType": (options: ["TypeRef"]), |
351 | "TupleType": ( collections: [["fields", "TypeRef"]] ), | 351 | "TupleType": ( collections: [["fields", "TypeRef"]] ), |