diff options
18 files changed, 449 insertions, 58 deletions
diff --git a/.travis.yml b/.travis.yml index c198cc5f7..d4cf71ab5 100644 --- a/.travis.yml +++ b/.travis.yml | |||
@@ -58,7 +58,7 @@ deploy: | |||
58 | provider: pages | 58 | provider: pages |
59 | skip-cleanup: true | 59 | skip-cleanup: true |
60 | github-token: $DOCS_TOKEN # Set in the settings page of your repository, as a secure variable | 60 | github-token: $DOCS_TOKEN # Set in the settings page of your repository, as a secure variable |
61 | keep-history: true | 61 | keep-history: false |
62 | local-dir: target/website/ | 62 | local-dir: target/website/ |
63 | on: | 63 | on: |
64 | branch: master | 64 | branch: master |
diff --git a/crates/ra_assists/src/assists/apply_demorgan.rs b/crates/ra_assists/src/assists/apply_demorgan.rs new file mode 100644 index 000000000..5f2b0dd18 --- /dev/null +++ b/crates/ra_assists/src/assists/apply_demorgan.rs | |||
@@ -0,0 +1,102 @@ | |||
1 | //! This contains the functions associated with the demorgan assist. | ||
2 | //! This assist transforms boolean expressions of the form `!a || !b` into | ||
3 | //! `!(a && b)`. | ||
4 | use hir::db::HirDatabase; | ||
5 | use ra_syntax::ast::{self, AstNode}; | ||
6 | use ra_syntax::SyntaxNode; | ||
7 | |||
8 | use crate::{Assist, AssistCtx, AssistId}; | ||
9 | |||
10 | /// Assist for applying demorgan's law | ||
11 | /// | ||
12 | /// This transforms expressions of the form `!l || !r` into `!(l && r)`. | ||
13 | /// This also works with `&&`. This assist can only be applied with the cursor | ||
14 | /// on either `||` or `&&`, with both operands being a negation of some kind. | ||
15 | /// This means something of the form `!x` or `x != y`. | ||
16 | pub(crate) fn apply_demorgan(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | ||
17 | let expr = ctx.node_at_offset::<ast::BinExpr>()?; | ||
18 | let op = expr.op_kind()?; | ||
19 | let op_range = expr.op_token()?.text_range(); | ||
20 | let opposite_op = opposite_logic_op(op)?; | ||
21 | let cursor_in_range = ctx.frange.range.is_subrange(&op_range); | ||
22 | if !cursor_in_range { | ||
23 | return None; | ||
24 | } | ||
25 | let lhs = expr.lhs()?.syntax().clone(); | ||
26 | let lhs_range = lhs.text_range(); | ||
27 | let rhs = expr.rhs()?.syntax().clone(); | ||
28 | let rhs_range = rhs.text_range(); | ||
29 | let not_lhs = undo_negation(lhs)?; | ||
30 | let not_rhs = undo_negation(rhs)?; | ||
31 | |||
32 | ctx.add_action(AssistId("apply_demorgan"), "apply demorgan's law", |edit| { | ||
33 | edit.target(op_range); | ||
34 | edit.replace(op_range, opposite_op); | ||
35 | edit.replace(lhs_range, format!("!({}", not_lhs)); | ||
36 | edit.replace(rhs_range, format!("{})", not_rhs)); | ||
37 | }); | ||
38 | ctx.build() | ||
39 | } | ||
40 | |||
41 | // Return the opposite text for a given logical operator, if it makes sense | ||
42 | fn opposite_logic_op(kind: ast::BinOp) -> Option<&'static str> { | ||
43 | match kind { | ||
44 | ast::BinOp::BooleanOr => Some("&&"), | ||
45 | ast::BinOp::BooleanAnd => Some("||"), | ||
46 | _ => None, | ||
47 | } | ||
48 | } | ||
49 | |||
50 | // This function tries to undo unary negation, or inequality | ||
51 | fn undo_negation(node: SyntaxNode) -> Option<String> { | ||
52 | match ast::Expr::cast(node)? { | ||
53 | ast::Expr::BinExpr(bin) => match bin.op_kind()? { | ||
54 | ast::BinOp::NegatedEqualityTest => { | ||
55 | let lhs = bin.lhs()?.syntax().text(); | ||
56 | let rhs = bin.rhs()?.syntax().text(); | ||
57 | Some(format!("{} == {}", lhs, rhs)) | ||
58 | } | ||
59 | _ => None, | ||
60 | }, | ||
61 | ast::Expr::PrefixExpr(pe) => match pe.op_kind()? { | ||
62 | ast::PrefixOp::Not => { | ||
63 | let child = pe.expr()?.syntax().text(); | ||
64 | Some(String::from(child)) | ||
65 | } | ||
66 | _ => None, | ||
67 | }, | ||
68 | _ => None, | ||
69 | } | ||
70 | } | ||
71 | |||
72 | #[cfg(test)] | ||
73 | mod tests { | ||
74 | use super::*; | ||
75 | |||
76 | use crate::helpers::{check_assist, check_assist_not_applicable}; | ||
77 | |||
78 | #[test] | ||
79 | fn demorgan_turns_and_into_or() { | ||
80 | check_assist(apply_demorgan, "fn f() { !x &&<|> !x }", "fn f() { !(x ||<|> x) }") | ||
81 | } | ||
82 | |||
83 | #[test] | ||
84 | fn demorgan_turns_or_into_and() { | ||
85 | check_assist(apply_demorgan, "fn f() { !x ||<|> !x }", "fn f() { !(x &&<|> x) }") | ||
86 | } | ||
87 | |||
88 | #[test] | ||
89 | fn demorgan_removes_inequality() { | ||
90 | check_assist(apply_demorgan, "fn f() { x != x ||<|> !x }", "fn f() { !(x == x &&<|> x) }") | ||
91 | } | ||
92 | |||
93 | #[test] | ||
94 | fn demorgan_doesnt_apply_with_cursor_not_on_op() { | ||
95 | check_assist_not_applicable(apply_demorgan, "fn f() { <|> !x || !x }") | ||
96 | } | ||
97 | |||
98 | #[test] | ||
99 | fn demorgan_doesnt_apply_when_operands_arent_negated_already() { | ||
100 | check_assist_not_applicable(apply_demorgan, "fn f() { x ||<|> x }") | ||
101 | } | ||
102 | } | ||
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 91b2a1dce..d2376c475 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -92,6 +92,7 @@ mod assists { | |||
92 | mod add_derive; | 92 | mod add_derive; |
93 | mod add_explicit_type; | 93 | mod add_explicit_type; |
94 | mod add_impl; | 94 | mod add_impl; |
95 | mod apply_demorgan; | ||
95 | mod flip_comma; | 96 | mod flip_comma; |
96 | mod flip_binexpr; | 97 | mod flip_binexpr; |
97 | mod change_visibility; | 98 | mod change_visibility; |
@@ -113,6 +114,7 @@ mod assists { | |||
113 | add_derive::add_derive, | 114 | add_derive::add_derive, |
114 | add_explicit_type::add_explicit_type, | 115 | add_explicit_type::add_explicit_type, |
115 | add_impl::add_impl, | 116 | add_impl::add_impl, |
117 | apply_demorgan::apply_demorgan, | ||
116 | change_visibility::change_visibility, | 118 | change_visibility::change_visibility, |
117 | fill_match_arms::fill_match_arms, | 119 | fill_match_arms::fill_match_arms, |
118 | merge_match_arms::merge_match_arms, | 120 | merge_match_arms::merge_match_arms, |
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 2e4a489a0..ca9aefc42 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs | |||
@@ -1602,6 +1602,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1602 | tail: Option<ExprId>, | 1602 | tail: Option<ExprId>, |
1603 | expected: &Expectation, | 1603 | expected: &Expectation, |
1604 | ) -> Ty { | 1604 | ) -> Ty { |
1605 | let mut diverges = false; | ||
1605 | for stmt in statements { | 1606 | for stmt in statements { |
1606 | match stmt { | 1607 | match stmt { |
1607 | Statement::Let { pat, type_ref, initializer } => { | 1608 | Statement::Let { pat, type_ref, initializer } => { |
@@ -1623,16 +1624,23 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1623 | self.infer_pat(*pat, &ty, BindingMode::default()); | 1624 | self.infer_pat(*pat, &ty, BindingMode::default()); |
1624 | } | 1625 | } |
1625 | Statement::Expr(expr) => { | 1626 | Statement::Expr(expr) => { |
1626 | self.infer_expr(*expr, &Expectation::none()); | 1627 | if let ty_app!(TypeCtor::Never) = self.infer_expr(*expr, &Expectation::none()) { |
1628 | diverges = true; | ||
1629 | } | ||
1627 | } | 1630 | } |
1628 | } | 1631 | } |
1629 | } | 1632 | } |
1630 | 1633 | ||
1631 | if let Some(expr) = tail { | 1634 | let ty = if let Some(expr) = tail { |
1632 | self.infer_expr_coerce(expr, expected) | 1635 | self.infer_expr_coerce(expr, expected) |
1633 | } else { | 1636 | } else { |
1634 | self.coerce(&Ty::unit(), &expected.ty); | 1637 | self.coerce(&Ty::unit(), &expected.ty); |
1635 | Ty::unit() | 1638 | Ty::unit() |
1639 | }; | ||
1640 | if diverges { | ||
1641 | Ty::simple(TypeCtor::Never) | ||
1642 | } else { | ||
1643 | ty | ||
1636 | } | 1644 | } |
1637 | } | 1645 | } |
1638 | 1646 | ||
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 171aead18..25dad81eb 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -263,7 +263,7 @@ fn test(a: u32, b: isize, c: !, d: &str) { | |||
263 | [17; 18) 'b': isize | 263 | [17; 18) 'b': isize |
264 | [27; 28) 'c': ! | 264 | [27; 28) 'c': ! |
265 | [33; 34) 'd': &str | 265 | [33; 34) 'd': &str |
266 | [42; 121) '{ ...f32; }': () | 266 | [42; 121) '{ ...f32; }': ! |
267 | [48; 49) 'a': u32 | 267 | [48; 49) 'a': u32 |
268 | [55; 56) 'b': isize | 268 | [55; 56) 'b': isize |
269 | [62; 63) 'c': ! | 269 | [62; 63) 'c': ! |
@@ -1026,6 +1026,67 @@ fn main(foo: Foo) { | |||
1026 | } | 1026 | } |
1027 | 1027 | ||
1028 | #[test] | 1028 | #[test] |
1029 | fn infer_if_match_with_return() { | ||
1030 | assert_snapshot!( | ||
1031 | infer(r#" | ||
1032 | fn foo() { | ||
1033 | let _x1 = if true { | ||
1034 | 1 | ||
1035 | } else { | ||
1036 | return; | ||
1037 | }; | ||
1038 | let _x2 = if true { | ||
1039 | 2 | ||
1040 | } else { | ||
1041 | return | ||
1042 | }; | ||
1043 | let _x3 = match true { | ||
1044 | true => 3, | ||
1045 | _ => { | ||
1046 | return; | ||
1047 | } | ||
1048 | }; | ||
1049 | let _x4 = match true { | ||
1050 | true => 4, | ||
1051 | _ => return | ||
1052 | }; | ||
1053 | }"#), | ||
1054 | @r###" | ||
1055 | [10; 323) '{ ... }; }': () | ||
1056 | [20; 23) '_x1': i32 | ||
1057 | [26; 80) 'if tru... }': i32 | ||
1058 | [29; 33) 'true': bool | ||
1059 | [34; 51) '{ ... }': i32 | ||
1060 | [44; 45) '1': i32 | ||
1061 | [57; 80) '{ ... }': ! | ||
1062 | [67; 73) 'return': ! | ||
1063 | [90; 93) '_x2': i32 | ||
1064 | [96; 149) 'if tru... }': i32 | ||
1065 | [99; 103) 'true': bool | ||
1066 | [104; 121) '{ ... }': i32 | ||
1067 | [114; 115) '2': i32 | ||
1068 | [127; 149) '{ ... }': ! | ||
1069 | [137; 143) 'return': ! | ||
1070 | [159; 162) '_x3': i32 | ||
1071 | [165; 247) 'match ... }': i32 | ||
1072 | [171; 175) 'true': bool | ||
1073 | [186; 190) 'true': bool | ||
1074 | [194; 195) '3': i32 | ||
1075 | [205; 206) '_': bool | ||
1076 | [210; 241) '{ ... }': ! | ||
1077 | [224; 230) 'return': ! | ||
1078 | [257; 260) '_x4': i32 | ||
1079 | [263; 320) 'match ... }': i32 | ||
1080 | [269; 273) 'true': bool | ||
1081 | [284; 288) 'true': bool | ||
1082 | [292; 293) '4': i32 | ||
1083 | [303; 304) '_': bool | ||
1084 | [308; 314) 'return': ! | ||
1085 | "### | ||
1086 | ) | ||
1087 | } | ||
1088 | |||
1089 | #[test] | ||
1029 | fn infer_inherent_method() { | 1090 | fn infer_inherent_method() { |
1030 | assert_snapshot!( | 1091 | assert_snapshot!( |
1031 | infer(r#" | 1092 | infer(r#" |
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs index 413ecb278..74b23e2f7 100644 --- a/crates/ra_parser/src/grammar/expressions.rs +++ b/crates/ra_parser/src/grammar/expressions.rs | |||
@@ -335,7 +335,13 @@ fn lhs(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)> | |||
335 | // } | 335 | // } |
336 | // | 336 | // |
337 | let (lhs, blocklike) = atom::atom_expr(p, r)?; | 337 | let (lhs, blocklike) = atom::atom_expr(p, r)?; |
338 | return Some(postfix_expr(p, lhs, blocklike, !(r.prefer_stmt && blocklike.is_block()))); | 338 | return Some(postfix_expr( |
339 | p, | ||
340 | lhs, | ||
341 | blocklike, | ||
342 | !(r.prefer_stmt && blocklike.is_block()), | ||
343 | r.forbid_structs, | ||
344 | )); | ||
339 | } | 345 | } |
340 | }; | 346 | }; |
341 | expr_bp(p, r, 255); | 347 | expr_bp(p, r, 255); |
@@ -350,6 +356,7 @@ fn postfix_expr( | |||
350 | // `while true {break}; ();` | 356 | // `while true {break}; ();` |
351 | mut block_like: BlockLike, | 357 | mut block_like: BlockLike, |
352 | mut allow_calls: bool, | 358 | mut allow_calls: bool, |
359 | forbid_structs: bool, | ||
353 | ) -> (CompletedMarker, BlockLike) { | 360 | ) -> (CompletedMarker, BlockLike) { |
354 | loop { | 361 | loop { |
355 | lhs = match p.current() { | 362 | lhs = match p.current() { |
@@ -363,7 +370,7 @@ fn postfix_expr( | |||
363 | // } | 370 | // } |
364 | T!['('] if allow_calls => call_expr(p, lhs), | 371 | T!['('] if allow_calls => call_expr(p, lhs), |
365 | T!['['] if allow_calls => index_expr(p, lhs), | 372 | T!['['] if allow_calls => index_expr(p, lhs), |
366 | T![.] => match postfix_dot_expr(p, lhs) { | 373 | T![.] => match postfix_dot_expr(p, lhs, forbid_structs) { |
367 | Ok(it) => it, | 374 | Ok(it) => it, |
368 | Err(it) => { | 375 | Err(it) => { |
369 | lhs = it; | 376 | lhs = it; |
@@ -382,6 +389,7 @@ fn postfix_expr( | |||
382 | fn postfix_dot_expr( | 389 | fn postfix_dot_expr( |
383 | p: &mut Parser, | 390 | p: &mut Parser, |
384 | lhs: CompletedMarker, | 391 | lhs: CompletedMarker, |
392 | forbid_structs: bool, | ||
385 | ) -> Result<CompletedMarker, CompletedMarker> { | 393 | ) -> Result<CompletedMarker, CompletedMarker> { |
386 | assert!(p.at(T![.])); | 394 | assert!(p.at(T![.])); |
387 | if p.nth(1) == IDENT && (p.nth(2) == T!['('] || p.nth_at(2, T![::])) { | 395 | if p.nth(1) == IDENT && (p.nth(2) == T!['('] || p.nth_at(2, T![::])) { |
@@ -402,10 +410,17 @@ fn postfix_expr( | |||
402 | } | 410 | } |
403 | 411 | ||
404 | // test postfix_range | 412 | // test postfix_range |
405 | // fn foo() { let x = 1..; } | 413 | // fn foo() { |
406 | for &(op, la) in [(T![..=], 3), (T![..], 2)].iter() { | 414 | // let x = 1..; |
415 | // match 1.. { _ => () }; | ||
416 | // match a.b()..S { _ => () }; | ||
417 | // } | ||
418 | for &(op, la) in &[(T![..=], 3), (T![..], 2)] { | ||
407 | if p.at(op) { | 419 | if p.at(op) { |
408 | return if EXPR_FIRST.contains(p.nth(la)) { | 420 | let next_token = p.nth(la); |
421 | let has_trailing_expression = | ||
422 | !(forbid_structs && next_token == T!['{']) && EXPR_FIRST.contains(next_token); | ||
423 | return if has_trailing_expression { | ||
409 | Err(lhs) | 424 | Err(lhs) |
410 | } else { | 425 | } else { |
411 | let m = lhs.precede(p); | 426 | let m = lhs.precede(p); |
diff --git a/crates/ra_parser/src/grammar/expressions/atom.rs b/crates/ra_parser/src/grammar/expressions/atom.rs index a52bdb3ea..7454005c4 100644 --- a/crates/ra_parser/src/grammar/expressions/atom.rs +++ b/crates/ra_parser/src/grammar/expressions/atom.rs | |||
@@ -121,11 +121,7 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar | |||
121 | // break; | 121 | // break; |
122 | // } | 122 | // } |
123 | // } | 123 | // } |
124 | if r.forbid_structs { | 124 | block_expr(p, None) |
125 | return None; | ||
126 | } else { | ||
127 | block_expr(p, None) | ||
128 | } | ||
129 | } | 125 | } |
130 | T![return] => return_expr(p), | 126 | T![return] => return_expr(p), |
131 | T![continue] => continue_expr(p), | 127 | T![continue] => continue_expr(p), |
@@ -261,6 +257,7 @@ fn lambda_expr(p: &mut Parser) -> CompletedMarker { | |||
261 | // if true {} else {}; | 257 | // if true {} else {}; |
262 | // if true {} else if false {} else {}; | 258 | // if true {} else if false {} else {}; |
263 | // if S {}; | 259 | // if S {}; |
260 | // if { true } { } else { }; | ||
264 | // } | 261 | // } |
265 | fn if_expr(p: &mut Parser) -> CompletedMarker { | 262 | fn if_expr(p: &mut Parser) -> CompletedMarker { |
266 | assert!(p.at(T![if])); | 263 | assert!(p.at(T![if])); |
@@ -309,6 +306,7 @@ fn loop_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { | |||
309 | // fn foo() { | 306 | // fn foo() { |
310 | // while true {}; | 307 | // while true {}; |
311 | // while let Some(x) = it.next() {}; | 308 | // while let Some(x) = it.next() {}; |
309 | // while { true } {}; | ||
312 | // } | 310 | // } |
313 | fn while_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { | 311 | fn while_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { |
314 | assert!(p.at(T![while])); | 312 | assert!(p.at(T![while])); |
@@ -356,6 +354,8 @@ fn cond(p: &mut Parser) { | |||
356 | // fn foo() { | 354 | // fn foo() { |
357 | // match () { }; | 355 | // match () { }; |
358 | // match S {}; | 356 | // match S {}; |
357 | // match { } { _ => () }; | ||
358 | // match { S {} } {}; | ||
359 | // } | 359 | // } |
360 | fn match_expr(p: &mut Parser) -> CompletedMarker { | 360 | fn match_expr(p: &mut Parser) -> CompletedMarker { |
361 | assert!(p.at(T![match])); | 361 | assert!(p.at(T![match])); |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 5f7d8c3bf..25e6f64ce 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -1,5 +1,5 @@ | |||
1 | // Stores definitions which must be used in multiple places | 1 | // Stores definitions which must be used in multiple places |
2 | // See `cargo gen-syntax` (defined in crates/tools/src/main.rs) | 2 | // See `cargo gen-syntax` (defined in crates/ra_tools/src/main.rs) |
3 | Grammar( | 3 | Grammar( |
4 | punct: [ | 4 | punct: [ |
5 | (";", "SEMI"), | 5 | (";", "SEMI"), |
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.rs b/crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.rs index 293046a04..2f8188160 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.rs +++ b/crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.rs | |||
@@ -1,4 +1,5 @@ | |||
1 | fn foo() { | 1 | fn foo() { |
2 | while true {}; | 2 | while true {}; |
3 | while let Some(x) = it.next() {}; | 3 | while let Some(x) = it.next() {}; |
4 | while { true } {}; | ||
4 | } | 5 | } |
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.txt b/crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.txt index 9b43d624c..a6e14a114 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.txt +++ b/crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.txt | |||
@@ -1,5 +1,5 @@ | |||
1 | SOURCE_FILE@[0; 70) | 1 | SOURCE_FILE@[0; 93) |
2 | FN_DEF@[0; 69) | 2 | FN_DEF@[0; 92) |
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,8 +8,8 @@ SOURCE_FILE@[0; 70) | |||
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_EXPR@[9; 69) | 11 | BLOCK_EXPR@[9; 92) |
12 | BLOCK@[9; 69) | 12 | BLOCK@[9; 92) |
13 | L_CURLY@[9; 10) "{" | 13 | L_CURLY@[9; 10) "{" |
14 | WHITESPACE@[10; 15) "\n " | 14 | WHITESPACE@[10; 15) "\n " |
15 | EXPR_STMT@[15; 29) | 15 | EXPR_STMT@[15; 29) |
@@ -64,6 +64,26 @@ SOURCE_FILE@[0; 70) | |||
64 | L_CURLY@[64; 65) "{" | 64 | L_CURLY@[64; 65) "{" |
65 | R_CURLY@[65; 66) "}" | 65 | R_CURLY@[65; 66) "}" |
66 | SEMI@[66; 67) ";" | 66 | SEMI@[66; 67) ";" |
67 | WHITESPACE@[67; 68) "\n" | 67 | WHITESPACE@[67; 72) "\n " |
68 | R_CURLY@[68; 69) "}" | 68 | EXPR_STMT@[72; 90) |
69 | WHITESPACE@[69; 70) "\n" | 69 | WHILE_EXPR@[72; 89) |
70 | WHILE_KW@[72; 77) "while" | ||
71 | WHITESPACE@[77; 78) " " | ||
72 | CONDITION@[78; 86) | ||
73 | BLOCK_EXPR@[78; 86) | ||
74 | BLOCK@[78; 86) | ||
75 | L_CURLY@[78; 79) "{" | ||
76 | WHITESPACE@[79; 80) " " | ||
77 | LITERAL@[80; 84) | ||
78 | TRUE_KW@[80; 84) "true" | ||
79 | WHITESPACE@[84; 85) " " | ||
80 | R_CURLY@[85; 86) "}" | ||
81 | WHITESPACE@[86; 87) " " | ||
82 | BLOCK_EXPR@[87; 89) | ||
83 | BLOCK@[87; 89) | ||
84 | L_CURLY@[87; 88) "{" | ||
85 | R_CURLY@[88; 89) "}" | ||
86 | SEMI@[89; 90) ";" | ||
87 | WHITESPACE@[90; 91) "\n" | ||
88 | R_CURLY@[91; 92) "}" | ||
89 | WHITESPACE@[92; 93) "\n" | ||
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.rs b/crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.rs index 4b0d9af89..40f227ba3 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.rs +++ b/crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.rs | |||
@@ -3,4 +3,5 @@ fn foo() { | |||
3 | if true {} else {}; | 3 | if true {} else {}; |
4 | if true {} else if false {} else {}; | 4 | if true {} else if false {} else {}; |
5 | if S {}; | 5 | if S {}; |
6 | if { true } { } else { }; | ||
6 | } | 7 | } |
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.txt b/crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.txt index 14ea91fd2..2ace3c8ee 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.txt +++ b/crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.txt | |||
@@ -1,5 +1,5 @@ | |||
1 | SOURCE_FILE@[0; 107) | 1 | SOURCE_FILE@[0; 137) |
2 | FN_DEF@[0; 106) | 2 | FN_DEF@[0; 136) |
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,8 +8,8 @@ SOURCE_FILE@[0; 107) | |||
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_EXPR@[9; 106) | 11 | BLOCK_EXPR@[9; 136) |
12 | BLOCK@[9; 106) | 12 | BLOCK@[9; 136) |
13 | L_CURLY@[9; 10) "{" | 13 | L_CURLY@[9; 10) "{" |
14 | WHITESPACE@[10; 15) "\n " | 14 | WHITESPACE@[10; 15) "\n " |
15 | EXPR_STMT@[15; 26) | 15 | EXPR_STMT@[15; 26) |
@@ -98,6 +98,35 @@ SOURCE_FILE@[0; 107) | |||
98 | L_CURLY@[101; 102) "{" | 98 | L_CURLY@[101; 102) "{" |
99 | R_CURLY@[102; 103) "}" | 99 | R_CURLY@[102; 103) "}" |
100 | SEMI@[103; 104) ";" | 100 | SEMI@[103; 104) ";" |
101 | WHITESPACE@[104; 105) "\n" | 101 | WHITESPACE@[104; 109) "\n " |
102 | R_CURLY@[105; 106) "}" | 102 | EXPR_STMT@[109; 134) |
103 | WHITESPACE@[106; 107) "\n" | 103 | IF_EXPR@[109; 133) |
104 | IF_KW@[109; 111) "if" | ||
105 | WHITESPACE@[111; 112) " " | ||
106 | CONDITION@[112; 120) | ||
107 | BLOCK_EXPR@[112; 120) | ||
108 | BLOCK@[112; 120) | ||
109 | L_CURLY@[112; 113) "{" | ||
110 | WHITESPACE@[113; 114) " " | ||
111 | LITERAL@[114; 118) | ||
112 | TRUE_KW@[114; 118) "true" | ||
113 | WHITESPACE@[118; 119) " " | ||
114 | R_CURLY@[119; 120) "}" | ||
115 | WHITESPACE@[120; 121) " " | ||
116 | BLOCK_EXPR@[121; 124) | ||
117 | BLOCK@[121; 124) | ||
118 | L_CURLY@[121; 122) "{" | ||
119 | WHITESPACE@[122; 123) " " | ||
120 | R_CURLY@[123; 124) "}" | ||
121 | WHITESPACE@[124; 125) " " | ||
122 | ELSE_KW@[125; 129) "else" | ||
123 | WHITESPACE@[129; 130) " " | ||
124 | BLOCK_EXPR@[130; 133) | ||
125 | BLOCK@[130; 133) | ||
126 | L_CURLY@[130; 131) "{" | ||
127 | WHITESPACE@[131; 132) " " | ||
128 | R_CURLY@[132; 133) "}" | ||
129 | SEMI@[133; 134) ";" | ||
130 | WHITESPACE@[134; 135) "\n" | ||
131 | R_CURLY@[135; 136) "}" | ||
132 | WHITESPACE@[136; 137) "\n" | ||
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.rs b/crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.rs index c9205dfa3..c4021dc10 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.rs +++ b/crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.rs | |||
@@ -1,4 +1,6 @@ | |||
1 | fn foo() { | 1 | fn foo() { |
2 | match () { }; | 2 | match () { }; |
3 | match S {}; | 3 | match S {}; |
4 | match { } { _ => () }; | ||
5 | match { S {} } {}; | ||
4 | } | 6 | } |
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.txt b/crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.txt index f62b6c6d5..0af668056 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.txt +++ b/crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.txt | |||
@@ -1,5 +1,5 @@ | |||
1 | SOURCE_FILE@[0; 47) | 1 | SOURCE_FILE@[0; 97) |
2 | FN_DEF@[0; 46) | 2 | FN_DEF@[0; 96) |
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,8 +8,8 @@ SOURCE_FILE@[0; 47) | |||
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_EXPR@[9; 46) | 11 | BLOCK_EXPR@[9; 96) |
12 | BLOCK@[9; 46) | 12 | BLOCK@[9; 96) |
13 | L_CURLY@[9; 10) "{" | 13 | L_CURLY@[9; 10) "{" |
14 | WHITESPACE@[10; 15) "\n " | 14 | WHITESPACE@[10; 15) "\n " |
15 | EXPR_STMT@[15; 28) | 15 | EXPR_STMT@[15; 28) |
@@ -40,6 +40,57 @@ SOURCE_FILE@[0; 47) | |||
40 | L_CURLY@[41; 42) "{" | 40 | L_CURLY@[41; 42) "{" |
41 | R_CURLY@[42; 43) "}" | 41 | R_CURLY@[42; 43) "}" |
42 | SEMI@[43; 44) ";" | 42 | SEMI@[43; 44) ";" |
43 | WHITESPACE@[44; 45) "\n" | 43 | WHITESPACE@[44; 49) "\n " |
44 | R_CURLY@[45; 46) "}" | 44 | EXPR_STMT@[49; 71) |
45 | WHITESPACE@[46; 47) "\n" | 45 | MATCH_EXPR@[49; 70) |
46 | MATCH_KW@[49; 54) "match" | ||
47 | WHITESPACE@[54; 55) " " | ||
48 | BLOCK_EXPR@[55; 58) | ||
49 | BLOCK@[55; 58) | ||
50 | L_CURLY@[55; 56) "{" | ||
51 | WHITESPACE@[56; 57) " " | ||
52 | R_CURLY@[57; 58) "}" | ||
53 | WHITESPACE@[58; 59) " " | ||
54 | MATCH_ARM_LIST@[59; 70) | ||
55 | L_CURLY@[59; 60) "{" | ||
56 | WHITESPACE@[60; 61) " " | ||
57 | MATCH_ARM@[61; 68) | ||
58 | PLACEHOLDER_PAT@[61; 62) | ||
59 | UNDERSCORE@[61; 62) "_" | ||
60 | WHITESPACE@[62; 63) " " | ||
61 | FAT_ARROW@[63; 65) "=>" | ||
62 | WHITESPACE@[65; 66) " " | ||
63 | TUPLE_EXPR@[66; 68) | ||
64 | L_PAREN@[66; 67) "(" | ||
65 | R_PAREN@[67; 68) ")" | ||
66 | WHITESPACE@[68; 69) " " | ||
67 | R_CURLY@[69; 70) "}" | ||
68 | SEMI@[70; 71) ";" | ||
69 | WHITESPACE@[71; 76) "\n " | ||
70 | EXPR_STMT@[76; 94) | ||
71 | MATCH_EXPR@[76; 93) | ||
72 | MATCH_KW@[76; 81) "match" | ||
73 | WHITESPACE@[81; 82) " " | ||
74 | BLOCK_EXPR@[82; 90) | ||
75 | BLOCK@[82; 90) | ||
76 | L_CURLY@[82; 83) "{" | ||
77 | WHITESPACE@[83; 84) " " | ||
78 | RECORD_LIT@[84; 88) | ||
79 | PATH@[84; 85) | ||
80 | PATH_SEGMENT@[84; 85) | ||
81 | NAME_REF@[84; 85) | ||
82 | IDENT@[84; 85) "S" | ||
83 | WHITESPACE@[85; 86) " " | ||
84 | RECORD_FIELD_LIST@[86; 88) | ||
85 | L_CURLY@[86; 87) "{" | ||
86 | R_CURLY@[87; 88) "}" | ||
87 | WHITESPACE@[88; 89) " " | ||
88 | R_CURLY@[89; 90) "}" | ||
89 | WHITESPACE@[90; 91) " " | ||
90 | MATCH_ARM_LIST@[91; 93) | ||
91 | L_CURLY@[91; 92) "{" | ||
92 | R_CURLY@[92; 93) "}" | ||
93 | SEMI@[93; 94) ";" | ||
94 | WHITESPACE@[94; 95) "\n" | ||
95 | R_CURLY@[95; 96) "}" | ||
96 | WHITESPACE@[96; 97) "\n" | ||
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.rs b/crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.rs index c39fe8e68..e7b7cfc6b 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.rs +++ b/crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.rs | |||
@@ -1 +1,5 @@ | |||
1 | fn foo() { let x = 1..; } | 1 | fn foo() { |
2 | let x = 1..; | ||
3 | match 1.. { _ => () }; | ||
4 | match a.b()..S { _ => () }; | ||
5 | } | ||
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.txt b/crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.txt index f3c292f5e..9f8a6b0f6 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.txt +++ b/crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.txt | |||
@@ -1,5 +1,5 @@ | |||
1 | SOURCE_FILE@[0; 26) | 1 | SOURCE_FILE@[0; 89) |
2 | FN_DEF@[0; 25) | 2 | FN_DEF@[0; 88) |
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,24 +8,89 @@ SOURCE_FILE@[0; 26) | |||
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_EXPR@[9; 25) | 11 | BLOCK_EXPR@[9; 88) |
12 | BLOCK@[9; 25) | 12 | BLOCK@[9; 88) |
13 | L_CURLY@[9; 10) "{" | 13 | L_CURLY@[9; 10) "{" |
14 | WHITESPACE@[10; 11) " " | 14 | WHITESPACE@[10; 15) "\n " |
15 | LET_STMT@[11; 23) | 15 | LET_STMT@[15; 27) |
16 | LET_KW@[11; 14) "let" | 16 | LET_KW@[15; 18) "let" |
17 | WHITESPACE@[14; 15) " " | ||
18 | BIND_PAT@[15; 16) | ||
19 | NAME@[15; 16) | ||
20 | IDENT@[15; 16) "x" | ||
21 | WHITESPACE@[16; 17) " " | ||
22 | EQ@[17; 18) "=" | ||
23 | WHITESPACE@[18; 19) " " | 17 | WHITESPACE@[18; 19) " " |
24 | RANGE_EXPR@[19; 22) | 18 | BIND_PAT@[19; 20) |
25 | LITERAL@[19; 20) | 19 | NAME@[19; 20) |
26 | INT_NUMBER@[19; 20) "1" | 20 | IDENT@[19; 20) "x" |
27 | DOTDOT@[20; 22) ".." | 21 | WHITESPACE@[20; 21) " " |
28 | SEMI@[22; 23) ";" | 22 | EQ@[21; 22) "=" |
29 | WHITESPACE@[23; 24) " " | 23 | WHITESPACE@[22; 23) " " |
30 | R_CURLY@[24; 25) "}" | 24 | RANGE_EXPR@[23; 26) |
31 | WHITESPACE@[25; 26) "\n" | 25 | LITERAL@[23; 24) |
26 | INT_NUMBER@[23; 24) "1" | ||
27 | DOTDOT@[24; 26) ".." | ||
28 | SEMI@[26; 27) ";" | ||
29 | WHITESPACE@[27; 32) "\n " | ||
30 | EXPR_STMT@[32; 54) | ||
31 | MATCH_EXPR@[32; 53) | ||
32 | MATCH_KW@[32; 37) "match" | ||
33 | WHITESPACE@[37; 38) " " | ||
34 | RANGE_EXPR@[38; 41) | ||
35 | LITERAL@[38; 39) | ||
36 | INT_NUMBER@[38; 39) "1" | ||
37 | DOTDOT@[39; 41) ".." | ||
38 | WHITESPACE@[41; 42) " " | ||
39 | MATCH_ARM_LIST@[42; 53) | ||
40 | L_CURLY@[42; 43) "{" | ||
41 | WHITESPACE@[43; 44) " " | ||
42 | MATCH_ARM@[44; 51) | ||
43 | PLACEHOLDER_PAT@[44; 45) | ||
44 | UNDERSCORE@[44; 45) "_" | ||
45 | WHITESPACE@[45; 46) " " | ||
46 | FAT_ARROW@[46; 48) "=>" | ||
47 | WHITESPACE@[48; 49) " " | ||
48 | TUPLE_EXPR@[49; 51) | ||
49 | L_PAREN@[49; 50) "(" | ||
50 | R_PAREN@[50; 51) ")" | ||
51 | WHITESPACE@[51; 52) " " | ||
52 | R_CURLY@[52; 53) "}" | ||
53 | SEMI@[53; 54) ";" | ||
54 | WHITESPACE@[54; 59) "\n " | ||
55 | EXPR_STMT@[59; 86) | ||
56 | MATCH_EXPR@[59; 85) | ||
57 | MATCH_KW@[59; 64) "match" | ||
58 | WHITESPACE@[64; 65) " " | ||
59 | RANGE_EXPR@[65; 73) | ||
60 | METHOD_CALL_EXPR@[65; 70) | ||
61 | PATH_EXPR@[65; 66) | ||
62 | PATH@[65; 66) | ||
63 | PATH_SEGMENT@[65; 66) | ||
64 | NAME_REF@[65; 66) | ||
65 | IDENT@[65; 66) "a" | ||
66 | DOT@[66; 67) "." | ||
67 | NAME_REF@[67; 68) | ||
68 | IDENT@[67; 68) "b" | ||
69 | ARG_LIST@[68; 70) | ||
70 | L_PAREN@[68; 69) "(" | ||
71 | R_PAREN@[69; 70) ")" | ||
72 | DOTDOT@[70; 72) ".." | ||
73 | PATH_EXPR@[72; 73) | ||
74 | PATH@[72; 73) | ||
75 | PATH_SEGMENT@[72; 73) | ||
76 | NAME_REF@[72; 73) | ||
77 | IDENT@[72; 73) "S" | ||
78 | WHITESPACE@[73; 74) " " | ||
79 | MATCH_ARM_LIST@[74; 85) | ||
80 | L_CURLY@[74; 75) "{" | ||
81 | WHITESPACE@[75; 76) " " | ||
82 | MATCH_ARM@[76; 83) | ||
83 | PLACEHOLDER_PAT@[76; 77) | ||
84 | UNDERSCORE@[76; 77) "_" | ||
85 | WHITESPACE@[77; 78) " " | ||
86 | FAT_ARROW@[78; 80) "=>" | ||
87 | WHITESPACE@[80; 81) " " | ||
88 | TUPLE_EXPR@[81; 83) | ||
89 | L_PAREN@[81; 82) "(" | ||
90 | R_PAREN@[82; 83) ")" | ||
91 | WHITESPACE@[83; 84) " " | ||
92 | R_CURLY@[84; 85) "}" | ||
93 | SEMI@[85; 86) ";" | ||
94 | WHITESPACE@[86; 87) "\n" | ||
95 | R_CURLY@[87; 88) "}" | ||
96 | WHITESPACE@[88; 89) "\n" | ||
diff --git a/docs/user/README.md b/docs/user/README.md index 9d03cad1c..44d6ee739 100644 --- a/docs/user/README.md +++ b/docs/user/README.md | |||
@@ -116,6 +116,22 @@ to load path and require it in `init.el` | |||
116 | [coc-vim-conf]: https://github.com/neoclide/coc.nvim/#example-vim-configuration | 116 | [coc-vim-conf]: https://github.com/neoclide/coc.nvim/#example-vim-configuration |
117 | [coc-rust-analyzer]: https://github.com/fannheyward/coc-rust-analyzer | 117 | [coc-rust-analyzer]: https://github.com/fannheyward/coc-rust-analyzer |
118 | 118 | ||
119 | ## Vim and NeoVim Alternative | ||
120 | |||
121 | * Install LanguageClient-neovim by following the instructions [here][lang-client-neovim] | ||
122 | - No extra run-time is required as this server is written in Rust | ||
123 | - The github project wiki has extra tips on configuration | ||
124 | |||
125 | * Configure by adding this to your vim/neovim config file (replacing the existing rust specific line if it exists): | ||
126 | |||
127 | ``` | ||
128 | let g:LanguageClient_serverCommands = { | ||
129 | \ 'rust': ['ra_lsp_server'], | ||
130 | \ } | ||
131 | ``` | ||
132 | |||
133 | [lang-client-neovim]: https://github.com/autozimu/LanguageClient-neovim | ||
134 | |||
119 | 135 | ||
120 | ## Sublime Text 3 | 136 | ## Sublime Text 3 |
121 | 137 | ||
diff --git a/docs/user/features.md b/docs/user/features.md index eb81cba26..0ce8f577b 100644 --- a/docs/user/features.md +++ b/docs/user/features.md | |||
@@ -166,6 +166,20 @@ impl Foo for S { | |||
166 | } | 166 | } |
167 | ``` | 167 | ``` |
168 | 168 | ||
169 | - Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) | ||
170 | |||
171 | ```rust | ||
172 | // before: | ||
173 | fn example(x: bool) -> bool { | ||
174 | !x || !x | ||
175 | } | ||
176 | |||
177 | // after: | ||
178 | fn example(x: bool) -> bool { | ||
179 | !(x && x) | ||
180 | } | ||
181 | ``` | ||
182 | |||
169 | - Import path | 183 | - Import path |
170 | 184 | ||
171 | ```rust | 185 | ```rust |