diff options
20 files changed, 384 insertions, 76 deletions
diff --git a/.gitignore b/.gitignore index 872f72f16..99e0c5003 100644 --- a/.gitignore +++ b/.gitignore | |||
@@ -1,6 +1,7 @@ | |||
1 | /target/ | 1 | /target/ |
2 | crates/*/target | 2 | crates/*/target |
3 | **/*.rs.bk | 3 | **/*.rs.bk |
4 | **/*.rs.pending-snap | ||
4 | .idea/* | 5 | .idea/* |
5 | *.log | 6 | *.log |
6 | *.iml | 7 | *.iml |
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index b2a237ece..589a9b2db 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs | |||
@@ -6,7 +6,7 @@ use rustc_hash::FxHashMap; | |||
6 | use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; | 6 | use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; |
7 | use ra_syntax::{ | 7 | use ra_syntax::{ |
8 | SyntaxNodePtr, AstPtr, AstNode, | 8 | SyntaxNodePtr, AstPtr, AstNode, |
9 | ast::{self, LoopBodyOwner, ArgListOwner, NameOwner, LiteralKind, TypeAscriptionOwner} | 9 | ast::{self, LoopBodyOwner, ArgListOwner, NameOwner, LiteralKind,ArrayExprKind, TypeAscriptionOwner} |
10 | }; | 10 | }; |
11 | 11 | ||
12 | use crate::{ | 12 | use crate::{ |
@@ -238,14 +238,17 @@ pub enum Expr { | |||
238 | Tuple { | 238 | Tuple { |
239 | exprs: Vec<ExprId>, | 239 | exprs: Vec<ExprId>, |
240 | }, | 240 | }, |
241 | Array { | 241 | Array(Array), |
242 | exprs: Vec<ExprId>, | ||
243 | }, | ||
244 | Literal(Literal), | 242 | Literal(Literal), |
245 | } | 243 | } |
246 | 244 | ||
247 | pub use ra_syntax::ast::PrefixOp as UnaryOp; | 245 | pub use ra_syntax::ast::PrefixOp as UnaryOp; |
248 | pub use ra_syntax::ast::BinOp as BinaryOp; | 246 | pub use ra_syntax::ast::BinOp as BinaryOp; |
247 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
248 | pub enum Array { | ||
249 | ElementList(Vec<ExprId>), | ||
250 | Repeat { initializer: ExprId, repeat: ExprId }, | ||
251 | } | ||
249 | 252 | ||
250 | #[derive(Debug, Clone, Eq, PartialEq)] | 253 | #[derive(Debug, Clone, Eq, PartialEq)] |
251 | pub struct MatchArm { | 254 | pub struct MatchArm { |
@@ -348,11 +351,22 @@ impl Expr { | |||
348 | | Expr::UnaryOp { expr, .. } => { | 351 | | Expr::UnaryOp { expr, .. } => { |
349 | f(*expr); | 352 | f(*expr); |
350 | } | 353 | } |
351 | Expr::Tuple { exprs } | Expr::Array { exprs } => { | 354 | Expr::Tuple { exprs } => { |
352 | for expr in exprs { | 355 | for expr in exprs { |
353 | f(*expr); | 356 | f(*expr); |
354 | } | 357 | } |
355 | } | 358 | } |
359 | Expr::Array(a) => match a { | ||
360 | Array::ElementList(exprs) => { | ||
361 | for expr in exprs { | ||
362 | f(*expr); | ||
363 | } | ||
364 | } | ||
365 | Array::Repeat { initializer, repeat } => { | ||
366 | f(*initializer); | ||
367 | f(*repeat) | ||
368 | } | ||
369 | }, | ||
356 | Expr::Literal(_) => {} | 370 | Expr::Literal(_) => {} |
357 | } | 371 | } |
358 | } | 372 | } |
@@ -671,7 +685,10 @@ impl ExprCollector { | |||
671 | } | 685 | } |
672 | ast::ExprKind::FieldExpr(e) => { | 686 | ast::ExprKind::FieldExpr(e) => { |
673 | let expr = self.collect_expr_opt(e.expr()); | 687 | let expr = self.collect_expr_opt(e.expr()); |
674 | let name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); | 688 | let name = match e.field_access() { |
689 | Some(kind) => kind.as_name(), | ||
690 | _ => Name::missing(), | ||
691 | }; | ||
675 | self.alloc_expr(Expr::Field { expr, name }, syntax_ptr) | 692 | self.alloc_expr(Expr::Field { expr, name }, syntax_ptr) |
676 | } | 693 | } |
677 | ast::ExprKind::TryExpr(e) => { | 694 | ast::ExprKind::TryExpr(e) => { |
@@ -720,10 +737,26 @@ impl ExprCollector { | |||
720 | let exprs = e.exprs().map(|expr| self.collect_expr(expr)).collect(); | 737 | let exprs = e.exprs().map(|expr| self.collect_expr(expr)).collect(); |
721 | self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr) | 738 | self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr) |
722 | } | 739 | } |
740 | |||
723 | ast::ExprKind::ArrayExpr(e) => { | 741 | ast::ExprKind::ArrayExpr(e) => { |
724 | let exprs = e.exprs().map(|expr| self.collect_expr(expr)).collect(); | 742 | let kind = e.kind(); |
725 | self.alloc_expr(Expr::Array { exprs }, syntax_ptr) | 743 | |
744 | match kind { | ||
745 | ArrayExprKind::ElementList(e) => { | ||
746 | let exprs = e.map(|expr| self.collect_expr(expr)).collect(); | ||
747 | self.alloc_expr(Expr::Array(Array::ElementList(exprs)), syntax_ptr) | ||
748 | } | ||
749 | ArrayExprKind::Repeat { initializer, repeat } => { | ||
750 | let initializer = self.collect_expr_opt(initializer); | ||
751 | let repeat = self.collect_expr_opt(repeat); | ||
752 | self.alloc_expr( | ||
753 | Expr::Array(Array::Repeat { initializer, repeat }), | ||
754 | syntax_ptr, | ||
755 | ) | ||
756 | } | ||
757 | } | ||
726 | } | 758 | } |
759 | |||
727 | ast::ExprKind::Literal(e) => { | 760 | ast::ExprKind::Literal(e) => { |
728 | let lit = match e.kind() { | 761 | let lit = match e.kind() { |
729 | LiteralKind::IntNumber { suffix } => { | 762 | LiteralKind::IntNumber { suffix } => { |
diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs index 677d18efc..283f37845 100644 --- a/crates/ra_hir/src/name.rs +++ b/crates/ra_hir/src/name.rs | |||
@@ -90,6 +90,15 @@ impl AsName for ast::Name { | |||
90 | } | 90 | } |
91 | } | 91 | } |
92 | 92 | ||
93 | impl<'a> AsName for ast::FieldKind<'a> { | ||
94 | fn as_name(&self) -> Name { | ||
95 | match self { | ||
96 | ast::FieldKind::Name(nr) => nr.as_name(), | ||
97 | ast::FieldKind::Index(idx) => Name::new(idx.text().clone()), | ||
98 | } | ||
99 | } | ||
100 | } | ||
101 | |||
93 | impl AsName for ra_db::Dependency { | 102 | impl AsName for ra_db::Dependency { |
94 | fn as_name(&self) -> Name { | 103 | fn as_name(&self) -> Name { |
95 | Name::new(self.name.clone()) | 104 | Name::new(self.name.clone()) |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index d42c61e9d..20e55d92d 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -353,10 +353,14 @@ impl HirDisplay for ApplicationTy { | |||
353 | TypeCtor::Int(t) => write!(f, "{}", t)?, | 353 | TypeCtor::Int(t) => write!(f, "{}", t)?, |
354 | TypeCtor::Float(t) => write!(f, "{}", t)?, | 354 | TypeCtor::Float(t) => write!(f, "{}", t)?, |
355 | TypeCtor::Str => write!(f, "str")?, | 355 | TypeCtor::Str => write!(f, "str")?, |
356 | TypeCtor::Slice | TypeCtor::Array => { | 356 | TypeCtor::Slice => { |
357 | let t = self.parameters.as_single(); | 357 | let t = self.parameters.as_single(); |
358 | write!(f, "[{}]", t.display(f.db))?; | 358 | write!(f, "[{}]", t.display(f.db))?; |
359 | } | 359 | } |
360 | TypeCtor::Array => { | ||
361 | let t = self.parameters.as_single(); | ||
362 | write!(f, "[{};_]", t.display(f.db))?; | ||
363 | } | ||
360 | TypeCtor::RawPtr(m) => { | 364 | TypeCtor::RawPtr(m) => { |
361 | let t = self.parameters.as_single(); | 365 | let t = self.parameters.as_single(); |
362 | write!(f, "*{}{}", m.as_keyword_for_ptr(), t.display(f.db))?; | 366 | write!(f, "*{}{}", m.as_keyword_for_ptr(), t.display(f.db))?; |
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 887153484..28947be51 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs | |||
@@ -32,7 +32,7 @@ use crate::{ | |||
32 | DefWithBody, | 32 | DefWithBody, |
33 | ImplItem, | 33 | ImplItem, |
34 | type_ref::{TypeRef, Mutability}, | 34 | type_ref::{TypeRef, Mutability}, |
35 | expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat, self}, | 35 | expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat,Array, self}, |
36 | generics::GenericParams, | 36 | generics::GenericParams, |
37 | path::{GenericArgs, GenericArg}, | 37 | path::{GenericArgs, GenericArg}, |
38 | adt::VariantDef, | 38 | adt::VariantDef, |
@@ -489,8 +489,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
489 | Some(ty) | 489 | Some(ty) |
490 | } | 490 | } |
491 | Resolution::LocalBinding(pat) => { | 491 | Resolution::LocalBinding(pat) => { |
492 | let ty = self.type_of_pat.get(pat)?; | 492 | let ty = self.type_of_pat.get(pat)?.clone(); |
493 | let ty = self.resolve_ty_as_possible(&mut vec![], ty.clone()); | 493 | let ty = self.resolve_ty_as_possible(&mut vec![], ty); |
494 | Some(ty) | 494 | Some(ty) |
495 | } | 495 | } |
496 | Resolution::GenericParam(..) => { | 496 | Resolution::GenericParam(..) => { |
@@ -1074,7 +1074,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1074 | 1074 | ||
1075 | Ty::apply(TypeCtor::Tuple, Substs(ty_vec.into())) | 1075 | Ty::apply(TypeCtor::Tuple, Substs(ty_vec.into())) |
1076 | } | 1076 | } |
1077 | Expr::Array { exprs } => { | 1077 | Expr::Array(array) => { |
1078 | let elem_ty = match &expected.ty { | 1078 | let elem_ty = match &expected.ty { |
1079 | Ty::Apply(a_ty) => match a_ty.ctor { | 1079 | Ty::Apply(a_ty) => match a_ty.ctor { |
1080 | TypeCtor::Slice | TypeCtor::Array => { | 1080 | TypeCtor::Slice | TypeCtor::Array => { |
@@ -1085,8 +1085,21 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1085 | _ => self.new_type_var(), | 1085 | _ => self.new_type_var(), |
1086 | }; | 1086 | }; |
1087 | 1087 | ||
1088 | for expr in exprs.iter() { | 1088 | match array { |
1089 | self.infer_expr(*expr, &Expectation::has_type(elem_ty.clone())); | 1089 | Array::ElementList(items) => { |
1090 | for expr in items.iter() { | ||
1091 | self.infer_expr(*expr, &Expectation::has_type(elem_ty.clone())); | ||
1092 | } | ||
1093 | } | ||
1094 | Array::Repeat { initializer, repeat } => { | ||
1095 | self.infer_expr(*initializer, &Expectation::has_type(elem_ty.clone())); | ||
1096 | self.infer_expr( | ||
1097 | *repeat, | ||
1098 | &Expectation::has_type(Ty::simple(TypeCtor::Int( | ||
1099 | primitive::UncertainIntTy::Known(primitive::IntTy::usize()), | ||
1100 | ))), | ||
1101 | ); | ||
1102 | } | ||
1090 | } | 1103 | } |
1091 | 1104 | ||
1092 | Ty::apply_one(TypeCtor::Array, elem_ty) | 1105 | Ty::apply_one(TypeCtor::Array, elem_ty) |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 0b7c841df..f6a325033 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -697,58 +697,58 @@ fn test(x: &str, y: isize) { | |||
697 | [9; 10) 'x': &str | 697 | [9; 10) 'x': &str |
698 | [18; 19) 'y': isize | 698 | [18; 19) 'y': isize |
699 | [28; 324) '{ ... 3]; }': () | 699 | [28; 324) '{ ... 3]; }': () |
700 | [38; 39) 'a': [&str] | 700 | [38; 39) 'a': [&str;_] |
701 | [42; 45) '[x]': [&str] | 701 | [42; 45) '[x]': [&str;_] |
702 | [43; 44) 'x': &str | 702 | [43; 44) 'x': &str |
703 | [55; 56) 'b': [[&str]] | 703 | [55; 56) 'b': [[&str;_];_] |
704 | [59; 65) '[a, a]': [[&str]] | 704 | [59; 65) '[a, a]': [[&str;_];_] |
705 | [60; 61) 'a': [&str] | 705 | [60; 61) 'a': [&str;_] |
706 | [63; 64) 'a': [&str] | 706 | [63; 64) 'a': [&str;_] |
707 | [75; 76) 'c': [[[&str]]] | 707 | [75; 76) 'c': [[[&str;_];_];_] |
708 | [79; 85) '[b, b]': [[[&str]]] | 708 | [79; 85) '[b, b]': [[[&str;_];_];_] |
709 | [80; 81) 'b': [[&str]] | 709 | [80; 81) 'b': [[&str;_];_] |
710 | [83; 84) 'b': [[&str]] | 710 | [83; 84) 'b': [[&str;_];_] |
711 | [96; 97) 'd': [isize] | 711 | [96; 97) 'd': [isize;_] |
712 | [100; 112) '[y, 1, 2, 3]': [isize] | 712 | [100; 112) '[y, 1, 2, 3]': [isize;_] |
713 | [101; 102) 'y': isize | 713 | [101; 102) 'y': isize |
714 | [104; 105) '1': isize | 714 | [104; 105) '1': isize |
715 | [107; 108) '2': isize | 715 | [107; 108) '2': isize |
716 | [110; 111) '3': isize | 716 | [110; 111) '3': isize |
717 | [122; 123) 'd': [isize] | 717 | [122; 123) 'd': [isize;_] |
718 | [126; 138) '[1, y, 2, 3]': [isize] | 718 | [126; 138) '[1, y, 2, 3]': [isize;_] |
719 | [127; 128) '1': isize | 719 | [127; 128) '1': isize |
720 | [130; 131) 'y': isize | 720 | [130; 131) 'y': isize |
721 | [133; 134) '2': isize | 721 | [133; 134) '2': isize |
722 | [136; 137) '3': isize | 722 | [136; 137) '3': isize |
723 | [148; 149) 'e': [isize] | 723 | [148; 149) 'e': [isize;_] |
724 | [152; 155) '[y]': [isize] | 724 | [152; 155) '[y]': [isize;_] |
725 | [153; 154) 'y': isize | 725 | [153; 154) 'y': isize |
726 | [165; 166) 'f': [[isize]] | 726 | [165; 166) 'f': [[isize;_];_] |
727 | [169; 175) '[d, d]': [[isize]] | 727 | [169; 175) '[d, d]': [[isize;_];_] |
728 | [170; 171) 'd': [isize] | 728 | [170; 171) 'd': [isize;_] |
729 | [173; 174) 'd': [isize] | 729 | [173; 174) 'd': [isize;_] |
730 | [185; 186) 'g': [[isize]] | 730 | [185; 186) 'g': [[isize;_];_] |
731 | [189; 195) '[e, e]': [[isize]] | 731 | [189; 195) '[e, e]': [[isize;_];_] |
732 | [190; 191) 'e': [isize] | 732 | [190; 191) 'e': [isize;_] |
733 | [193; 194) 'e': [isize] | 733 | [193; 194) 'e': [isize;_] |
734 | [206; 207) 'h': [i32] | 734 | [206; 207) 'h': [i32;_] |
735 | [210; 216) '[1, 2]': [i32] | 735 | [210; 216) '[1, 2]': [i32;_] |
736 | [211; 212) '1': i32 | 736 | [211; 212) '1': i32 |
737 | [214; 215) '2': i32 | 737 | [214; 215) '2': i32 |
738 | [226; 227) 'i': [&str] | 738 | [226; 227) 'i': [&str;_] |
739 | [230; 240) '["a", "b"]': [&str] | 739 | [230; 240) '["a", "b"]': [&str;_] |
740 | [231; 234) '"a"': &str | 740 | [231; 234) '"a"': &str |
741 | [236; 239) '"b"': &str | 741 | [236; 239) '"b"': &str |
742 | [251; 252) 'b': [[&str]] | 742 | [251; 252) 'b': [[&str;_];_] |
743 | [255; 265) '[a, ["b"]]': [[&str]] | 743 | [255; 265) '[a, ["b"]]': [[&str;_];_] |
744 | [256; 257) 'a': [&str] | 744 | [256; 257) 'a': [&str;_] |
745 | [259; 264) '["b"]': [&str] | 745 | [259; 264) '["b"]': [&str;_] |
746 | [260; 263) '"b"': &str | 746 | [260; 263) '"b"': &str |
747 | [275; 276) 'x': [u8] | 747 | [275; 276) 'x': [u8;_] |
748 | [288; 290) '[]': [u8] | 748 | [288; 290) '[]': [u8;_] |
749 | [300; 301) 'z': &[u8] | 749 | [300; 301) 'z': &[u8;_] |
750 | [311; 321) '&[1, 2, 3]': &[u8] | 750 | [311; 321) '&[1, 2, 3]': &[u8;_] |
751 | [312; 321) '[1, 2, 3]': [u8] | 751 | [312; 321) '[1, 2, 3]': [u8;_] |
752 | [313; 314) '1': u8 | 752 | [313; 314) '1': u8 |
753 | [316; 317) '2': u8 | 753 | [316; 317) '2': u8 |
754 | [319; 320) '3': u8"### | 754 | [319; 320) '3': u8"### |
@@ -1553,7 +1553,7 @@ fn test() { | |||
1553 | [11; 48) '{ ...&y]; }': () | 1553 | [11; 48) '{ ...&y]; }': () |
1554 | [21; 22) 'y': &{unknown} | 1554 | [21; 22) 'y': &{unknown} |
1555 | [25; 32) 'unknown': &{unknown} | 1555 | [25; 32) 'unknown': &{unknown} |
1556 | [38; 45) '[y, &y]': [&&{unknown}] | 1556 | [38; 45) '[y, &y]': [&&{unknown};_] |
1557 | [39; 40) 'y': &{unknown} | 1557 | [39; 40) 'y': &{unknown} |
1558 | [42; 44) '&y': &&{unknown} | 1558 | [42; 44) '&y': &&{unknown} |
1559 | [43; 44) 'y': &{unknown}"### | 1559 | [43; 44) 'y': &{unknown}"### |
@@ -1578,7 +1578,7 @@ fn test() { | |||
1578 | [25; 32) 'unknown': &&{unknown} | 1578 | [25; 32) 'unknown': &&{unknown} |
1579 | [42; 43) 'y': &&{unknown} | 1579 | [42; 43) 'y': &&{unknown} |
1580 | [46; 53) 'unknown': &&{unknown} | 1580 | [46; 53) 'unknown': &&{unknown} |
1581 | [59; 77) '[(x, y..., &x)]': [(&&{unknown}, &&{unknown})] | 1581 | [59; 77) '[(x, y..., &x)]': [(&&{unknown}, &&{unknown});_] |
1582 | [60; 66) '(x, y)': (&&{unknown}, &&{unknown}) | 1582 | [60; 66) '(x, y)': (&&{unknown}, &&{unknown}) |
1583 | [61; 62) 'x': &&{unknown} | 1583 | [61; 62) 'x': &&{unknown} |
1584 | [64; 65) 'y': &&{unknown} | 1584 | [64; 65) 'y': &&{unknown} |
@@ -1670,8 +1670,8 @@ fn test_line_buffer() { | |||
1670 | "#), | 1670 | "#), |
1671 | @r###" | 1671 | @r###" |
1672 | [23; 53) '{ ...n']; }': () | 1672 | [23; 53) '{ ...n']; }': () |
1673 | [29; 50) '&[0, b...b'\n']': &[u8] | 1673 | [29; 50) '&[0, b...b'\n']': &[u8;_] |
1674 | [30; 50) '[0, b'...b'\n']': [u8] | 1674 | [30; 50) '[0, b'...b'\n']': [u8;_] |
1675 | [31; 32) '0': u8 | 1675 | [31; 32) '0': u8 |
1676 | [34; 39) 'b'\n'': u8 | 1676 | [34; 39) 'b'\n'': u8 |
1677 | [41; 42) '1': u8 | 1677 | [41; 42) '1': u8 |
@@ -2242,6 +2242,65 @@ static B: u64 = { let x = 1; x }; | |||
2242 | ); | 2242 | ); |
2243 | } | 2243 | } |
2244 | 2244 | ||
2245 | #[test] | ||
2246 | fn tuple_struct_fields() { | ||
2247 | assert_snapshot_matches!( | ||
2248 | infer(r#" | ||
2249 | struct S(i32, u64); | ||
2250 | fn test() -> u64 { | ||
2251 | let a = S(4, 6); | ||
2252 | let b = a.0; | ||
2253 | a.1 | ||
2254 | } | ||
2255 | "#), | ||
2256 | @r###" | ||
2257 | [38; 87) '{ ... a.1 }': u64 | ||
2258 | [48; 49) 'a': S | ||
2259 | [52; 53) 'S': S(i32, u64) -> S | ||
2260 | [52; 59) 'S(4, 6)': S | ||
2261 | [54; 55) '4': i32 | ||
2262 | [57; 58) '6': u64 | ||
2263 | [69; 70) 'b': i32 | ||
2264 | [73; 74) 'a': S | ||
2265 | [73; 76) 'a.0': i32 | ||
2266 | [82; 83) 'a': S | ||
2267 | [82; 85) 'a.1': u64"### | ||
2268 | ); | ||
2269 | } | ||
2270 | |||
2271 | #[test] | ||
2272 | fn tuple_struct_with_fn() { | ||
2273 | assert_snapshot_matches!( | ||
2274 | infer(r#" | ||
2275 | struct S(fn(u32) -> u64); | ||
2276 | fn test() -> u64 { | ||
2277 | let a = S(|i| 2*i); | ||
2278 | let b = a.0(4); | ||
2279 | a.0(2) | ||
2280 | } | ||
2281 | "#), | ||
2282 | @r###" | ||
2283 | [44; 102) '{ ...0(2) }': u64 | ||
2284 | [54; 55) 'a': S | ||
2285 | [58; 59) 'S': S(fn(u32) -> u64) -> S | ||
2286 | [58; 68) 'S(|i| 2*i)': S | ||
2287 | [60; 67) '|i| 2*i': fn(u32) -> u64 | ||
2288 | [61; 62) 'i': i32 | ||
2289 | [64; 65) '2': i32 | ||
2290 | [64; 67) '2*i': i32 | ||
2291 | [66; 67) 'i': i32 | ||
2292 | [78; 79) 'b': u64 | ||
2293 | [82; 83) 'a': S | ||
2294 | [82; 85) 'a.0': fn(u32) -> u64 | ||
2295 | [82; 88) 'a.0(4)': u64 | ||
2296 | [86; 87) '4': u32 | ||
2297 | [94; 95) 'a': S | ||
2298 | [94; 97) 'a.0': fn(u32) -> u64 | ||
2299 | [94; 100) 'a.0(2)': u64 | ||
2300 | [98; 99) '2': u32"### | ||
2301 | ); | ||
2302 | } | ||
2303 | |||
2245 | fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { | 2304 | fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { |
2246 | let func = source_binder::function_from_position(db, pos).unwrap(); | 2305 | let func = source_binder::function_from_position(db, pos).unwrap(); |
2247 | let body_source_map = func.body_source_map(db); | 2306 | let body_source_map = func.body_source_map(db); |
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs index f54a02d1d..18b2d68d5 100644 --- a/crates/ra_ide_api/src/completion/complete_dot.rs +++ b/crates/ra_ide_api/src/completion/complete_dot.rs | |||
@@ -184,4 +184,26 @@ mod tests { | |||
184 | ", | 184 | ", |
185 | ); | 185 | ); |
186 | } | 186 | } |
187 | |||
188 | #[test] | ||
189 | fn test_tuple_field_inference() { | ||
190 | check_ref_completion( | ||
191 | "tuple_field_inference", | ||
192 | r" | ||
193 | pub struct S; | ||
194 | impl S { | ||
195 | pub fn blah(&self) {} | ||
196 | } | ||
197 | |||
198 | struct T(S); | ||
199 | |||
200 | impl T { | ||
201 | fn foo(&self) { | ||
202 | // FIXME: This doesn't work without the trailing `a` as `0.` is a float | ||
203 | self.0.a<|> | ||
204 | } | ||
205 | } | ||
206 | ", | ||
207 | ); | ||
208 | } | ||
187 | } | 209 | } |
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__tuple_field_inference.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__tuple_field_inference.snap new file mode 100644 index 000000000..72c8973b8 --- /dev/null +++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__tuple_field_inference.snap | |||
@@ -0,0 +1,16 @@ | |||
1 | --- | ||
2 | created: "2019-04-05T23:00:18.283812700Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | ||
5 | expression: kind_completions | ||
6 | --- | ||
7 | [ | ||
8 | CompletionItem { | ||
9 | label: "blah", | ||
10 | source_range: [299; 300), | ||
11 | delete: [299; 300), | ||
12 | insert: "blah()$0", | ||
13 | kind: Method, | ||
14 | detail: "pub fn blah(&self)" | ||
15 | } | ||
16 | ] | ||
diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs index af4798494..4b1d592bb 100644 --- a/crates/ra_lsp_server/src/server_world.rs +++ b/crates/ra_lsp_server/src/server_world.rs | |||
@@ -11,12 +11,14 @@ use ra_ide_api::{ | |||
11 | use ra_vfs::{Vfs, VfsChange, VfsFile, VfsRoot}; | 11 | use ra_vfs::{Vfs, VfsChange, VfsFile, VfsRoot}; |
12 | use relative_path::RelativePathBuf; | 12 | use relative_path::RelativePathBuf; |
13 | use parking_lot::RwLock; | 13 | use parking_lot::RwLock; |
14 | use failure::format_err; | 14 | use failure::{Error, format_err}; |
15 | use gen_lsp_server::ErrorCode; | ||
15 | 16 | ||
16 | use crate::{ | 17 | use crate::{ |
17 | project_model::ProjectWorkspace, | 18 | project_model::ProjectWorkspace, |
18 | vfs_filter::IncludeRustFiles, | 19 | vfs_filter::IncludeRustFiles, |
19 | Result, | 20 | Result, |
21 | LspError, | ||
20 | }; | 22 | }; |
21 | 23 | ||
22 | #[derive(Debug)] | 24 | #[derive(Debug)] |
@@ -152,11 +154,13 @@ impl ServerWorld { | |||
152 | 154 | ||
153 | pub fn uri_to_file_id(&self, uri: &Url) -> Result<FileId> { | 155 | pub fn uri_to_file_id(&self, uri: &Url) -> Result<FileId> { |
154 | let path = uri.to_file_path().map_err(|()| format_err!("invalid uri: {}", uri))?; | 156 | let path = uri.to_file_path().map_err(|()| format_err!("invalid uri: {}", uri))?; |
155 | let file = self | 157 | let file = self.vfs.read().path2file(&path).ok_or_else(|| { |
156 | .vfs | 158 | // Show warning as this file is outside current workspace |
157 | .read() | 159 | Error::from(LspError { |
158 | .path2file(&path) | 160 | code: ErrorCode::InvalidRequest as i32, |
159 | .ok_or_else(|| format_err!("unknown file: {}", path.display()))?; | 161 | message: "Rust file outside current workspace is not supported yet.".to_string(), |
162 | }) | ||
163 | })?; | ||
160 | Ok(FileId(file.0.into())) | 164 | Ok(FileId(file.0.into())) |
161 | } | 165 | } |
162 | 166 | ||
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs index bf5d6544d..9b38b0a31 100644 --- a/crates/ra_parser/src/grammar/expressions.rs +++ b/crates/ra_parser/src/grammar/expressions.rs | |||
@@ -379,6 +379,14 @@ fn method_call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker { | |||
379 | // fn foo() { | 379 | // fn foo() { |
380 | // x.foo; | 380 | // x.foo; |
381 | // x.0.bar; | 381 | // x.0.bar; |
382 | // x.0(); | ||
383 | // } | ||
384 | |||
385 | // test_err bad_tuple_index_expr | ||
386 | // fn foo() { | ||
387 | // x.0.; | ||
388 | // x.1i32; | ||
389 | // x.0x01; | ||
382 | // } | 390 | // } |
383 | fn field_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker { | 391 | fn field_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker { |
384 | assert!(p.at(DOT)); | 392 | assert!(p.at(DOT)); |
@@ -387,7 +395,10 @@ fn field_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker { | |||
387 | if p.at(IDENT) { | 395 | if p.at(IDENT) { |
388 | name_ref(p) | 396 | name_ref(p) |
389 | } else if p.at(INT_NUMBER) { | 397 | } else if p.at(INT_NUMBER) { |
390 | p.bump() | 398 | p.bump(); |
399 | } else if p.at(FLOAT_NUMBER) { | ||
400 | // FIXME: How to recover and instead parse INT + DOT? | ||
401 | p.bump(); | ||
391 | } else { | 402 | } else { |
392 | p.error("expected field name or number") | 403 | p.error("expected field name or number") |
393 | } | 404 | } |
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 9f5c41b0c..c2ab19d97 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -17,8 +17,8 @@ pub use self::{ | |||
17 | generated::*, | 17 | generated::*, |
18 | traits::*, | 18 | traits::*, |
19 | tokens::*, | 19 | tokens::*, |
20 | extensions::{PathSegmentKind, StructKind, SelfParamKind}, | 20 | extensions::{PathSegmentKind, StructKind,FieldKind, SelfParamKind}, |
21 | expr_extensions::{ElseBranch, PrefixOp, BinOp, LiteralKind}, | 21 | expr_extensions::{ElseBranch, PrefixOp, BinOp, LiteralKind,ArrayExprKind}, |
22 | }; | 22 | }; |
23 | 23 | ||
24 | /// The main trait to go from untyped `SyntaxNode` to a typed ast. The | 24 | /// The main trait to go from untyped `SyntaxNode` to a typed ast. The |
diff --git a/crates/ra_syntax/src/ast/expr_extensions.rs b/crates/ra_syntax/src/ast/expr_extensions.rs index 1d8313810..9484c3b9b 100644 --- a/crates/ra_syntax/src/ast/expr_extensions.rs +++ b/crates/ra_syntax/src/ast/expr_extensions.rs | |||
@@ -193,6 +193,28 @@ impl ast::BinExpr { | |||
193 | } | 193 | } |
194 | } | 194 | } |
195 | 195 | ||
196 | pub enum ArrayExprKind<'a> { | ||
197 | Repeat { initializer: Option<&'a ast::Expr>, repeat: Option<&'a ast::Expr> }, | ||
198 | ElementList(AstChildren<'a, ast::Expr>), | ||
199 | } | ||
200 | |||
201 | impl ast::ArrayExpr { | ||
202 | pub fn kind(&self) -> ArrayExprKind { | ||
203 | if self.is_repeat() { | ||
204 | ArrayExprKind::Repeat { | ||
205 | initializer: children(self).nth(0), | ||
206 | repeat: children(self).nth(1), | ||
207 | } | ||
208 | } else { | ||
209 | ArrayExprKind::ElementList(children(self)) | ||
210 | } | ||
211 | } | ||
212 | |||
213 | fn is_repeat(&self) -> bool { | ||
214 | self.syntax().children_with_tokens().any(|it| it.kind() == SEMI) | ||
215 | } | ||
216 | } | ||
217 | |||
196 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | 218 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] |
197 | pub enum LiteralKind { | 219 | pub enum LiteralKind { |
198 | String, | 220 | String, |
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs index aec57c380..ca33b43e7 100644 --- a/crates/ra_syntax/src/ast/extensions.rs +++ b/crates/ra_syntax/src/ast/extensions.rs | |||
@@ -3,11 +3,8 @@ | |||
3 | 3 | ||
4 | use itertools::Itertools; | 4 | use itertools::Itertools; |
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{SmolStr, SyntaxToken, ast::{self, AstNode, children, child_opt}, SyntaxKind::*, SyntaxElement}; |
7 | SmolStr, SyntaxToken, | 7 | use ra_parser::SyntaxKind; |
8 | ast::{self, AstNode, children, child_opt}, | ||
9 | SyntaxKind::*, | ||
10 | }; | ||
11 | 8 | ||
12 | impl ast::Name { | 9 | impl ast::Name { |
13 | pub fn text(&self) -> &SmolStr { | 10 | pub fn text(&self) -> &SmolStr { |
@@ -217,6 +214,33 @@ impl ast::ExprStmt { | |||
217 | } | 214 | } |
218 | } | 215 | } |
219 | 216 | ||
217 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
218 | pub enum FieldKind<'a> { | ||
219 | Name(&'a ast::NameRef), | ||
220 | Index(SyntaxToken<'a>), | ||
221 | } | ||
222 | |||
223 | impl ast::FieldExpr { | ||
224 | pub fn index_token(&self) -> Option<SyntaxToken> { | ||
225 | self.syntax | ||
226 | .children_with_tokens() | ||
227 | // FIXME: Accepting floats here to reject them in validation later | ||
228 | .find(|c| c.kind() == SyntaxKind::INT_NUMBER || c.kind() == SyntaxKind::FLOAT_NUMBER) | ||
229 | .as_ref() | ||
230 | .and_then(SyntaxElement::as_token) | ||
231 | } | ||
232 | |||
233 | pub fn field_access(&self) -> Option<FieldKind> { | ||
234 | if let Some(nr) = self.name_ref() { | ||
235 | Some(FieldKind::Name(nr)) | ||
236 | } else if let Some(tok) = self.index_token() { | ||
237 | Some(FieldKind::Index(tok)) | ||
238 | } else { | ||
239 | None | ||
240 | } | ||
241 | } | ||
242 | } | ||
243 | |||
220 | impl ast::RefPat { | 244 | impl ast::RefPat { |
221 | pub fn is_mut(&self) -> bool { | 245 | pub fn is_mut(&self) -> bool { |
222 | self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW) | 246 | self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW) |
diff --git a/crates/ra_syntax/src/syntax_error.rs b/crates/ra_syntax/src/syntax_error.rs index 4b8c22a57..4198eefdb 100644 --- a/crates/ra_syntax/src/syntax_error.rs +++ b/crates/ra_syntax/src/syntax_error.rs | |||
@@ -95,6 +95,7 @@ pub enum SyntaxErrorKind { | |||
95 | InvalidSuffix, | 95 | InvalidSuffix, |
96 | InvalidBlockAttr, | 96 | InvalidBlockAttr, |
97 | InvalidMatchInnerAttr, | 97 | InvalidMatchInnerAttr, |
98 | InvalidTupleIndexFormat, | ||
98 | } | 99 | } |
99 | 100 | ||
100 | impl fmt::Display for SyntaxErrorKind { | 101 | impl fmt::Display for SyntaxErrorKind { |
@@ -139,6 +140,9 @@ impl fmt::Display for SyntaxErrorKind { | |||
139 | InvalidMatchInnerAttr => { | 140 | InvalidMatchInnerAttr => { |
140 | write!(f, "Inner attributes are only allowed directly after the opening brace of the match expression") | 141 | write!(f, "Inner attributes are only allowed directly after the opening brace of the match expression") |
141 | } | 142 | } |
143 | InvalidTupleIndexFormat => { | ||
144 | write!(f, "Tuple (struct) field access is only allowed through decimal integers with no underscores or suffix") | ||
145 | } | ||
142 | ParseError(msg) => write!(f, "{}", msg.0), | 146 | ParseError(msg) => write!(f, "{}", msg.0), |
143 | } | 147 | } |
144 | } | 148 | } |
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index fc534df83..c2f545173 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -3,6 +3,7 @@ mod byte_string; | |||
3 | mod char; | 3 | mod char; |
4 | mod string; | 4 | mod string; |
5 | mod block; | 5 | mod block; |
6 | mod field_expr; | ||
6 | 7 | ||
7 | use crate::{ | 8 | use crate::{ |
8 | SourceFile, SyntaxError, AstNode, SyntaxNode, | 9 | SourceFile, SyntaxError, AstNode, SyntaxNode, |
@@ -17,6 +18,7 @@ pub(crate) fn validate(file: &SourceFile) -> Vec<SyntaxError> { | |||
17 | let _ = visitor_ctx(&mut errors) | 18 | let _ = visitor_ctx(&mut errors) |
18 | .visit::<ast::Literal, _>(validate_literal) | 19 | .visit::<ast::Literal, _>(validate_literal) |
19 | .visit::<ast::Block, _>(block::validate_block_node) | 20 | .visit::<ast::Block, _>(block::validate_block_node) |
21 | .visit::<ast::FieldExpr, _>(field_expr::validate_field_expr_node) | ||
20 | .accept(node); | 22 | .accept(node); |
21 | } | 23 | } |
22 | errors | 24 | errors |
diff --git a/crates/ra_syntax/src/validation/field_expr.rs b/crates/ra_syntax/src/validation/field_expr.rs new file mode 100644 index 000000000..2b405062e --- /dev/null +++ b/crates/ra_syntax/src/validation/field_expr.rs | |||
@@ -0,0 +1,12 @@ | |||
1 | use crate::{ast::{self, FieldKind}, | ||
2 | SyntaxError, | ||
3 | SyntaxErrorKind::*, | ||
4 | }; | ||
5 | |||
6 | pub(crate) fn validate_field_expr_node(node: &ast::FieldExpr, errors: &mut Vec<SyntaxError>) { | ||
7 | if let Some(FieldKind::Index(idx)) = node.field_access() { | ||
8 | if idx.text().chars().any(|c| c < '0' || c > '9') { | ||
9 | errors.push(SyntaxError::new(InvalidTupleIndexFormat, idx.range())); | ||
10 | } | ||
11 | } | ||
12 | } | ||
diff --git a/crates/ra_syntax/tests/data/parser/inline/err/0010_bad_tuple_index_expr.rs b/crates/ra_syntax/tests/data/parser/inline/err/0010_bad_tuple_index_expr.rs new file mode 100644 index 000000000..30cc49138 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/err/0010_bad_tuple_index_expr.rs | |||
@@ -0,0 +1,5 @@ | |||
1 | fn foo() { | ||
2 | x.0.; | ||
3 | x.1i32; | ||
4 | x.0x01; | ||
5 | } | ||
diff --git a/crates/ra_syntax/tests/data/parser/inline/err/0010_bad_tuple_index_expr.txt b/crates/ra_syntax/tests/data/parser/inline/err/0010_bad_tuple_index_expr.txt new file mode 100644 index 000000000..c111f60ea --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/err/0010_bad_tuple_index_expr.txt | |||
@@ -0,0 +1,51 @@ | |||
1 | SOURCE_FILE@[0; 47) | ||
2 | FN_DEF@[0; 46) | ||
3 | FN_KW@[0; 2) "fn" | ||
4 | WHITESPACE@[2; 3) " " | ||
5 | NAME@[3; 6) | ||
6 | IDENT@[3; 6) "foo" | ||
7 | PARAM_LIST@[6; 8) | ||
8 | L_PAREN@[6; 7) "(" | ||
9 | R_PAREN@[7; 8) ")" | ||
10 | WHITESPACE@[8; 9) " " | ||
11 | BLOCK@[9; 46) | ||
12 | L_CURLY@[9; 10) "{" | ||
13 | WHITESPACE@[10; 15) "\n " | ||
14 | EXPR_STMT@[15; 20) | ||
15 | FIELD_EXPR@[15; 19) | ||
16 | PATH_EXPR@[15; 16) | ||
17 | PATH@[15; 16) | ||
18 | PATH_SEGMENT@[15; 16) | ||
19 | NAME_REF@[15; 16) | ||
20 | IDENT@[15; 16) "x" | ||
21 | DOT@[16; 17) "." | ||
22 | err: `Tuple (struct) field access is only allowed through decimal integers with no underscores or suffix` | ||
23 | FLOAT_NUMBER@[17; 19) "0." | ||
24 | SEMI@[19; 20) ";" | ||
25 | WHITESPACE@[20; 25) "\n " | ||
26 | EXPR_STMT@[25; 32) | ||
27 | FIELD_EXPR@[25; 31) | ||
28 | PATH_EXPR@[25; 26) | ||
29 | PATH@[25; 26) | ||
30 | PATH_SEGMENT@[25; 26) | ||
31 | NAME_REF@[25; 26) | ||
32 | IDENT@[25; 26) "x" | ||
33 | DOT@[26; 27) "." | ||
34 | err: `Tuple (struct) field access is only allowed through decimal integers with no underscores or suffix` | ||
35 | INT_NUMBER@[27; 31) "1i32" | ||
36 | SEMI@[31; 32) ";" | ||
37 | WHITESPACE@[32; 37) "\n " | ||
38 | EXPR_STMT@[37; 44) | ||
39 | FIELD_EXPR@[37; 43) | ||
40 | PATH_EXPR@[37; 38) | ||
41 | PATH@[37; 38) | ||
42 | PATH_SEGMENT@[37; 38) | ||
43 | NAME_REF@[37; 38) | ||
44 | IDENT@[37; 38) "x" | ||
45 | DOT@[38; 39) "." | ||
46 | err: `Tuple (struct) field access is only allowed through decimal integers with no underscores or suffix` | ||
47 | INT_NUMBER@[39; 43) "0x01" | ||
48 | SEMI@[43; 44) ";" | ||
49 | WHITESPACE@[44; 45) "\n" | ||
50 | R_CURLY@[45; 46) "}" | ||
51 | WHITESPACE@[46; 47) "\n" | ||
diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0011_field_expr.rs b/crates/ra_syntax/tests/data/parser/inline/ok/0011_field_expr.rs index 3e69538e5..b8da2ddc3 100644 --- a/crates/ra_syntax/tests/data/parser/inline/ok/0011_field_expr.rs +++ b/crates/ra_syntax/tests/data/parser/inline/ok/0011_field_expr.rs | |||
@@ -1,4 +1,5 @@ | |||
1 | fn foo() { | 1 | fn foo() { |
2 | x.foo; | 2 | x.foo; |
3 | x.0.bar; | 3 | x.0.bar; |
4 | x.0(); | ||
4 | } | 5 | } |
diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0011_field_expr.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0011_field_expr.txt index a86702843..78054ec5a 100644 --- a/crates/ra_syntax/tests/data/parser/inline/ok/0011_field_expr.txt +++ b/crates/ra_syntax/tests/data/parser/inline/ok/0011_field_expr.txt | |||
@@ -1,5 +1,5 @@ | |||
1 | SOURCE_FILE@[0; 37) | 1 | SOURCE_FILE@[0; 48) |
2 | FN_DEF@[0; 36) | 2 | FN_DEF@[0; 47) |
3 | FN_KW@[0; 2) "fn" | 3 | FN_KW@[0; 2) "fn" |
4 | WHITESPACE@[2; 3) " " | 4 | WHITESPACE@[2; 3) " " |
5 | NAME@[3; 6) | 5 | NAME@[3; 6) |
@@ -8,7 +8,7 @@ SOURCE_FILE@[0; 37) | |||
8 | L_PAREN@[6; 7) "(" | 8 | L_PAREN@[6; 7) "(" |
9 | R_PAREN@[7; 8) ")" | 9 | R_PAREN@[7; 8) ")" |
10 | WHITESPACE@[8; 9) " " | 10 | WHITESPACE@[8; 9) " " |
11 | BLOCK@[9; 36) | 11 | BLOCK@[9; 47) |
12 | L_CURLY@[9; 10) "{" | 12 | L_CURLY@[9; 10) "{" |
13 | WHITESPACE@[10; 15) "\n " | 13 | WHITESPACE@[10; 15) "\n " |
14 | EXPR_STMT@[15; 21) | 14 | EXPR_STMT@[15; 21) |
@@ -37,6 +37,21 @@ SOURCE_FILE@[0; 37) | |||
37 | NAME_REF@[30; 33) | 37 | NAME_REF@[30; 33) |
38 | IDENT@[30; 33) "bar" | 38 | IDENT@[30; 33) "bar" |
39 | SEMI@[33; 34) ";" | 39 | SEMI@[33; 34) ";" |
40 | WHITESPACE@[34; 35) "\n" | 40 | WHITESPACE@[34; 39) "\n " |
41 | R_CURLY@[35; 36) "}" | 41 | EXPR_STMT@[39; 45) |
42 | WHITESPACE@[36; 37) "\n" | 42 | CALL_EXPR@[39; 44) |
43 | FIELD_EXPR@[39; 42) | ||
44 | PATH_EXPR@[39; 40) | ||
45 | PATH@[39; 40) | ||
46 | PATH_SEGMENT@[39; 40) | ||
47 | NAME_REF@[39; 40) | ||
48 | IDENT@[39; 40) "x" | ||
49 | DOT@[40; 41) "." | ||
50 | INT_NUMBER@[41; 42) "0" | ||
51 | ARG_LIST@[42; 44) | ||
52 | L_PAREN@[42; 43) "(" | ||
53 | R_PAREN@[43; 44) ")" | ||
54 | SEMI@[44; 45) ";" | ||
55 | WHITESPACE@[45; 46) "\n" | ||
56 | R_CURLY@[46; 47) "}" | ||
57 | WHITESPACE@[47; 48) "\n" | ||