aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--crates/ra_hir/src/expr.rs49
-rw-r--r--crates/ra_hir/src/name.rs9
-rw-r--r--crates/ra_hir/src/ty.rs6
-rw-r--r--crates/ra_hir/src/ty/infer.rs25
-rw-r--r--crates/ra_hir/src/ty/tests.rs141
-rw-r--r--crates/ra_ide_api/src/completion/complete_dot.rs22
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__tuple_field_inference.snap16
-rw-r--r--crates/ra_lsp_server/src/server_world.rs16
-rw-r--r--crates/ra_parser/src/grammar/expressions.rs13
-rw-r--r--crates/ra_syntax/src/ast.rs4
-rw-r--r--crates/ra_syntax/src/ast/expr_extensions.rs22
-rw-r--r--crates/ra_syntax/src/ast/extensions.rs34
-rw-r--r--crates/ra_syntax/src/syntax_error.rs4
-rw-r--r--crates/ra_syntax/src/validation.rs2
-rw-r--r--crates/ra_syntax/src/validation/field_expr.rs12
-rw-r--r--crates/ra_syntax/tests/data/parser/inline/err/0010_bad_tuple_index_expr.rs5
-rw-r--r--crates/ra_syntax/tests/data/parser/inline/err/0010_bad_tuple_index_expr.txt51
-rw-r--r--crates/ra_syntax/tests/data/parser/inline/ok/0011_field_expr.rs1
-rw-r--r--crates/ra_syntax/tests/data/parser/inline/ok/0011_field_expr.txt27
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/
2crates/*/target 2crates/*/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;
6use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; 6use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
7use ra_syntax::{ 7use 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
12use crate::{ 12use 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
247pub use ra_syntax::ast::PrefixOp as UnaryOp; 245pub use ra_syntax::ast::PrefixOp as UnaryOp;
248pub use ra_syntax::ast::BinOp as BinaryOp; 246pub use ra_syntax::ast::BinOp as BinaryOp;
247#[derive(Debug, Clone, Eq, PartialEq)]
248pub 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)]
251pub struct MatchArm { 254pub 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
93impl<'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
93impl AsName for ra_db::Dependency { 102impl 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]
2246fn tuple_struct_fields() {
2247 assert_snapshot_matches!(
2248 infer(r#"
2249struct S(i32, u64);
2250fn 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]
2272fn tuple_struct_with_fn() {
2273 assert_snapshot_matches!(
2274 infer(r#"
2275struct S(fn(u32) -> u64);
2276fn 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
2245fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { 2304fn 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---
2created: "2019-04-05T23:00:18.283812700Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/completion/completion_item.rs
5expression: 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::{
11use ra_vfs::{Vfs, VfsChange, VfsFile, VfsRoot}; 11use ra_vfs::{Vfs, VfsChange, VfsFile, VfsRoot};
12use relative_path::RelativePathBuf; 12use relative_path::RelativePathBuf;
13use parking_lot::RwLock; 13use parking_lot::RwLock;
14use failure::format_err; 14use failure::{Error, format_err};
15use gen_lsp_server::ErrorCode;
15 16
16use crate::{ 17use 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// }
383fn field_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker { 391fn 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
196pub enum ArrayExprKind<'a> {
197 Repeat { initializer: Option<&'a ast::Expr>, repeat: Option<&'a ast::Expr> },
198 ElementList(AstChildren<'a, ast::Expr>),
199}
200
201impl 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)]
197pub enum LiteralKind { 219pub 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
4use itertools::Itertools; 4use itertools::Itertools;
5 5
6use crate::{ 6use crate::{SmolStr, SyntaxToken, ast::{self, AstNode, children, child_opt}, SyntaxKind::*, SyntaxElement};
7 SmolStr, SyntaxToken, 7use ra_parser::SyntaxKind;
8 ast::{self, AstNode, children, child_opt},
9 SyntaxKind::*,
10};
11 8
12impl ast::Name { 9impl 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)]
218pub enum FieldKind<'a> {
219 Name(&'a ast::NameRef),
220 Index(SyntaxToken<'a>),
221}
222
223impl 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
220impl ast::RefPat { 244impl 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
100impl fmt::Display for SyntaxErrorKind { 101impl 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;
3mod char; 3mod char;
4mod string; 4mod string;
5mod block; 5mod block;
6mod field_expr;
6 7
7use crate::{ 8use 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 @@
1use crate::{ast::{self, FieldKind},
2 SyntaxError,
3 SyntaxErrorKind::*,
4};
5
6pub(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 @@
1fn 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 @@
1SOURCE_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 @@
1fn foo() { 1fn 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 @@
1SOURCE_FILE@[0; 37) 1SOURCE_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"