diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/src/code_model_api.rs | 36 | ||||
-rw-r--r-- | crates/ra_hir/src/code_model_impl/function.rs | 13 | ||||
-rw-r--r-- | crates/ra_hir/src/expr.rs | 9 | ||||
-rw-r--r-- | crates/ra_hir/src/generics.rs | 42 | ||||
-rw-r--r-- | crates/ra_hir/src/ids.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir/src/impl_block.rs | 20 | ||||
-rw-r--r-- | crates/ra_hir/src/path.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 65 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/method_resolution.rs | 12 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/snapshots/tests__infer_generic_chain.snap | 54 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics.snap | 39 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics_with_autoderef.snap | 16 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 52 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_dot.rs | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/generated.rs | 5 | ||||
-rw-r--r-- | crates/ra_syntax/src/grammar.ron | 4 |
16 files changed, 290 insertions, 84 deletions
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 99f004905..26ebc445b 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs | |||
@@ -463,7 +463,7 @@ impl Function { | |||
463 | self.id.source(db) | 463 | self.id.source(db) |
464 | } | 464 | } |
465 | 465 | ||
466 | pub fn module(&self, db: &impl HirDatabase) -> Module { | 466 | pub fn module(&self, db: &impl PersistentHirDatabase) -> Module { |
467 | self.id.module(db) | 467 | self.id.module(db) |
468 | } | 468 | } |
469 | 469 | ||
@@ -497,6 +497,12 @@ impl Function { | |||
497 | db.generic_params((*self).into()) | 497 | db.generic_params((*self).into()) |
498 | } | 498 | } |
499 | 499 | ||
500 | /// The containing impl block, if this is a method. | ||
501 | pub fn impl_block(&self, db: &impl PersistentHirDatabase) -> Option<ImplBlock> { | ||
502 | let module_impls = db.impls_in_module(self.module(db)); | ||
503 | ImplBlock::containing(module_impls, (*self).into()) | ||
504 | } | ||
505 | |||
500 | // TODO: move to a more general type for 'body-having' items | 506 | // TODO: move to a more general type for 'body-having' items |
501 | /// Builds a resolver for code inside this item. | 507 | /// Builds a resolver for code inside this item. |
502 | pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { | 508 | pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { |
@@ -527,6 +533,16 @@ impl Const { | |||
527 | pub fn source(&self, db: &impl PersistentHirDatabase) -> (HirFileId, TreeArc<ast::ConstDef>) { | 533 | pub fn source(&self, db: &impl PersistentHirDatabase) -> (HirFileId, TreeArc<ast::ConstDef>) { |
528 | self.id.source(db) | 534 | self.id.source(db) |
529 | } | 535 | } |
536 | |||
537 | pub fn module(&self, db: &impl PersistentHirDatabase) -> Module { | ||
538 | self.id.module(db) | ||
539 | } | ||
540 | |||
541 | /// The containing impl block, if this is a method. | ||
542 | pub fn impl_block(&self, db: &impl PersistentHirDatabase) -> Option<ImplBlock> { | ||
543 | let module_impls = db.impls_in_module(self.module(db)); | ||
544 | ImplBlock::containing(module_impls, (*self).into()) | ||
545 | } | ||
530 | } | 546 | } |
531 | 547 | ||
532 | impl Docs for Const { | 548 | impl Docs for Const { |
@@ -544,6 +560,10 @@ impl Static { | |||
544 | pub fn source(&self, db: &impl PersistentHirDatabase) -> (HirFileId, TreeArc<ast::StaticDef>) { | 560 | pub fn source(&self, db: &impl PersistentHirDatabase) -> (HirFileId, TreeArc<ast::StaticDef>) { |
545 | self.id.source(db) | 561 | self.id.source(db) |
546 | } | 562 | } |
563 | |||
564 | pub fn module(&self, db: &impl PersistentHirDatabase) -> Module { | ||
565 | self.id.module(db) | ||
566 | } | ||
547 | } | 567 | } |
548 | 568 | ||
549 | impl Docs for Static { | 569 | impl Docs for Static { |
@@ -562,6 +582,10 @@ impl Trait { | |||
562 | self.id.source(db) | 582 | self.id.source(db) |
563 | } | 583 | } |
564 | 584 | ||
585 | pub fn module(&self, db: &impl PersistentHirDatabase) -> Module { | ||
586 | self.id.module(db) | ||
587 | } | ||
588 | |||
565 | pub fn generic_params(&self, db: &impl PersistentHirDatabase) -> Arc<GenericParams> { | 589 | pub fn generic_params(&self, db: &impl PersistentHirDatabase) -> Arc<GenericParams> { |
566 | db.generic_params((*self).into()) | 590 | db.generic_params((*self).into()) |
567 | } | 591 | } |
@@ -586,6 +610,16 @@ impl Type { | |||
586 | pub fn generic_params(&self, db: &impl PersistentHirDatabase) -> Arc<GenericParams> { | 610 | pub fn generic_params(&self, db: &impl PersistentHirDatabase) -> Arc<GenericParams> { |
587 | db.generic_params((*self).into()) | 611 | db.generic_params((*self).into()) |
588 | } | 612 | } |
613 | |||
614 | pub fn module(&self, db: &impl PersistentHirDatabase) -> Module { | ||
615 | self.id.module(db) | ||
616 | } | ||
617 | |||
618 | /// The containing impl block, if this is a method. | ||
619 | pub fn impl_block(&self, db: &impl PersistentHirDatabase) -> Option<ImplBlock> { | ||
620 | let module_impls = db.impls_in_module(self.module(db)); | ||
621 | ImplBlock::containing(module_impls, (*self).into()) | ||
622 | } | ||
589 | } | 623 | } |
590 | 624 | ||
591 | impl Docs for Type { | 625 | impl Docs for Type { |
diff --git a/crates/ra_hir/src/code_model_impl/function.rs b/crates/ra_hir/src/code_model_impl/function.rs index b9438fdb7..c401528c6 100644 --- a/crates/ra_hir/src/code_model_impl/function.rs +++ b/crates/ra_hir/src/code_model_impl/function.rs | |||
@@ -3,22 +3,11 @@ use std::sync::Arc; | |||
3 | use ra_syntax::ast::{self, NameOwner}; | 3 | use ra_syntax::ast::{self, NameOwner}; |
4 | 4 | ||
5 | use crate::{ | 5 | use crate::{ |
6 | HirDatabase, Name, AsName, Function, FnSignature, | 6 | Name, AsName, Function, FnSignature, |
7 | type_ref::{TypeRef, Mutability}, | 7 | type_ref::{TypeRef, Mutability}, |
8 | PersistentHirDatabase, | 8 | PersistentHirDatabase, |
9 | impl_block::ImplBlock, | ||
10 | }; | 9 | }; |
11 | 10 | ||
12 | impl Function { | ||
13 | // TODO impl_block should probably also be part of the code model API? | ||
14 | |||
15 | /// The containing impl block, if this is a method. | ||
16 | pub(crate) fn impl_block(&self, db: &impl HirDatabase) -> Option<ImplBlock> { | ||
17 | let module_impls = db.impls_in_module(self.module(db)); | ||
18 | ImplBlock::containing(module_impls, (*self).into()) | ||
19 | } | ||
20 | } | ||
21 | |||
22 | impl FnSignature { | 11 | impl FnSignature { |
23 | pub(crate) fn fn_signature_query( | 12 | pub(crate) fn fn_signature_query( |
24 | db: &impl PersistentHirDatabase, | 13 | db: &impl PersistentHirDatabase, |
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 4e73590d0..e9db8282f 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs | |||
@@ -14,7 +14,7 @@ use crate::{ | |||
14 | name::AsName, | 14 | name::AsName, |
15 | type_ref::{Mutability, TypeRef}, | 15 | type_ref::{Mutability, TypeRef}, |
16 | }; | 16 | }; |
17 | use crate::ty::primitive::{UintTy, UncertainIntTy, UncertainFloatTy}; | 17 | use crate::{ path::GenericArgs, ty::primitive::{UintTy, UncertainIntTy, UncertainFloatTy}}; |
18 | 18 | ||
19 | pub use self::scope::{ExprScopes, ScopesWithSyntaxMapping, ScopeEntryWithSyntax}; | 19 | pub use self::scope::{ExprScopes, ScopesWithSyntaxMapping, ScopeEntryWithSyntax}; |
20 | 20 | ||
@@ -193,6 +193,7 @@ pub enum Expr { | |||
193 | receiver: ExprId, | 193 | receiver: ExprId, |
194 | method_name: Name, | 194 | method_name: Name, |
195 | args: Vec<ExprId>, | 195 | args: Vec<ExprId>, |
196 | generic_args: Option<GenericArgs>, | ||
196 | }, | 197 | }, |
197 | Match { | 198 | Match { |
198 | expr: ExprId, | 199 | expr: ExprId, |
@@ -597,7 +598,11 @@ impl ExprCollector { | |||
597 | Vec::new() | 598 | Vec::new() |
598 | }; | 599 | }; |
599 | let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); | 600 | let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); |
600 | self.alloc_expr(Expr::MethodCall { receiver, method_name, args }, syntax_ptr) | 601 | let generic_args = e.type_arg_list().and_then(GenericArgs::from_ast); |
602 | self.alloc_expr( | ||
603 | Expr::MethodCall { receiver, method_name, args, generic_args }, | ||
604 | syntax_ptr, | ||
605 | ) | ||
601 | } | 606 | } |
602 | ast::ExprKind::MatchExpr(e) => { | 607 | ast::ExprKind::MatchExpr(e) => { |
603 | let expr = self.collect_expr_opt(e.expr()); | 608 | let expr = self.collect_expr_opt(e.expr()); |
diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index c72360f44..fcc513353 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs | |||
@@ -7,7 +7,7 @@ use std::sync::Arc; | |||
7 | 7 | ||
8 | use ra_syntax::ast::{self, NameOwner, TypeParamsOwner}; | 8 | use ra_syntax::ast::{self, NameOwner, TypeParamsOwner}; |
9 | 9 | ||
10 | use crate::{db::PersistentHirDatabase, Name, AsName, Function, Struct, Enum, Trait, Type}; | 10 | use crate::{db::PersistentHirDatabase, Name, AsName, Function, Struct, Enum, Trait, Type, ImplBlock}; |
11 | 11 | ||
12 | /// Data about a generic parameter (to a function, struct, impl, ...). | 12 | /// Data about a generic parameter (to a function, struct, impl, ...). |
13 | #[derive(Clone, PartialEq, Eq, Debug)] | 13 | #[derive(Clone, PartialEq, Eq, Debug)] |
@@ -20,6 +20,7 @@ pub struct GenericParam { | |||
20 | /// Data about the generic parameters of a function, struct, impl, etc. | 20 | /// Data about the generic parameters of a function, struct, impl, etc. |
21 | #[derive(Clone, PartialEq, Eq, Debug, Default)] | 21 | #[derive(Clone, PartialEq, Eq, Debug, Default)] |
22 | pub struct GenericParams { | 22 | pub struct GenericParams { |
23 | pub(crate) parent_params: Option<Arc<GenericParams>>, | ||
23 | pub(crate) params: Vec<GenericParam>, | 24 | pub(crate) params: Vec<GenericParam>, |
24 | } | 25 | } |
25 | 26 | ||
@@ -30,8 +31,9 @@ pub enum GenericDef { | |||
30 | Enum(Enum), | 31 | Enum(Enum), |
31 | Trait(Trait), | 32 | Trait(Trait), |
32 | Type(Type), | 33 | Type(Type), |
34 | ImplBlock(ImplBlock), | ||
33 | } | 35 | } |
34 | impl_froms!(GenericDef: Function, Struct, Enum, Trait, Type); | 36 | impl_froms!(GenericDef: Function, Struct, Enum, Trait, Type, ImplBlock); |
35 | 37 | ||
36 | impl GenericParams { | 38 | impl GenericParams { |
37 | pub(crate) fn generic_params_query( | 39 | pub(crate) fn generic_params_query( |
@@ -39,27 +41,36 @@ impl GenericParams { | |||
39 | def: GenericDef, | 41 | def: GenericDef, |
40 | ) -> Arc<GenericParams> { | 42 | ) -> Arc<GenericParams> { |
41 | let mut generics = GenericParams::default(); | 43 | let mut generics = GenericParams::default(); |
44 | let parent = match def { | ||
45 | GenericDef::Function(it) => it.impl_block(db), | ||
46 | GenericDef::Type(it) => it.impl_block(db), | ||
47 | GenericDef::Struct(_) | GenericDef::Enum(_) | GenericDef::Trait(_) => None, | ||
48 | GenericDef::ImplBlock(_) => None, | ||
49 | }; | ||
50 | generics.parent_params = parent.map(|p| p.generic_params(db)); | ||
51 | let start = generics.parent_params.as_ref().map(|p| p.params.len()).unwrap_or(0) as u32; | ||
42 | match def { | 52 | match def { |
43 | GenericDef::Function(it) => generics.fill(&*it.source(db).1), | 53 | GenericDef::Function(it) => generics.fill(&*it.source(db).1, start), |
44 | GenericDef::Struct(it) => generics.fill(&*it.source(db).1), | 54 | GenericDef::Struct(it) => generics.fill(&*it.source(db).1, start), |
45 | GenericDef::Enum(it) => generics.fill(&*it.source(db).1), | 55 | GenericDef::Enum(it) => generics.fill(&*it.source(db).1, start), |
46 | GenericDef::Trait(it) => generics.fill(&*it.source(db).1), | 56 | GenericDef::Trait(it) => generics.fill(&*it.source(db).1, start), |
47 | GenericDef::Type(it) => generics.fill(&*it.source(db).1), | 57 | GenericDef::Type(it) => generics.fill(&*it.source(db).1, start), |
58 | GenericDef::ImplBlock(it) => generics.fill(&*it.source(db).1, start), | ||
48 | } | 59 | } |
49 | 60 | ||
50 | Arc::new(generics) | 61 | Arc::new(generics) |
51 | } | 62 | } |
52 | 63 | ||
53 | fn fill(&mut self, node: &impl TypeParamsOwner) { | 64 | fn fill(&mut self, node: &impl TypeParamsOwner, start: u32) { |
54 | if let Some(params) = node.type_param_list() { | 65 | if let Some(params) = node.type_param_list() { |
55 | self.fill_params(params) | 66 | self.fill_params(params, start) |
56 | } | 67 | } |
57 | } | 68 | } |
58 | 69 | ||
59 | fn fill_params(&mut self, params: &ast::TypeParamList) { | 70 | fn fill_params(&mut self, params: &ast::TypeParamList, start: u32) { |
60 | for (idx, type_param) in params.type_params().enumerate() { | 71 | for (idx, type_param) in params.type_params().enumerate() { |
61 | let name = type_param.name().map(AsName::as_name).unwrap_or_else(Name::missing); | 72 | let name = type_param.name().map(AsName::as_name).unwrap_or_else(Name::missing); |
62 | let param = GenericParam { idx: idx as u32, name }; | 73 | let param = GenericParam { idx: idx as u32 + start, name }; |
63 | self.params.push(param); | 74 | self.params.push(param); |
64 | } | 75 | } |
65 | } | 76 | } |
@@ -67,4 +78,13 @@ impl GenericParams { | |||
67 | pub(crate) fn find_by_name(&self, name: &Name) -> Option<&GenericParam> { | 78 | pub(crate) fn find_by_name(&self, name: &Name) -> Option<&GenericParam> { |
68 | self.params.iter().find(|p| &p.name == name) | 79 | self.params.iter().find(|p| &p.name == name) |
69 | } | 80 | } |
81 | |||
82 | pub fn count_parent_params(&self) -> usize { | ||
83 | self.parent_params.as_ref().map(|p| p.count_params_including_parent()).unwrap_or(0) | ||
84 | } | ||
85 | |||
86 | pub fn count_params_including_parent(&self) -> usize { | ||
87 | let parent_count = self.count_parent_params(); | ||
88 | parent_count + self.params.len() | ||
89 | } | ||
70 | } | 90 | } |
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 3e11dd6ad..6df037859 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs | |||
@@ -9,7 +9,6 @@ use ra_syntax::{TreeArc, SyntaxNode, SourceFile, AstNode, SyntaxNodePtr, ast}; | |||
9 | use ra_arena::{Arena, RawId, ArenaId, impl_arena_id}; | 9 | use ra_arena::{Arena, RawId, ArenaId, impl_arena_id}; |
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | HirDatabase, | ||
13 | Module, | 12 | Module, |
14 | PersistentHirDatabase, | 13 | PersistentHirDatabase, |
15 | }; | 14 | }; |
@@ -215,7 +214,7 @@ pub(crate) trait AstItemDef<N: AstNode>: ArenaId + Clone { | |||
215 | N::cast(&syntax).unwrap_or_else(|| panic!("invalid ItemLoc: {:?}", loc.raw)).to_owned(); | 214 | N::cast(&syntax).unwrap_or_else(|| panic!("invalid ItemLoc: {:?}", loc.raw)).to_owned(); |
216 | (loc.raw.file_id, ast) | 215 | (loc.raw.file_id, ast) |
217 | } | 216 | } |
218 | fn module(self, db: &impl HirDatabase) -> Module { | 217 | fn module(self, db: &impl PersistentHirDatabase) -> Module { |
219 | let int = Self::interner(db.as_ref()); | 218 | let int = Self::interner(db.as_ref()); |
220 | let loc = int.id2loc(self); | 219 | let loc = int.id2loc(self); |
221 | loc.module | 220 | loc.module |
diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index 4d8bdf33a..7ecf8c368 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs | |||
@@ -13,7 +13,7 @@ use crate::{ | |||
13 | type_ref::TypeRef, | 13 | type_ref::TypeRef, |
14 | ids::LocationCtx, | 14 | ids::LocationCtx, |
15 | resolve::Resolver, | 15 | resolve::Resolver, |
16 | ty::Ty, | 16 | ty::Ty, generics::GenericParams |
17 | }; | 17 | }; |
18 | 18 | ||
19 | use crate::code_model_api::{Module, ModuleSource}; | 19 | use crate::code_model_api::{Module, ModuleSource}; |
@@ -38,7 +38,7 @@ impl ImplSourceMap { | |||
38 | } | 38 | } |
39 | } | 39 | } |
40 | 40 | ||
41 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 41 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
42 | pub struct ImplBlock { | 42 | pub struct ImplBlock { |
43 | module: Module, | 43 | module: Module, |
44 | impl_id: ImplId, | 44 | impl_id: ImplId, |
@@ -58,7 +58,7 @@ impl ImplBlock { | |||
58 | } | 58 | } |
59 | 59 | ||
60 | /// Returns the syntax of the impl block | 60 | /// Returns the syntax of the impl block |
61 | pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc<ast::ImplBlock>) { | 61 | pub fn source(&self, db: &impl PersistentHirDatabase) -> (HirFileId, TreeArc<ast::ImplBlock>) { |
62 | let source_map = db.impls_in_module_source_map(self.module); | 62 | let source_map = db.impls_in_module_source_map(self.module); |
63 | let (file_id, source) = self.module.definition_source(db); | 63 | let (file_id, source) = self.module.definition_source(db); |
64 | (file_id, source_map.get(&source, self.impl_id)) | 64 | (file_id, source_map.get(&source, self.impl_id)) |
@@ -72,11 +72,11 @@ impl ImplBlock { | |||
72 | self.module | 72 | self.module |
73 | } | 73 | } |
74 | 74 | ||
75 | pub fn target_trait_ref(&self, db: &impl HirDatabase) -> Option<TypeRef> { | 75 | pub fn target_trait_ref(&self, db: &impl PersistentHirDatabase) -> Option<TypeRef> { |
76 | db.impls_in_module(self.module).impls[self.impl_id].target_trait().cloned() | 76 | db.impls_in_module(self.module).impls[self.impl_id].target_trait().cloned() |
77 | } | 77 | } |
78 | 78 | ||
79 | pub fn target_type(&self, db: &impl HirDatabase) -> TypeRef { | 79 | pub fn target_type(&self, db: &impl PersistentHirDatabase) -> TypeRef { |
80 | db.impls_in_module(self.module).impls[self.impl_id].target_type().clone() | 80 | db.impls_in_module(self.module).impls[self.impl_id].target_type().clone() |
81 | } | 81 | } |
82 | 82 | ||
@@ -96,13 +96,19 @@ impl ImplBlock { | |||
96 | None | 96 | None |
97 | } | 97 | } |
98 | 98 | ||
99 | pub fn items(&self, db: &impl HirDatabase) -> Vec<ImplItem> { | 99 | pub fn items(&self, db: &impl PersistentHirDatabase) -> Vec<ImplItem> { |
100 | db.impls_in_module(self.module).impls[self.impl_id].items().to_vec() | 100 | db.impls_in_module(self.module).impls[self.impl_id].items().to_vec() |
101 | } | 101 | } |
102 | 102 | ||
103 | pub fn generic_params(&self, db: &impl PersistentHirDatabase) -> Arc<GenericParams> { | ||
104 | db.generic_params((*self).into()) | ||
105 | } | ||
106 | |||
103 | pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { | 107 | pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { |
104 | let r = self.module().resolver(db); | 108 | let r = self.module().resolver(db); |
105 | // TODO: add generics | 109 | // add generic params, if present |
110 | let p = self.generic_params(db); | ||
111 | let r = if !p.params.is_empty() { r.push_generic_params_scope(p) } else { r }; | ||
106 | let r = r.push_impl_block_scope(self.clone()); | 112 | let r = r.push_impl_block_scope(self.clone()); |
107 | r | 113 | r |
108 | } | 114 | } |
diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index 8ed54607a..6ca373e34 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs | |||
@@ -129,7 +129,7 @@ impl Path { | |||
129 | } | 129 | } |
130 | 130 | ||
131 | impl GenericArgs { | 131 | impl GenericArgs { |
132 | fn from_ast(node: &ast::TypeArgList) -> Option<GenericArgs> { | 132 | pub(crate) fn from_ast(node: &ast::TypeArgList) -> Option<GenericArgs> { |
133 | let mut args = Vec::new(); | 133 | let mut args = Vec::new(); |
134 | for type_arg in node.type_args() { | 134 | for type_arg in node.type_args() { |
135 | let type_ref = TypeRef::from_ast_opt(type_arg.type_ref()); | 135 | let type_ref = TypeRef::from_ast_opt(type_arg.type_ref()); |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index f28a7e731..562ad1f49 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -165,6 +165,17 @@ impl Substs { | |||
165 | pub fn empty() -> Substs { | 165 | pub fn empty() -> Substs { |
166 | Substs(Arc::new([])) | 166 | Substs(Arc::new([])) |
167 | } | 167 | } |
168 | |||
169 | /// Replaces the end of the substitutions by other ones. | ||
170 | pub(crate) fn replace_tail(self, replace_by: Vec<Ty>) -> Substs { | ||
171 | // again missing Arc::make_mut_slice... | ||
172 | let len = replace_by.len().min(self.0.len()); | ||
173 | let parent_len = self.0.len() - len; | ||
174 | let mut result = Vec::with_capacity(parent_len + len); | ||
175 | result.extend(self.0.iter().take(parent_len).cloned()); | ||
176 | result.extend(replace_by); | ||
177 | Substs(result.into()) | ||
178 | } | ||
168 | } | 179 | } |
169 | 180 | ||
170 | /// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs). | 181 | /// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs). |
@@ -421,7 +432,8 @@ impl Ty { | |||
421 | (var.parent_enum(db).generic_params(db), segment) | 432 | (var.parent_enum(db).generic_params(db), segment) |
422 | } | 433 | } |
423 | }; | 434 | }; |
424 | // substs_from_path | 435 | let parent_param_count = def_generics.count_parent_params(); |
436 | substs.extend((0..parent_param_count).map(|_| Ty::Unknown)); | ||
425 | if let Some(generic_args) = &segment.args_and_bindings { | 437 | if let Some(generic_args) = &segment.args_and_bindings { |
426 | // if args are provided, it should be all of them, but we can't rely on that | 438 | // if args are provided, it should be all of them, but we can't rely on that |
427 | let param_count = def_generics.params.len(); | 439 | let param_count = def_generics.params.len(); |
@@ -436,9 +448,8 @@ impl Ty { | |||
436 | } | 448 | } |
437 | // add placeholders for args that were not provided | 449 | // add placeholders for args that were not provided |
438 | // TODO: handle defaults | 450 | // TODO: handle defaults |
439 | let supplied_params = | 451 | let supplied_params = substs.len(); |
440 | segment.args_and_bindings.as_ref().map(|ga| ga.args.len()).unwrap_or(0); | 452 | for _ in supplied_params..def_generics.count_params_including_parent() { |
441 | for _ in supplied_params..def_generics.params.len() { | ||
442 | substs.push(Ty::Unknown); | 453 | substs.push(Ty::Unknown); |
443 | } | 454 | } |
444 | assert_eq!(substs.len(), def_generics.params.len()); | 455 | assert_eq!(substs.len(), def_generics.params.len()); |
@@ -666,7 +677,12 @@ fn type_for_fn(db: &impl HirDatabase, def: Function) -> Ty { | |||
666 | } | 677 | } |
667 | 678 | ||
668 | fn make_substs(generics: &GenericParams) -> Substs { | 679 | fn make_substs(generics: &GenericParams) -> Substs { |
669 | Substs(generics.params.iter().map(|_p| Ty::Unknown).collect::<Vec<_>>().into()) | 680 | Substs( |
681 | (0..generics.count_params_including_parent()) | ||
682 | .map(|_p| Ty::Unknown) | ||
683 | .collect::<Vec<_>>() | ||
684 | .into(), | ||
685 | ) | ||
670 | } | 686 | } |
671 | 687 | ||
672 | fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Ty { | 688 | fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Ty { |
@@ -1362,15 +1378,34 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1362 | } | 1378 | } |
1363 | ret_ty | 1379 | ret_ty |
1364 | } | 1380 | } |
1365 | Expr::MethodCall { receiver, args, method_name } => { | 1381 | Expr::MethodCall { receiver, args, method_name, generic_args } => { |
1366 | let receiver_ty = self.infer_expr(*receiver, &Expectation::none()); | 1382 | let receiver_ty = self.infer_expr(*receiver, &Expectation::none()); |
1367 | let resolved = receiver_ty.clone().lookup_method(self.db, method_name); | 1383 | let resolved = receiver_ty.clone().lookup_method(self.db, method_name); |
1368 | let method_ty = match resolved { | 1384 | let (derefed_receiver_ty, method_ty, def_generics) = match resolved { |
1369 | Some(func) => { | 1385 | Some((ty, func)) => { |
1370 | self.write_method_resolution(tgt_expr, func); | 1386 | self.write_method_resolution(tgt_expr, func); |
1371 | self.db.type_for_def(func.into()) | 1387 | (ty, self.db.type_for_def(func.into()), Some(func.generic_params(self.db))) |
1388 | } | ||
1389 | None => (Ty::Unknown, receiver_ty, None), | ||
1390 | }; | ||
1391 | // handle provided type arguments | ||
1392 | let method_ty = if let Some(generic_args) = generic_args { | ||
1393 | // if args are provided, it should be all of them, but we can't rely on that | ||
1394 | let param_count = def_generics.map(|g| g.params.len()).unwrap_or(0); | ||
1395 | let mut new_substs = Vec::with_capacity(generic_args.args.len()); | ||
1396 | for arg in generic_args.args.iter().take(param_count) { | ||
1397 | match arg { | ||
1398 | GenericArg::Type(type_ref) => { | ||
1399 | let ty = self.make_ty(type_ref); | ||
1400 | new_substs.push(ty); | ||
1401 | } | ||
1402 | } | ||
1372 | } | 1403 | } |
1373 | None => Ty::Unknown, | 1404 | let substs = method_ty.substs().unwrap_or_else(Substs::empty); |
1405 | let substs = substs.replace_tail(new_substs); | ||
1406 | method_ty.apply_substs(substs) | ||
1407 | } else { | ||
1408 | method_ty | ||
1374 | }; | 1409 | }; |
1375 | let method_ty = self.insert_type_vars(method_ty); | 1410 | let method_ty = self.insert_type_vars(method_ty); |
1376 | let (expected_receiver_ty, param_tys, ret_ty) = match &method_ty { | 1411 | let (expected_receiver_ty, param_tys, ret_ty) = match &method_ty { |
@@ -1394,9 +1429,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1394 | } | 1429 | } |
1395 | _ => (Ty::Unknown, Vec::new(), Ty::Unknown), | 1430 | _ => (Ty::Unknown, Vec::new(), Ty::Unknown), |
1396 | }; | 1431 | }; |
1397 | // TODO we would have to apply the autoderef/autoref steps here | 1432 | // Apply autoref so the below unification works correctly |
1398 | // to get the correct receiver type to unify... | 1433 | let actual_receiver_ty = match expected_receiver_ty { |
1399 | self.unify(&expected_receiver_ty, &receiver_ty); | 1434 | Ty::Ref(_, mutability) => Ty::Ref(Arc::new(derefed_receiver_ty), mutability), |
1435 | _ => derefed_receiver_ty, | ||
1436 | }; | ||
1437 | self.unify(&expected_receiver_ty, &actual_receiver_ty); | ||
1438 | |||
1400 | let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown)); | 1439 | let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown)); |
1401 | for (arg, param) in args.iter().zip(param_iter) { | 1440 | for (arg, param) in args.iter().zip(param_iter) { |
1402 | self.infer_expr(*arg, &Expectation::has_type(param)); | 1441 | self.infer_expr(*arg, &Expectation::has_type(param)); |
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 8d1076774..94b757af2 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs | |||
@@ -118,11 +118,13 @@ impl Ty { | |||
118 | // TODO: cache this as a query? | 118 | // TODO: cache this as a query? |
119 | // - if so, what signature? (TyFingerprint, Name)? | 119 | // - if so, what signature? (TyFingerprint, Name)? |
120 | // - or maybe cache all names and def_ids of methods per fingerprint? | 120 | // - or maybe cache all names and def_ids of methods per fingerprint? |
121 | pub fn lookup_method(self, db: &impl HirDatabase, name: &Name) -> Option<Function> { | 121 | /// Look up the method with the given name, returning the actual autoderefed |
122 | self.iterate_methods(db, |f| { | 122 | /// receiver type (but without autoref applied yet). |
123 | pub fn lookup_method(self, db: &impl HirDatabase, name: &Name) -> Option<(Ty, Function)> { | ||
124 | self.iterate_methods(db, |ty, f| { | ||
123 | let sig = f.signature(db); | 125 | let sig = f.signature(db); |
124 | if sig.name() == name && sig.has_self_param() { | 126 | if sig.name() == name && sig.has_self_param() { |
125 | Some(f) | 127 | Some((ty.clone(), f)) |
126 | } else { | 128 | } else { |
127 | None | 129 | None |
128 | } | 130 | } |
@@ -134,7 +136,7 @@ impl Ty { | |||
134 | pub fn iterate_methods<T>( | 136 | pub fn iterate_methods<T>( |
135 | self, | 137 | self, |
136 | db: &impl HirDatabase, | 138 | db: &impl HirDatabase, |
137 | mut callback: impl FnMut(Function) -> Option<T>, | 139 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, |
138 | ) -> Option<T> { | 140 | ) -> Option<T> { |
139 | // For method calls, rust first does any number of autoderef, and then one | 141 | // For method calls, rust first does any number of autoderef, and then one |
140 | // autoref (i.e. when the method takes &self or &mut self). We just ignore | 142 | // autoref (i.e. when the method takes &self or &mut self). We just ignore |
@@ -156,7 +158,7 @@ impl Ty { | |||
156 | for item in impl_block.items(db) { | 158 | for item in impl_block.items(db) { |
157 | match item { | 159 | match item { |
158 | ImplItem::Method(f) => { | 160 | ImplItem::Method(f) => { |
159 | if let Some(result) = callback(f) { | 161 | if let Some(result) = callback(&derefed_ty, f) { |
160 | return Some(result); | 162 | return Some(result); |
161 | } | 163 | } |
162 | } | 164 | } |
diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_generic_chain.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_generic_chain.snap index 626f31252..d1ce87b0a 100644 --- a/crates/ra_hir/src/ty/snapshots/tests__infer_generic_chain.snap +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_generic_chain.snap | |||
@@ -1,36 +1,36 @@ | |||
1 | --- | 1 | --- |
2 | created: "2019-01-27T14:52:29.938713255+00:00" | 2 | created: "2019-02-16T20:53:59.655361804Z" |
3 | creator: [email protected] | 3 | creator: [email protected] |
4 | expression: "&result" | ||
5 | source: crates/ra_hir/src/ty/tests.rs | 4 | source: crates/ra_hir/src/ty/tests.rs |
5 | expression: "&result" | ||
6 | --- | 6 | --- |
7 | [53; 57) 'self': A<[unknown]> | 7 | [53; 57) 'self': A<T2> |
8 | [65; 87) '{ ... }': [unknown] | 8 | [65; 87) '{ ... }': T2 |
9 | [75; 79) 'self': A<[unknown]> | 9 | [75; 79) 'self': A<T2> |
10 | [75; 81) 'self.x': [unknown] | 10 | [75; 81) 'self.x': T2 |
11 | [99; 100) 't': T | 11 | [99; 100) 't': T |
12 | [110; 115) '{ t }': T | 12 | [110; 115) '{ t }': T |
13 | [112; 113) 't': T | 13 | [112; 113) 't': T |
14 | [135; 261) '{ ....x() }': i128 | 14 | [135; 261) '{ ....x() }': i128 |
15 | [146; 147) 'x': i32 | 15 | [146; 147) 'x': i128 |
16 | [150; 151) '1': i32 | 16 | [150; 151) '1': i128 |
17 | [162; 163) 'y': i32 | 17 | [162; 163) 'y': i128 |
18 | [166; 168) 'id': fn id<i32>(T) -> T | 18 | [166; 168) 'id': fn id<i128>(T) -> T |
19 | [166; 171) 'id(x)': i32 | 19 | [166; 171) 'id(x)': i128 |
20 | [169; 170) 'x': i32 | 20 | [169; 170) 'x': i128 |
21 | [182; 183) 'a': A<i32> | 21 | [182; 183) 'a': A<i128> |
22 | [186; 200) 'A { x: id(y) }': A<i32> | 22 | [186; 200) 'A { x: id(y) }': A<i128> |
23 | [193; 195) 'id': fn id<i32>(T) -> T | 23 | [193; 195) 'id': fn id<i128>(T) -> T |
24 | [193; 198) 'id(y)': i32 | 24 | [193; 198) 'id(y)': i128 |
25 | [196; 197) 'y': i32 | 25 | [196; 197) 'y': i128 |
26 | [211; 212) 'z': i32 | 26 | [211; 212) 'z': i128 |
27 | [215; 217) 'id': fn id<i32>(T) -> T | 27 | [215; 217) 'id': fn id<i128>(T) -> T |
28 | [215; 222) 'id(a.x)': i32 | 28 | [215; 222) 'id(a.x)': i128 |
29 | [218; 219) 'a': A<i32> | 29 | [218; 219) 'a': A<i128> |
30 | [218; 221) 'a.x': i32 | 30 | [218; 221) 'a.x': i128 |
31 | [233; 234) 'b': A<i32> | 31 | [233; 234) 'b': A<i128> |
32 | [237; 247) 'A { x: z }': A<i32> | 32 | [237; 247) 'A { x: z }': A<i128> |
33 | [244; 245) 'z': i32 | 33 | [244; 245) 'z': i128 |
34 | [254; 255) 'b': A<i32> | 34 | [254; 255) 'b': A<i128> |
35 | [254; 259) 'b.x()': i128 | 35 | [254; 259) 'b.x()': i128 |
36 | 36 | ||
diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics.snap new file mode 100644 index 000000000..44f00a52e --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics.snap | |||
@@ -0,0 +1,39 @@ | |||
1 | --- | ||
2 | created: "2019-02-16T21:58:14.029368845Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_hir/src/ty/tests.rs | ||
5 | expression: "&result" | ||
6 | --- | ||
7 | [74; 78) 'self': A<X, Y> | ||
8 | [85; 107) '{ ... }': X | ||
9 | [95; 99) 'self': A<X, Y> | ||
10 | [95; 101) 'self.x': X | ||
11 | [117; 121) 'self': A<X, Y> | ||
12 | [128; 150) '{ ... }': Y | ||
13 | [138; 142) 'self': A<X, Y> | ||
14 | [138; 144) 'self.y': Y | ||
15 | [163; 167) 'self': A<X, Y> | ||
16 | [169; 170) 't': T | ||
17 | [188; 223) '{ ... }': (X, Y, T) | ||
18 | [198; 217) '(self.....y, t)': (X, Y, T) | ||
19 | [199; 203) 'self': A<X, Y> | ||
20 | [199; 205) 'self.x': X | ||
21 | [207; 211) 'self': A<X, Y> | ||
22 | [207; 213) 'self.y': Y | ||
23 | [215; 216) 't': T | ||
24 | [245; 342) '{ ...(1); }': () | ||
25 | [255; 256) 'a': A<u64, i64> | ||
26 | [259; 281) 'A { x:...1i64 }': A<u64, i64> | ||
27 | [266; 270) '1u64': u64 | ||
28 | [275; 279) '1i64': i64 | ||
29 | [287; 288) 'a': A<u64, i64> | ||
30 | [287; 292) 'a.x()': u64 | ||
31 | [298; 299) 'a': A<u64, i64> | ||
32 | [298; 303) 'a.y()': i64 | ||
33 | [309; 310) 'a': A<u64, i64> | ||
34 | [309; 319) 'a.z(1i128)': (u64, i64, i128) | ||
35 | [313; 318) '1i128': i128 | ||
36 | [325; 326) 'a': A<u64, i64> | ||
37 | [325; 339) 'a.z::<u128>(1)': (u64, i64, u128) | ||
38 | [337; 338) '1': u128 | ||
39 | |||
diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics_with_autoderef.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics_with_autoderef.snap new file mode 100644 index 000000000..f609eaf7c --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics_with_autoderef.snap | |||
@@ -0,0 +1,16 @@ | |||
1 | --- | ||
2 | created: "2019-02-17T13:35:06.385679926Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_hir/src/ty/tests.rs | ||
5 | expression: "&result" | ||
6 | --- | ||
7 | [78; 82) 'self': &Option<T> | ||
8 | [98; 100) '{}': () | ||
9 | [111; 112) 'o': Option<u32> | ||
10 | [127; 165) '{ ...f(); }': () | ||
11 | [133; 146) '(&o).as_ref()': Option<&u32> | ||
12 | [134; 136) '&o': &Option<u32> | ||
13 | [135; 136) 'o': Option<u32> | ||
14 | [152; 153) 'o': Option<u32> | ||
15 | [152; 162) 'o.as_ref()': Option<&u32> | ||
16 | |||
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 203f1fe4d..5eb9c4f5b 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -507,6 +507,58 @@ fn test() { | |||
507 | } | 507 | } |
508 | 508 | ||
509 | #[test] | 509 | #[test] |
510 | fn infer_impl_generics() { | ||
511 | check_inference( | ||
512 | "infer_impl_generics", | ||
513 | r#" | ||
514 | struct A<T1, T2> { | ||
515 | x: T1, | ||
516 | y: T2, | ||
517 | } | ||
518 | impl<Y, X> A<X, Y> { | ||
519 | fn x(self) -> X { | ||
520 | self.x | ||
521 | } | ||
522 | fn y(self) -> Y { | ||
523 | self.y | ||
524 | } | ||
525 | fn z<T>(self, t: T) -> (X, Y, T) { | ||
526 | (self.x, self.y, t) | ||
527 | } | ||
528 | } | ||
529 | |||
530 | fn test() -> i128 { | ||
531 | let a = A { x: 1u64, y: 1i64 }; | ||
532 | a.x(); | ||
533 | a.y(); | ||
534 | a.z(1i128); | ||
535 | a.z::<u128>(1); | ||
536 | } | ||
537 | "#, | ||
538 | ); | ||
539 | } | ||
540 | |||
541 | #[test] | ||
542 | fn infer_impl_generics_with_autoderef() { | ||
543 | check_inference( | ||
544 | "infer_impl_generics_with_autoderef", | ||
545 | r#" | ||
546 | enum Option<T> { | ||
547 | Some(T), | ||
548 | None, | ||
549 | } | ||
550 | impl<T> Option<T> { | ||
551 | fn as_ref(&self) -> Option<&T> {} | ||
552 | } | ||
553 | fn test(o: Option<u32>) { | ||
554 | (&o).as_ref(); | ||
555 | o.as_ref(); | ||
556 | } | ||
557 | "#, | ||
558 | ); | ||
559 | } | ||
560 | |||
561 | #[test] | ||
510 | fn infer_generic_chain() { | 562 | fn infer_generic_chain() { |
511 | check_inference( | 563 | check_inference( |
512 | "infer_generic_chain", | 564 | "infer_generic_chain", |
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs index be839345f..20fa323ce 100644 --- a/crates/ra_ide_api/src/completion/complete_dot.rs +++ b/crates/ra_ide_api/src/completion/complete_dot.rs | |||
@@ -63,7 +63,7 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) | |||
63 | } | 63 | } |
64 | 64 | ||
65 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) { | 65 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) { |
66 | receiver.iterate_methods(ctx.db, |func| { | 66 | receiver.iterate_methods(ctx.db, |_ty, func| { |
67 | let sig = func.signature(ctx.db); | 67 | let sig = func.signature(ctx.db); |
68 | if sig.has_self_param() { | 68 | if sig.has_self_param() { |
69 | CompletionItem::new( | 69 | CompletionItem::new( |
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 7c5e8ce5e..dd91b5063 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -1352,6 +1352,7 @@ impl ToOwned for ImplBlock { | |||
1352 | } | 1352 | } |
1353 | 1353 | ||
1354 | 1354 | ||
1355 | impl ast::TypeParamsOwner for ImplBlock {} | ||
1355 | impl ImplBlock { | 1356 | impl ImplBlock { |
1356 | pub fn item_list(&self) -> Option<&ItemList> { | 1357 | pub fn item_list(&self) -> Option<&ItemList> { |
1357 | super::child_opt(self) | 1358 | super::child_opt(self) |
@@ -2094,6 +2095,10 @@ impl MethodCallExpr { | |||
2094 | pub fn name_ref(&self) -> Option<&NameRef> { | 2095 | pub fn name_ref(&self) -> Option<&NameRef> { |
2095 | super::child_opt(self) | 2096 | super::child_opt(self) |
2096 | } | 2097 | } |
2098 | |||
2099 | pub fn type_arg_list(&self) -> Option<&TypeArgList> { | ||
2100 | super::child_opt(self) | ||
2101 | } | ||
2097 | } | 2102 | } |
2098 | 2103 | ||
2099 | // Module | 2104 | // Module |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 304bc5909..27a123681 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -322,7 +322,7 @@ Grammar( | |||
322 | ], | 322 | ], |
323 | options: ["TypeRef"] | 323 | options: ["TypeRef"] |
324 | ), | 324 | ), |
325 | "ImplBlock": (options: ["ItemList"]), | 325 | "ImplBlock": (options: ["ItemList"], traits: ["TypeParamsOwner"]), |
326 | 326 | ||
327 | "ParenType": (options: ["TypeRef"]), | 327 | "ParenType": (options: ["TypeRef"]), |
328 | "TupleType": ( collections: [["fields", "TypeRef"]] ), | 328 | "TupleType": ( collections: [["fields", "TypeRef"]] ), |
@@ -431,7 +431,7 @@ Grammar( | |||
431 | ), | 431 | ), |
432 | "MethodCallExpr": ( | 432 | "MethodCallExpr": ( |
433 | traits: ["ArgListOwner"], | 433 | traits: ["ArgListOwner"], |
434 | options: [ "Expr", "NameRef" ], | 434 | options: [ "Expr", "NameRef", "TypeArgList" ], |
435 | ), | 435 | ), |
436 | "IndexExpr": (), | 436 | "IndexExpr": (), |
437 | "FieldExpr": (options: ["Expr", "NameRef"]), | 437 | "FieldExpr": (options: ["Expr", "NameRef"]), |