aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--crates/ra_hir/src/expr.rs5
-rw-r--r--crates/ra_hir/src/name.rs9
-rw-r--r--crates/ra_hir/src/ty/tests.rs59
-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_parser/src/grammar/expressions.rs13
-rw-r--r--crates/ra_syntax/src/ast.rs2
-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
16 files changed, 249 insertions, 14 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..cfa824458 100644
--- a/crates/ra_hir/src/expr.rs
+++ b/crates/ra_hir/src/expr.rs
@@ -671,7 +671,10 @@ impl ExprCollector {
671 } 671 }
672 ast::ExprKind::FieldExpr(e) => { 672 ast::ExprKind::FieldExpr(e) => {
673 let expr = self.collect_expr_opt(e.expr()); 673 let expr = self.collect_expr_opt(e.expr());
674 let name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); 674 let name = match e.field_access() {
675 Some(kind) => kind.as_name(),
676 _ => Name::missing(),
677 };
675 self.alloc_expr(Expr::Field { expr, name }, syntax_ptr) 678 self.alloc_expr(Expr::Field { expr, name }, syntax_ptr)
676 } 679 }
677 ast::ExprKind::TryExpr(e) => { 680 ast::ExprKind::TryExpr(e) => {
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/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 0b7c841df..f0164f7ea 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -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_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..a06a6375d 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -17,7 +17,7 @@ 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},
22}; 22};
23 23
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"