aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir/src/diagnostics.rs28
-rw-r--r--crates/ra_hir/src/expr.rs1
-rw-r--r--crates/ra_hir/src/expr/validation.rs53
-rw-r--r--crates/ra_hir/src/name.rs2
-rw-r--r--crates/ra_hir/src/ty/infer.rs19
-rw-r--r--crates/ra_ide_api/src/diagnostics.rs215
-rw-r--r--crates/ra_parser/src/grammar/expressions/atom.rs2
-rw-r--r--crates/ra_parser/src/grammar/patterns.rs49
-rw-r--r--crates/ra_parser/src/syntax_kind/generated.rs1
-rw-r--r--crates/ra_syntax/src/ast/generated.rs39
-rw-r--r--crates/ra_syntax/src/grammar.ron3
-rw-r--r--crates/ra_syntax/src/ptr.rs7
-rw-r--r--crates/ra_syntax/test_data/parser/err/0034_bad_box_pattern.rs6
-rw-r--r--crates/ra_syntax/test_data/parser/err/0034_bad_box_pattern.txt95
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0066_match_arm.rs2
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0066_match_arm.txt59
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0112_bind_pat.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0112_bind_pat.txt41
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.rs5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.txt109
20 files changed, 624 insertions, 113 deletions
diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs
index 301109cb8..475dd5766 100644
--- a/crates/ra_hir/src/diagnostics.rs
+++ b/crates/ra_hir/src/diagnostics.rs
@@ -143,3 +143,31 @@ impl AstDiagnostic for MissingFields {
143 ast::RecordFieldList::cast(node).unwrap() 143 ast::RecordFieldList::cast(node).unwrap()
144 } 144 }
145} 145}
146
147#[derive(Debug)]
148pub struct MissingOkInTailExpr {
149 pub file: HirFileId,
150 pub expr: AstPtr<ast::Expr>,
151}
152
153impl Diagnostic for MissingOkInTailExpr {
154 fn message(&self) -> String {
155 "wrap return expression in Ok".to_string()
156 }
157 fn source(&self) -> Source<SyntaxNodePtr> {
158 Source { file_id: self.file, ast: self.expr.into() }
159 }
160 fn as_any(&self) -> &(dyn Any + Send + 'static) {
161 self
162 }
163}
164
165impl AstDiagnostic for MissingOkInTailExpr {
166 type AST = ast::Expr;
167
168 fn ast(&self, db: &impl HirDatabase) -> Self::AST {
169 let root = db.parse_or_expand(self.file).unwrap();
170 let node = self.source().ast.to_node(&root);
171 ast::Expr::cast(node).unwrap()
172 }
173}
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index 9e8584908..7cdc7555c 100644
--- a/crates/ra_hir/src/expr.rs
+++ b/crates/ra_hir/src/expr.rs
@@ -1020,6 +1020,7 @@ where
1020 } 1020 }
1021 1021
1022 // FIXME: implement 1022 // FIXME: implement
1023 ast::Pat::BoxPat(_) => Pat::Missing,
1023 ast::Pat::LiteralPat(_) => Pat::Missing, 1024 ast::Pat::LiteralPat(_) => Pat::Missing,
1024 ast::Pat::SlicePat(_) | ast::Pat::RangePat(_) => Pat::Missing, 1025 ast::Pat::SlicePat(_) | ast::Pat::RangePat(_) => Pat::Missing,
1025 }; 1026 };
diff --git a/crates/ra_hir/src/expr/validation.rs b/crates/ra_hir/src/expr/validation.rs
index 62f7d41f5..5d9d59ff8 100644
--- a/crates/ra_hir/src/expr/validation.rs
+++ b/crates/ra_hir/src/expr/validation.rs
@@ -6,11 +6,14 @@ use ra_syntax::ast::{AstNode, RecordLit};
6use super::{Expr, ExprId, RecordLitField}; 6use super::{Expr, ExprId, RecordLitField};
7use crate::{ 7use crate::{
8 adt::AdtDef, 8 adt::AdtDef,
9 diagnostics::{DiagnosticSink, MissingFields}, 9 diagnostics::{DiagnosticSink, MissingFields, MissingOkInTailExpr},
10 expr::AstPtr, 10 expr::AstPtr,
11 ty::InferenceResult, 11 name,
12 Function, HasSource, HirDatabase, Name, Path, 12 path::{PathKind, PathSegment},
13 ty::{ApplicationTy, InferenceResult, Ty, TypeCtor},
14 Function, HasSource, HirDatabase, ModuleDef, Name, Path, PerNs, Resolution,
13}; 15};
16use ra_syntax::ast;
14 17
15pub(crate) struct ExprValidator<'a, 'b: 'a> { 18pub(crate) struct ExprValidator<'a, 'b: 'a> {
16 func: Function, 19 func: Function,
@@ -29,11 +32,17 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
29 32
30 pub(crate) fn validate_body(&mut self, db: &impl HirDatabase) { 33 pub(crate) fn validate_body(&mut self, db: &impl HirDatabase) {
31 let body = self.func.body(db); 34 let body = self.func.body(db);
35
32 for e in body.exprs() { 36 for e in body.exprs() {
33 if let (id, Expr::RecordLit { path, fields, spread }) = e { 37 if let (id, Expr::RecordLit { path, fields, spread }) = e {
34 self.validate_record_literal(id, path, fields, *spread, db); 38 self.validate_record_literal(id, path, fields, *spread, db);
35 } 39 }
36 } 40 }
41
42 let body_expr = &body[body.body_expr()];
43 if let Expr::Block { statements: _, tail: Some(t) } = body_expr {
44 self.validate_results_in_tail_expr(*t, db);
45 }
37 } 46 }
38 47
39 fn validate_record_literal( 48 fn validate_record_literal(
@@ -87,4 +96,42 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
87 }) 96 })
88 } 97 }
89 } 98 }
99
100 fn validate_results_in_tail_expr(&mut self, id: ExprId, db: &impl HirDatabase) {
101 let mismatch = match self.infer.type_mismatch_for_expr(id) {
102 Some(m) => m,
103 None => return,
104 };
105
106 let std_result_path = Path {
107 kind: PathKind::Abs,
108 segments: vec![
109 PathSegment { name: name::STD, args_and_bindings: None },
110 PathSegment { name: name::RESULT_MOD, args_and_bindings: None },
111 PathSegment { name: name::RESULT_TYPE, args_and_bindings: None },
112 ],
113 };
114
115 let resolver = self.func.resolver(db);
116 let std_result_enum =
117 match resolver.resolve_path_segments(db, &std_result_path).into_fully_resolved() {
118 PerNs { types: Some(Resolution::Def(ModuleDef::Enum(e))), .. } => e,
119 _ => return,
120 };
121
122 let std_result_ctor = TypeCtor::Adt(AdtDef::Enum(std_result_enum));
123 let params = match &mismatch.expected {
124 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &std_result_ctor => parameters,
125 _ => return,
126 };
127
128 if params.len() == 2 && &params[0] == &mismatch.actual {
129 let source_map = self.func.body_source_map(db);
130 let file_id = self.func.source(db).file_id;
131
132 if let Some(expr) = source_map.expr_syntax(id).and_then(|n| n.cast::<ast::Expr>()) {
133 self.sink.push(MissingOkInTailExpr { file: file_id, expr });
134 }
135 }
136 }
90} 137}
diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs
index 6d14eea8e..9c4822d91 100644
--- a/crates/ra_hir/src/name.rs
+++ b/crates/ra_hir/src/name.rs
@@ -120,6 +120,8 @@ pub(crate) const TRY: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"Try")
120pub(crate) const OK: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"Ok")); 120pub(crate) const OK: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"Ok"));
121pub(crate) const FUTURE_MOD: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"future")); 121pub(crate) const FUTURE_MOD: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"future"));
122pub(crate) const FUTURE_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Future")); 122pub(crate) const FUTURE_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Future"));
123pub(crate) const RESULT_MOD: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"result"));
124pub(crate) const RESULT_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Result"));
123pub(crate) const OUTPUT: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Output")); 125pub(crate) const OUTPUT: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Output"));
124 126
125fn resolve_name(text: &SmolStr) -> SmolStr { 127fn resolve_name(text: &SmolStr) -> SmolStr {
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs
index b33de5687..d94e8154b 100644
--- a/crates/ra_hir/src/ty/infer.rs
+++ b/crates/ra_hir/src/ty/infer.rs
@@ -106,6 +106,13 @@ impl Default for BindingMode {
106 } 106 }
107} 107}
108 108
109/// A mismatch between an expected and an inferred type.
110#[derive(Clone, PartialEq, Eq, Debug, Hash)]
111pub struct TypeMismatch {
112 pub expected: Ty,
113 pub actual: Ty,
114}
115
109/// The result of type inference: A mapping from expressions and patterns to types. 116/// The result of type inference: A mapping from expressions and patterns to types.
110#[derive(Clone, PartialEq, Eq, Debug, Default)] 117#[derive(Clone, PartialEq, Eq, Debug, Default)]
111pub struct InferenceResult { 118pub struct InferenceResult {
@@ -120,6 +127,7 @@ pub struct InferenceResult {
120 diagnostics: Vec<InferenceDiagnostic>, 127 diagnostics: Vec<InferenceDiagnostic>,
121 pub(super) type_of_expr: ArenaMap<ExprId, Ty>, 128 pub(super) type_of_expr: ArenaMap<ExprId, Ty>,
122 pub(super) type_of_pat: ArenaMap<PatId, Ty>, 129 pub(super) type_of_pat: ArenaMap<PatId, Ty>,
130 pub(super) type_mismatches: ArenaMap<ExprId, TypeMismatch>,
123} 131}
124 132
125impl InferenceResult { 133impl InferenceResult {
@@ -141,6 +149,9 @@ impl InferenceResult {
141 pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<ImplItem> { 149 pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<ImplItem> {
142 self.assoc_resolutions.get(&id.into()).copied() 150 self.assoc_resolutions.get(&id.into()).copied()
143 } 151 }
152 pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> {
153 self.type_mismatches.get(expr)
154 }
144 pub(crate) fn add_diagnostics( 155 pub(crate) fn add_diagnostics(
145 &self, 156 &self,
146 db: &impl HirDatabase, 157 db: &impl HirDatabase,
@@ -1345,9 +1356,15 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
1345 }; 1356 };
1346 // use a new type variable if we got Ty::Unknown here 1357 // use a new type variable if we got Ty::Unknown here
1347 let ty = self.insert_type_vars_shallow(ty); 1358 let ty = self.insert_type_vars_shallow(ty);
1348 self.unify(&ty, &expected.ty); 1359 let could_unify = self.unify(&ty, &expected.ty);
1349 let ty = self.resolve_ty_as_possible(&mut vec![], ty); 1360 let ty = self.resolve_ty_as_possible(&mut vec![], ty);
1350 self.write_expr_ty(tgt_expr, ty.clone()); 1361 self.write_expr_ty(tgt_expr, ty.clone());
1362 if !could_unify {
1363 self.result.type_mismatches.insert(
1364 tgt_expr,
1365 TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() },
1366 );
1367 }
1351 ty 1368 ty
1352 } 1369 }
1353 1370
diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs
index c2b959cb3..1a4882824 100644
--- a/crates/ra_ide_api/src/diagnostics.rs
+++ b/crates/ra_ide_api/src/diagnostics.rs
@@ -75,6 +75,19 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
75 severity: Severity::Error, 75 severity: Severity::Error,
76 fix: Some(fix), 76 fix: Some(fix),
77 }) 77 })
78 })
79 .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
80 let node = d.ast(db);
81 let mut builder = TextEditBuilder::default();
82 let replacement = format!("Ok({})", node.syntax());
83 builder.replace(node.syntax().text_range(), replacement);
84 let fix = SourceChange::source_file_edit_from("wrap with ok", file_id, builder.finish());
85 res.borrow_mut().push(Diagnostic {
86 range: d.highlight_range(),
87 message: d.message(),
88 severity: Severity::Error,
89 fix: Some(fix),
90 })
78 }); 91 });
79 if let Some(m) = source_binder::module_from_file_id(db, file_id) { 92 if let Some(m) = source_binder::module_from_file_id(db, file_id) {
80 m.diagnostics(db, &mut sink); 93 m.diagnostics(db, &mut sink);
@@ -171,10 +184,11 @@ fn check_struct_shorthand_initialization(
171#[cfg(test)] 184#[cfg(test)]
172mod tests { 185mod tests {
173 use insta::assert_debug_snapshot_matches; 186 use insta::assert_debug_snapshot_matches;
187 use join_to_string::join;
174 use ra_syntax::SourceFile; 188 use ra_syntax::SourceFile;
175 use test_utils::assert_eq_text; 189 use test_utils::assert_eq_text;
176 190
177 use crate::mock_analysis::single_file; 191 use crate::mock_analysis::{analysis_and_position, single_file};
178 192
179 use super::*; 193 use super::*;
180 194
@@ -203,6 +217,48 @@ mod tests {
203 assert_eq_text!(after, &actual); 217 assert_eq_text!(after, &actual);
204 } 218 }
205 219
220 /// Takes a multi-file input fixture with annotated cursor positions,
221 /// and checks that:
222 /// * a diagnostic is produced
223 /// * this diagnostic touches the input cursor position
224 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
225 fn check_apply_diagnostic_fix_from_position(fixture: &str, after: &str) {
226 let (analysis, file_position) = analysis_and_position(fixture);
227 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap();
228 let mut fix = diagnostic.fix.unwrap();
229 let edit = fix.source_file_edits.pop().unwrap().edit;
230 let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
231 let actual = edit.apply(&target_file_contents);
232
233 // Strip indent and empty lines from `after`, to match the behaviour of
234 // `parse_fixture` called from `analysis_and_position`.
235 let margin = fixture
236 .lines()
237 .filter(|it| it.trim_start().starts_with("//-"))
238 .map(|it| it.len() - it.trim_start().len())
239 .next()
240 .expect("empty fixture");
241 let after = join(after.lines().filter_map(|line| {
242 if line.len() > margin {
243 Some(&line[margin..])
244 } else {
245 None
246 }
247 }))
248 .separator("\n")
249 .suffix("\n")
250 .to_string();
251
252 assert_eq_text!(&after, &actual);
253 assert!(
254 diagnostic.range.start() <= file_position.offset
255 && diagnostic.range.end() >= file_position.offset,
256 "diagnostic range {} does not touch cursor position {}",
257 diagnostic.range,
258 file_position.offset
259 );
260 }
261
206 fn check_apply_diagnostic_fix(before: &str, after: &str) { 262 fn check_apply_diagnostic_fix(before: &str, after: &str) {
207 let (analysis, file_id) = single_file(before); 263 let (analysis, file_id) = single_file(before);
208 let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); 264 let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap();
@@ -212,6 +268,14 @@ mod tests {
212 assert_eq_text!(after, &actual); 268 assert_eq_text!(after, &actual);
213 } 269 }
214 270
271 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
272 /// apply to the file containing the cursor.
273 fn check_no_diagnostic_for_target_file(fixture: &str) {
274 let (analysis, file_position) = analysis_and_position(fixture);
275 let diagnostics = analysis.diagnostics(file_position.file_id).unwrap();
276 assert_eq!(diagnostics.len(), 0);
277 }
278
215 fn check_no_diagnostic(content: &str) { 279 fn check_no_diagnostic(content: &str) {
216 let (analysis, file_id) = single_file(content); 280 let (analysis, file_id) = single_file(content);
217 let diagnostics = analysis.diagnostics(file_id).unwrap(); 281 let diagnostics = analysis.diagnostics(file_id).unwrap();
@@ -219,6 +283,155 @@ mod tests {
219 } 283 }
220 284
221 #[test] 285 #[test]
286 fn test_wrap_return_type() {
287 let before = r#"
288 //- /main.rs
289 use std::{string::String, result::Result::{self, Ok, Err}};
290
291 fn div(x: i32, y: i32) -> Result<i32, String> {
292 if y == 0 {
293 return Err("div by zero".into());
294 }
295 x / y<|>
296 }
297
298 //- /std/lib.rs
299 pub mod string {
300 pub struct String { }
301 }
302 pub mod result {
303 pub enum Result<T, E> { Ok(T), Err(E) }
304 }
305 "#;
306 let after = r#"
307 use std::{string::String, result::Result::{self, Ok, Err}};
308
309 fn div(x: i32, y: i32) -> Result<i32, String> {
310 if y == 0 {
311 return Err("div by zero".into());
312 }
313 Ok(x / y)
314 }
315 "#;
316 check_apply_diagnostic_fix_from_position(before, after);
317 }
318
319 #[test]
320 fn test_wrap_return_type_handles_generic_functions() {
321 let before = r#"
322 //- /main.rs
323 use std::result::Result::{self, Ok, Err};
324
325 fn div<T>(x: T) -> Result<T, i32> {
326 if x == 0 {
327 return Err(7);
328 }
329 <|>x
330 }
331
332 //- /std/lib.rs
333 pub mod result {
334 pub enum Result<T, E> { Ok(T), Err(E) }
335 }
336 "#;
337 let after = r#"
338 use std::result::Result::{self, Ok, Err};
339
340 fn div<T>(x: T) -> Result<T, i32> {
341 if x == 0 {
342 return Err(7);
343 }
344 Ok(x)
345 }
346 "#;
347 check_apply_diagnostic_fix_from_position(before, after);
348 }
349
350 #[test]
351 fn test_wrap_return_type_handles_type_aliases() {
352 let before = r#"
353 //- /main.rs
354 use std::{string::String, result::Result::{self, Ok, Err}};
355
356 type MyResult<T> = Result<T, String>;
357
358 fn div(x: i32, y: i32) -> MyResult<i32> {
359 if y == 0 {
360 return Err("div by zero".into());
361 }
362 x <|>/ y
363 }
364
365 //- /std/lib.rs
366 pub mod string {
367 pub struct String { }
368 }
369 pub mod result {
370 pub enum Result<T, E> { Ok(T), Err(E) }
371 }
372 "#;
373 let after = r#"
374 use std::{string::String, result::Result::{self, Ok, Err}};
375
376 type MyResult<T> = Result<T, String>;
377 fn div(x: i32, y: i32) -> MyResult<i32> {
378 if y == 0 {
379 return Err("div by zero".into());
380 }
381 Ok(x / y)
382 }
383 "#;
384 check_apply_diagnostic_fix_from_position(before, after);
385 }
386
387 #[test]
388 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
389 let content = r#"
390 //- /main.rs
391 use std::{string::String, result::Result::{self, Ok, Err}};
392
393 fn foo() -> Result<String, i32> {
394 0<|>
395 }
396
397 //- /std/lib.rs
398 pub mod string {
399 pub struct String { }
400 }
401 pub mod result {
402 pub enum Result<T, E> { Ok(T), Err(E) }
403 }
404 "#;
405 check_no_diagnostic_for_target_file(content);
406 }
407
408 #[test]
409 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
410 let content = r#"
411 //- /main.rs
412 use std::{string::String, result::Result::{self, Ok, Err}};
413
414 enum SomeOtherEnum {
415 Ok(i32),
416 Err(String),
417 }
418
419 fn foo() -> SomeOtherEnum {
420 0<|>
421 }
422
423 //- /std/lib.rs
424 pub mod string {
425 pub struct String { }
426 }
427 pub mod result {
428 pub enum Result<T, E> { Ok(T), Err(E) }
429 }
430 "#;
431 check_no_diagnostic_for_target_file(content);
432 }
433
434 #[test]
222 fn test_fill_struct_fields_empty() { 435 fn test_fill_struct_fields_empty() {
223 let before = r" 436 let before = r"
224 struct TestStruct { 437 struct TestStruct {
diff --git a/crates/ra_parser/src/grammar/expressions/atom.rs b/crates/ra_parser/src/grammar/expressions/atom.rs
index ab8fb9f6e..bc942ae01 100644
--- a/crates/ra_parser/src/grammar/expressions/atom.rs
+++ b/crates/ra_parser/src/grammar/expressions/atom.rs
@@ -414,8 +414,6 @@ pub(crate) fn match_arm_list(p: &mut Parser) {
414// X | Y if Z => (), 414// X | Y if Z => (),
415// | X | Y if Z => (), 415// | X | Y if Z => (),
416// | X => (), 416// | X => (),
417// box X => (),
418// Some(box X) => (),
419// }; 417// };
420// } 418// }
421fn match_arm(p: &mut Parser) -> BlockLike { 419fn match_arm(p: &mut Parser) -> BlockLike {
diff --git a/crates/ra_parser/src/grammar/patterns.rs b/crates/ra_parser/src/grammar/patterns.rs
index 8979aa499..eae70ab85 100644
--- a/crates/ra_parser/src/grammar/patterns.rs
+++ b/crates/ra_parser/src/grammar/patterns.rs
@@ -56,37 +56,33 @@ const PAT_RECOVERY_SET: TokenSet =
56 token_set![LET_KW, IF_KW, WHILE_KW, LOOP_KW, MATCH_KW, R_PAREN, COMMA]; 56 token_set![LET_KW, IF_KW, WHILE_KW, LOOP_KW, MATCH_KW, R_PAREN, COMMA];
57 57
58fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> { 58fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> {
59 let la0 = p.nth(0); 59 // Checks the token after an IDENT to see if a pattern is a path (Struct { .. }) or macro
60 let la1 = p.nth(1); 60 // (T![x]).
61 if la0 == T![ref] 61 let is_path_or_macro_pat =
62 || la0 == T![mut] 62 |la1| la1 == T![::] || la1 == T!['('] || la1 == T!['{'] || la1 == T![!];
63 || la0 == T![box]
64 || (la0 == IDENT && !(la1 == T![::] || la1 == T!['('] || la1 == T!['{'] || la1 == T![!]))
65 {
66 return Some(bind_pat(p, true));
67 }
68 if paths::is_use_path_start(p) {
69 return Some(path_pat(p));
70 }
71 63
72 if is_literal_pat_start(p) { 64 let m = match p.nth(0) {
73 return Some(literal_pat(p)); 65 T![box] => box_pat(p),
74 } 66 T![ref] | T![mut] | IDENT if !is_path_or_macro_pat(p.nth(1)) => bind_pat(p, true),
67
68 _ if paths::is_use_path_start(p) => path_pat(p),
69 _ if is_literal_pat_start(p) => literal_pat(p),
75 70
76 let m = match la0 {
77 T![_] => placeholder_pat(p), 71 T![_] => placeholder_pat(p),
78 T![&] => ref_pat(p), 72 T![&] => ref_pat(p),
79 T!['('] => tuple_pat(p), 73 T!['('] => tuple_pat(p),
80 T!['['] => slice_pat(p), 74 T!['['] => slice_pat(p),
75
81 _ => { 76 _ => {
82 p.err_recover("expected pattern", recovery_set); 77 p.err_recover("expected pattern", recovery_set);
83 return None; 78 return None;
84 } 79 }
85 }; 80 };
81
86 Some(m) 82 Some(m)
87} 83}
88 84
89fn is_literal_pat_start(p: &mut Parser) -> bool { 85fn is_literal_pat_start(p: &Parser) -> bool {
90 p.at(T![-]) && (p.nth(1) == INT_NUMBER || p.nth(1) == FLOAT_NUMBER) 86 p.at(T![-]) && (p.nth(1) == INT_NUMBER || p.nth(1) == FLOAT_NUMBER)
91 || p.at_ts(expressions::LITERAL_FIRST) 87 || p.at_ts(expressions::LITERAL_FIRST)
92} 88}
@@ -165,6 +161,9 @@ fn record_field_pat_list(p: &mut Parser) {
165 T![..] => p.bump(), 161 T![..] => p.bump(),
166 IDENT if p.nth(1) == T![:] => record_field_pat(p), 162 IDENT if p.nth(1) == T![:] => record_field_pat(p),
167 T!['{'] => error_block(p, "expected ident"), 163 T!['{'] => error_block(p, "expected ident"),
164 T![box] => {
165 box_pat(p);
166 }
168 _ => { 167 _ => {
169 bind_pat(p, false); 168 bind_pat(p, false);
170 } 169 }
@@ -261,11 +260,9 @@ fn pat_list(p: &mut Parser, ket: SyntaxKind) {
261// let ref mut d = (); 260// let ref mut d = ();
262// let e @ _ = (); 261// let e @ _ = ();
263// let ref mut f @ g @ _ = (); 262// let ref mut f @ g @ _ = ();
264// let box i = Box::new(1i32);
265// } 263// }
266fn bind_pat(p: &mut Parser, with_at: bool) -> CompletedMarker { 264fn bind_pat(p: &mut Parser, with_at: bool) -> CompletedMarker {
267 let m = p.start(); 265 let m = p.start();
268 p.eat(T![box]);
269 p.eat(T![ref]); 266 p.eat(T![ref]);
270 p.eat(T![mut]); 267 p.eat(T![mut]);
271 name(p); 268 name(p);
@@ -274,3 +271,17 @@ fn bind_pat(p: &mut Parser, with_at: bool) -> CompletedMarker {
274 } 271 }
275 m.complete(p, BIND_PAT) 272 m.complete(p, BIND_PAT)
276} 273}
274
275// test box_pat
276// fn main() {
277// let box i = ();
278// let box Outer { box i, j: box Inner(box &x) } = ();
279// let box ref mut i = ();
280// }
281fn box_pat(p: &mut Parser) -> CompletedMarker {
282 assert!(p.at(T![box]));
283 let m = p.start();
284 p.bump();
285 pattern(p);
286 m.complete(p, BOX_PAT)
287}
diff --git a/crates/ra_parser/src/syntax_kind/generated.rs b/crates/ra_parser/src/syntax_kind/generated.rs
index f15e98e68..8ba29ebf8 100644
--- a/crates/ra_parser/src/syntax_kind/generated.rs
+++ b/crates/ra_parser/src/syntax_kind/generated.rs
@@ -149,6 +149,7 @@ pub enum SyntaxKind {
149 IMPL_TRAIT_TYPE, 149 IMPL_TRAIT_TYPE,
150 DYN_TRAIT_TYPE, 150 DYN_TRAIT_TYPE,
151 REF_PAT, 151 REF_PAT,
152 BOX_PAT,
152 BIND_PAT, 153 BIND_PAT,
153 PLACEHOLDER_PAT, 154 PLACEHOLDER_PAT,
154 PATH_PAT, 155 PATH_PAT,
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index 07cc3e0db..90480b6ca 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -307,6 +307,33 @@ impl BlockExpr {
307 } 307 }
308} 308}
309#[derive(Debug, Clone, PartialEq, Eq, Hash)] 309#[derive(Debug, Clone, PartialEq, Eq, Hash)]
310pub struct BoxPat {
311 pub(crate) syntax: SyntaxNode,
312}
313impl AstNode for BoxPat {
314 fn can_cast(kind: SyntaxKind) -> bool {
315 match kind {
316 BOX_PAT => true,
317 _ => false,
318 }
319 }
320 fn cast(syntax: SyntaxNode) -> Option<Self> {
321 if Self::can_cast(syntax.kind()) {
322 Some(Self { syntax })
323 } else {
324 None
325 }
326 }
327 fn syntax(&self) -> &SyntaxNode {
328 &self.syntax
329 }
330}
331impl BoxPat {
332 pub fn pat(&self) -> Option<Pat> {
333 AstChildren::new(&self.syntax).next()
334 }
335}
336#[derive(Debug, Clone, PartialEq, Eq, Hash)]
310pub struct BreakExpr { 337pub struct BreakExpr {
311 pub(crate) syntax: SyntaxNode, 338 pub(crate) syntax: SyntaxNode,
312} 339}
@@ -2063,6 +2090,7 @@ impl ParenType {
2063#[derive(Debug, Clone, PartialEq, Eq, Hash)] 2090#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2064pub enum Pat { 2091pub enum Pat {
2065 RefPat(RefPat), 2092 RefPat(RefPat),
2093 BoxPat(BoxPat),
2066 BindPat(BindPat), 2094 BindPat(BindPat),
2067 PlaceholderPat(PlaceholderPat), 2095 PlaceholderPat(PlaceholderPat),
2068 PathPat(PathPat), 2096 PathPat(PathPat),
@@ -2078,6 +2106,11 @@ impl From<RefPat> for Pat {
2078 Pat::RefPat(node) 2106 Pat::RefPat(node)
2079 } 2107 }
2080} 2108}
2109impl From<BoxPat> for Pat {
2110 fn from(node: BoxPat) -> Pat {
2111 Pat::BoxPat(node)
2112 }
2113}
2081impl From<BindPat> for Pat { 2114impl From<BindPat> for Pat {
2082 fn from(node: BindPat) -> Pat { 2115 fn from(node: BindPat) -> Pat {
2083 Pat::BindPat(node) 2116 Pat::BindPat(node)
@@ -2126,14 +2159,15 @@ impl From<LiteralPat> for Pat {
2126impl AstNode for Pat { 2159impl AstNode for Pat {
2127 fn can_cast(kind: SyntaxKind) -> bool { 2160 fn can_cast(kind: SyntaxKind) -> bool {
2128 match kind { 2161 match kind {
2129 REF_PAT | BIND_PAT | PLACEHOLDER_PAT | PATH_PAT | RECORD_PAT | TUPLE_STRUCT_PAT 2162 REF_PAT | BOX_PAT | BIND_PAT | PLACEHOLDER_PAT | PATH_PAT | RECORD_PAT
2130 | TUPLE_PAT | SLICE_PAT | RANGE_PAT | LITERAL_PAT => true, 2163 | TUPLE_STRUCT_PAT | TUPLE_PAT | SLICE_PAT | RANGE_PAT | LITERAL_PAT => true,
2131 _ => false, 2164 _ => false,
2132 } 2165 }
2133 } 2166 }
2134 fn cast(syntax: SyntaxNode) -> Option<Self> { 2167 fn cast(syntax: SyntaxNode) -> Option<Self> {
2135 let res = match syntax.kind() { 2168 let res = match syntax.kind() {
2136 REF_PAT => Pat::RefPat(RefPat { syntax }), 2169 REF_PAT => Pat::RefPat(RefPat { syntax }),
2170 BOX_PAT => Pat::BoxPat(BoxPat { syntax }),
2137 BIND_PAT => Pat::BindPat(BindPat { syntax }), 2171 BIND_PAT => Pat::BindPat(BindPat { syntax }),
2138 PLACEHOLDER_PAT => Pat::PlaceholderPat(PlaceholderPat { syntax }), 2172 PLACEHOLDER_PAT => Pat::PlaceholderPat(PlaceholderPat { syntax }),
2139 PATH_PAT => Pat::PathPat(PathPat { syntax }), 2173 PATH_PAT => Pat::PathPat(PathPat { syntax }),
@@ -2150,6 +2184,7 @@ impl AstNode for Pat {
2150 fn syntax(&self) -> &SyntaxNode { 2184 fn syntax(&self) -> &SyntaxNode {
2151 match self { 2185 match self {
2152 Pat::RefPat(it) => &it.syntax, 2186 Pat::RefPat(it) => &it.syntax,
2187 Pat::BoxPat(it) => &it.syntax,
2153 Pat::BindPat(it) => &it.syntax, 2188 Pat::BindPat(it) => &it.syntax,
2154 Pat::PlaceholderPat(it) => &it.syntax, 2189 Pat::PlaceholderPat(it) => &it.syntax,
2155 Pat::PathPat(it) => &it.syntax, 2190 Pat::PathPat(it) => &it.syntax,
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index 4659192cd..1836862fe 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -158,6 +158,7 @@ Grammar(
158 "DYN_TRAIT_TYPE", 158 "DYN_TRAIT_TYPE",
159 159
160 "REF_PAT", 160 "REF_PAT",
161 "BOX_PAT",
161 "BIND_PAT", 162 "BIND_PAT",
162 "PLACEHOLDER_PAT", 163 "PLACEHOLDER_PAT",
163 "PATH_PAT", 164 "PATH_PAT",
@@ -523,6 +524,7 @@ Grammar(
523 ), 524 ),
524 525
525 "RefPat": ( options: [ "Pat" ]), 526 "RefPat": ( options: [ "Pat" ]),
527 "BoxPat": ( options: [ "Pat" ]),
526 "BindPat": ( 528 "BindPat": (
527 options: [ "Pat" ], 529 options: [ "Pat" ],
528 traits: ["NameOwner"] 530 traits: ["NameOwner"]
@@ -552,6 +554,7 @@ Grammar(
552 "Pat": ( 554 "Pat": (
553 enum: [ 555 enum: [
554 "RefPat", 556 "RefPat",
557 "BoxPat",
555 "BindPat", 558 "BindPat",
556 "PlaceholderPat", 559 "PlaceholderPat",
557 "PathPat", 560 "PathPat",
diff --git a/crates/ra_syntax/src/ptr.rs b/crates/ra_syntax/src/ptr.rs
index d24660ac3..992034ef0 100644
--- a/crates/ra_syntax/src/ptr.rs
+++ b/crates/ra_syntax/src/ptr.rs
@@ -31,6 +31,13 @@ impl SyntaxNodePtr {
31 pub fn kind(self) -> SyntaxKind { 31 pub fn kind(self) -> SyntaxKind {
32 self.kind 32 self.kind
33 } 33 }
34
35 pub fn cast<N: AstNode>(self) -> Option<AstPtr<N>> {
36 if !N::can_cast(self.kind()) {
37 return None;
38 }
39 Some(AstPtr { raw: self, _ty: PhantomData })
40 }
34} 41}
35 42
36/// Like `SyntaxNodePtr`, but remembers the type of node 43/// Like `SyntaxNodePtr`, but remembers the type of node
diff --git a/crates/ra_syntax/test_data/parser/err/0034_bad_box_pattern.rs b/crates/ra_syntax/test_data/parser/err/0034_bad_box_pattern.rs
new file mode 100644
index 000000000..d3fa2e468
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0034_bad_box_pattern.rs
@@ -0,0 +1,6 @@
1fn main() {
2 let ref box i = ();
3 let mut box i = ();
4 let ref mut box i = ();
5}
6
diff --git a/crates/ra_syntax/test_data/parser/err/0034_bad_box_pattern.txt b/crates/ra_syntax/test_data/parser/err/0034_bad_box_pattern.txt
new file mode 100644
index 000000000..0cdca4b55
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0034_bad_box_pattern.txt
@@ -0,0 +1,95 @@
1SOURCE_FILE@[0; 91)
2 FN_DEF@[0; 89)
3 FN_KW@[0; 2) "fn"
4 WHITESPACE@[2; 3) " "
5 NAME@[3; 7)
6 IDENT@[3; 7) "main"
7 PARAM_LIST@[7; 9)
8 L_PAREN@[7; 8) "("
9 R_PAREN@[8; 9) ")"
10 WHITESPACE@[9; 10) " "
11 BLOCK@[10; 89)
12 L_CURLY@[10; 11) "{"
13 WHITESPACE@[11; 16) "\n "
14 LET_STMT@[16; 27)
15 LET_KW@[16; 19) "let"
16 WHITESPACE@[19; 20) " "
17 BIND_PAT@[20; 27)
18 REF_KW@[20; 23) "ref"
19 WHITESPACE@[23; 24) " "
20 ERROR@[24; 27)
21 BOX_KW@[24; 27) "box"
22 WHITESPACE@[27; 28) " "
23 EXPR_STMT@[28; 35)
24 BIN_EXPR@[28; 34)
25 PATH_EXPR@[28; 29)
26 PATH@[28; 29)
27 PATH_SEGMENT@[28; 29)
28 NAME_REF@[28; 29)
29 IDENT@[28; 29) "i"
30 WHITESPACE@[29; 30) " "
31 EQ@[30; 31) "="
32 WHITESPACE@[31; 32) " "
33 TUPLE_EXPR@[32; 34)
34 L_PAREN@[32; 33) "("
35 R_PAREN@[33; 34) ")"
36 SEMI@[34; 35) ";"
37 WHITESPACE@[35; 40) "\n "
38 LET_STMT@[40; 51)
39 LET_KW@[40; 43) "let"
40 WHITESPACE@[43; 44) " "
41 BIND_PAT@[44; 51)
42 MUT_KW@[44; 47) "mut"
43 WHITESPACE@[47; 48) " "
44 ERROR@[48; 51)
45 BOX_KW@[48; 51) "box"
46 WHITESPACE@[51; 52) " "
47 EXPR_STMT@[52; 59)
48 BIN_EXPR@[52; 58)
49 PATH_EXPR@[52; 53)
50 PATH@[52; 53)
51 PATH_SEGMENT@[52; 53)
52 NAME_REF@[52; 53)
53 IDENT@[52; 53) "i"
54 WHITESPACE@[53; 54) " "
55 EQ@[54; 55) "="
56 WHITESPACE@[55; 56) " "
57 TUPLE_EXPR@[56; 58)
58 L_PAREN@[56; 57) "("
59 R_PAREN@[57; 58) ")"
60 SEMI@[58; 59) ";"
61 WHITESPACE@[59; 64) "\n "
62 LET_STMT@[64; 79)
63 LET_KW@[64; 67) "let"
64 WHITESPACE@[67; 68) " "
65 BIND_PAT@[68; 79)
66 REF_KW@[68; 71) "ref"
67 WHITESPACE@[71; 72) " "
68 MUT_KW@[72; 75) "mut"
69 WHITESPACE@[75; 76) " "
70 ERROR@[76; 79)
71 BOX_KW@[76; 79) "box"
72 WHITESPACE@[79; 80) " "
73 EXPR_STMT@[80; 87)
74 BIN_EXPR@[80; 86)
75 PATH_EXPR@[80; 81)
76 PATH@[80; 81)
77 PATH_SEGMENT@[80; 81)
78 NAME_REF@[80; 81)
79 IDENT@[80; 81) "i"
80 WHITESPACE@[81; 82) " "
81 EQ@[82; 83) "="
82 WHITESPACE@[83; 84) " "
83 TUPLE_EXPR@[84; 86)
84 L_PAREN@[84; 85) "("
85 R_PAREN@[85; 86) ")"
86 SEMI@[86; 87) ";"
87 WHITESPACE@[87; 88) "\n"
88 R_CURLY@[88; 89) "}"
89 WHITESPACE@[89; 91) "\n\n"
90error 24: expected a name
91error 27: expected SEMI
92error 48: expected a name
93error 51: expected SEMI
94error 76: expected a name
95error 79: expected SEMI
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0066_match_arm.rs b/crates/ra_syntax/test_data/parser/inline/ok/0066_match_arm.rs
index 2d476278d..9e009e24f 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0066_match_arm.rs
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0066_match_arm.rs
@@ -5,7 +5,5 @@ fn foo() {
5 X | Y if Z => (), 5 X | Y if Z => (),
6 | X | Y if Z => (), 6 | X | Y if Z => (),
7 | X => (), 7 | X => (),
8 box X => (),
9 Some(box X) => (),
10 }; 8 };
11} 9}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0066_match_arm.txt b/crates/ra_syntax/test_data/parser/inline/ok/0066_match_arm.txt
index 35f023782..c2f256ac3 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0066_match_arm.txt
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0066_match_arm.txt
@@ -1,5 +1,5 @@
1SOURCE_FILE@[0; 215) 1SOURCE_FILE@[0; 167)
2 FN_DEF@[0; 214) 2 FN_DEF@[0; 166)
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,18 +8,18 @@ SOURCE_FILE@[0; 215)
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; 214) 11 BLOCK@[9; 166)
12 L_CURLY@[9; 10) "{" 12 L_CURLY@[9; 10) "{"
13 WHITESPACE@[10; 15) "\n " 13 WHITESPACE@[10; 15) "\n "
14 EXPR_STMT@[15; 212) 14 EXPR_STMT@[15; 164)
15 MATCH_EXPR@[15; 211) 15 MATCH_EXPR@[15; 163)
16 MATCH_KW@[15; 20) "match" 16 MATCH_KW@[15; 20) "match"
17 WHITESPACE@[20; 21) " " 17 WHITESPACE@[20; 21) " "
18 TUPLE_EXPR@[21; 23) 18 TUPLE_EXPR@[21; 23)
19 L_PAREN@[21; 22) "(" 19 L_PAREN@[21; 22) "("
20 R_PAREN@[22; 23) ")" 20 R_PAREN@[22; 23) ")"
21 WHITESPACE@[23; 24) " " 21 WHITESPACE@[23; 24) " "
22 MATCH_ARM_LIST@[24; 211) 22 MATCH_ARM_LIST@[24; 163)
23 L_CURLY@[24; 25) "{" 23 L_CURLY@[24; 25) "{"
24 WHITESPACE@[25; 34) "\n " 24 WHITESPACE@[25; 34) "\n "
25 MATCH_ARM@[34; 41) 25 MATCH_ARM@[34; 41)
@@ -141,44 +141,9 @@ SOURCE_FILE@[0; 215)
141 L_PAREN@[154; 155) "(" 141 L_PAREN@[154; 155) "("
142 R_PAREN@[155; 156) ")" 142 R_PAREN@[155; 156) ")"
143 COMMA@[156; 157) "," 143 COMMA@[156; 157) ","
144 WHITESPACE@[157; 166) "\n " 144 WHITESPACE@[157; 162) "\n "
145 MATCH_ARM@[166; 177) 145 R_CURLY@[162; 163) "}"
146 BIND_PAT@[166; 171) 146 SEMI@[163; 164) ";"
147 BOX_KW@[166; 169) "box" 147 WHITESPACE@[164; 165) "\n"
148 WHITESPACE@[169; 170) " " 148 R_CURLY@[165; 166) "}"
149 NAME@[170; 171) 149 WHITESPACE@[166; 167) "\n"
150 IDENT@[170; 171) "X"
151 WHITESPACE@[171; 172) " "
152 FAT_ARROW@[172; 174) "=>"
153 WHITESPACE@[174; 175) " "
154 TUPLE_EXPR@[175; 177)
155 L_PAREN@[175; 176) "("
156 R_PAREN@[176; 177) ")"
157 COMMA@[177; 178) ","
158 WHITESPACE@[178; 187) "\n "
159 MATCH_ARM@[187; 204)
160 TUPLE_STRUCT_PAT@[187; 198)
161 PATH@[187; 191)
162 PATH_SEGMENT@[187; 191)
163 NAME_REF@[187; 191)
164 IDENT@[187; 191) "Some"
165 L_PAREN@[191; 192) "("
166 BIND_PAT@[192; 197)
167 BOX_KW@[192; 195) "box"
168 WHITESPACE@[195; 196) " "
169 NAME@[196; 197)
170 IDENT@[196; 197) "X"
171 R_PAREN@[197; 198) ")"
172 WHITESPACE@[198; 199) " "
173 FAT_ARROW@[199; 201) "=>"
174 WHITESPACE@[201; 202) " "
175 TUPLE_EXPR@[202; 204)
176 L_PAREN@[202; 203) "("
177 R_PAREN@[203; 204) ")"
178 COMMA@[204; 205) ","
179 WHITESPACE@[205; 210) "\n "
180 R_CURLY@[210; 211) "}"
181 SEMI@[211; 212) ";"
182 WHITESPACE@[212; 213) "\n"
183 R_CURLY@[213; 214) "}"
184 WHITESPACE@[214; 215) "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0112_bind_pat.rs b/crates/ra_syntax/test_data/parser/inline/ok/0112_bind_pat.rs
index 5a93469af..820a9e72c 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0112_bind_pat.rs
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0112_bind_pat.rs
@@ -5,5 +5,4 @@ fn main() {
5 let ref mut d = (); 5 let ref mut d = ();
6 let e @ _ = (); 6 let e @ _ = ();
7 let ref mut f @ g @ _ = (); 7 let ref mut f @ g @ _ = ();
8 let box i = Box::new(1i32);
9} 8}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0112_bind_pat.txt b/crates/ra_syntax/test_data/parser/inline/ok/0112_bind_pat.txt
index 189254a19..ab0f88507 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0112_bind_pat.txt
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0112_bind_pat.txt
@@ -1,5 +1,5 @@
1SOURCE_FILE@[0; 178) 1SOURCE_FILE@[0; 146)
2 FN_DEF@[0; 177) 2 FN_DEF@[0; 145)
3 FN_KW@[0; 2) "fn" 3 FN_KW@[0; 2) "fn"
4 WHITESPACE@[2; 3) " " 4 WHITESPACE@[2; 3) " "
5 NAME@[3; 7) 5 NAME@[3; 7)
@@ -8,7 +8,7 @@ SOURCE_FILE@[0; 178)
8 L_PAREN@[7; 8) "(" 8 L_PAREN@[7; 8) "("
9 R_PAREN@[8; 9) ")" 9 R_PAREN@[8; 9) ")"
10 WHITESPACE@[9; 10) " " 10 WHITESPACE@[9; 10) " "
11 BLOCK@[10; 177) 11 BLOCK@[10; 145)
12 L_CURLY@[10; 11) "{" 12 L_CURLY@[10; 11) "{"
13 WHITESPACE@[11; 16) "\n " 13 WHITESPACE@[11; 16) "\n "
14 LET_STMT@[16; 27) 14 LET_STMT@[16; 27)
@@ -122,35 +122,6 @@ SOURCE_FILE@[0; 178)
122 L_PAREN@[140; 141) "(" 122 L_PAREN@[140; 141) "("
123 R_PAREN@[141; 142) ")" 123 R_PAREN@[141; 142) ")"
124 SEMI@[142; 143) ";" 124 SEMI@[142; 143) ";"
125 WHITESPACE@[143; 148) "\n " 125 WHITESPACE@[143; 144) "\n"
126 LET_STMT@[148; 175) 126 R_CURLY@[144; 145) "}"
127 LET_KW@[148; 151) "let" 127 WHITESPACE@[145; 146) "\n"
128 WHITESPACE@[151; 152) " "
129 BIND_PAT@[152; 157)
130 BOX_KW@[152; 155) "box"
131 WHITESPACE@[155; 156) " "
132 NAME@[156; 157)
133 IDENT@[156; 157) "i"
134 WHITESPACE@[157; 158) " "
135 EQ@[158; 159) "="
136 WHITESPACE@[159; 160) " "
137 CALL_EXPR@[160; 174)
138 PATH_EXPR@[160; 168)
139 PATH@[160; 168)
140 PATH@[160; 163)
141 PATH_SEGMENT@[160; 163)
142 NAME_REF@[160; 163)
143 IDENT@[160; 163) "Box"
144 COLONCOLON@[163; 165) "::"
145 PATH_SEGMENT@[165; 168)
146 NAME_REF@[165; 168)
147 IDENT@[165; 168) "new"
148 ARG_LIST@[168; 174)
149 L_PAREN@[168; 169) "("
150 LITERAL@[169; 173)
151 INT_NUMBER@[169; 173) "1i32"
152 R_PAREN@[173; 174) ")"
153 SEMI@[174; 175) ";"
154 WHITESPACE@[175; 176) "\n"
155 R_CURLY@[176; 177) "}"
156 WHITESPACE@[177; 178) "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.rs b/crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.rs
new file mode 100644
index 000000000..9d458aa1e
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.rs
@@ -0,0 +1,5 @@
1fn main() {
2 let box i = ();
3 let box Outer { box i, j: box Inner(box &x) } = ();
4 let box ref mut i = ();
5}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.txt b/crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.txt
new file mode 100644
index 000000000..f0db58143
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.txt
@@ -0,0 +1,109 @@
1SOURCE_FILE@[0; 118)
2 FN_DEF@[0; 117)
3 FN_KW@[0; 2) "fn"
4 WHITESPACE@[2; 3) " "
5 NAME@[3; 7)
6 IDENT@[3; 7) "main"
7 PARAM_LIST@[7; 9)
8 L_PAREN@[7; 8) "("
9 R_PAREN@[8; 9) ")"
10 WHITESPACE@[9; 10) " "
11 BLOCK@[10; 117)
12 L_CURLY@[10; 11) "{"
13 WHITESPACE@[11; 16) "\n "
14 LET_STMT@[16; 31)
15 LET_KW@[16; 19) "let"
16 WHITESPACE@[19; 20) " "
17 BOX_PAT@[20; 25)
18 BOX_KW@[20; 23) "box"
19 WHITESPACE@[23; 24) " "
20 BIND_PAT@[24; 25)
21 NAME@[24; 25)
22 IDENT@[24; 25) "i"
23 WHITESPACE@[25; 26) " "
24 EQ@[26; 27) "="
25 WHITESPACE@[27; 28) " "
26 TUPLE_EXPR@[28; 30)
27 L_PAREN@[28; 29) "("
28 R_PAREN@[29; 30) ")"
29 SEMI@[30; 31) ";"
30 WHITESPACE@[31; 36) "\n "
31 LET_STMT@[36; 87)
32 LET_KW@[36; 39) "let"
33 WHITESPACE@[39; 40) " "
34 BOX_PAT@[40; 81)
35 BOX_KW@[40; 43) "box"
36 WHITESPACE@[43; 44) " "
37 RECORD_PAT@[44; 81)
38 PATH@[44; 49)
39 PATH_SEGMENT@[44; 49)
40 NAME_REF@[44; 49)
41 IDENT@[44; 49) "Outer"
42 WHITESPACE@[49; 50) " "
43 RECORD_FIELD_PAT_LIST@[50; 81)
44 L_CURLY@[50; 51) "{"
45 WHITESPACE@[51; 52) " "
46 BOX_PAT@[52; 57)
47 BOX_KW@[52; 55) "box"
48 WHITESPACE@[55; 56) " "
49 BIND_PAT@[56; 57)
50 NAME@[56; 57)
51 IDENT@[56; 57) "i"
52 COMMA@[57; 58) ","
53 WHITESPACE@[58; 59) " "
54 RECORD_FIELD_PAT@[59; 79)
55 NAME@[59; 60)
56 IDENT@[59; 60) "j"
57 COLON@[60; 61) ":"
58 WHITESPACE@[61; 62) " "
59 BOX_PAT@[62; 79)
60 BOX_KW@[62; 65) "box"
61 WHITESPACE@[65; 66) " "
62 TUPLE_STRUCT_PAT@[66; 79)
63 PATH@[66; 71)
64 PATH_SEGMENT@[66; 71)
65 NAME_REF@[66; 71)
66 IDENT@[66; 71) "Inner"
67 L_PAREN@[71; 72) "("
68 BOX_PAT@[72; 78)
69 BOX_KW@[72; 75) "box"
70 WHITESPACE@[75; 76) " "
71 REF_PAT@[76; 78)
72 AMP@[76; 77) "&"
73 BIND_PAT@[77; 78)
74 NAME@[77; 78)
75 IDENT@[77; 78) "x"
76 R_PAREN@[78; 79) ")"
77 WHITESPACE@[79; 80) " "
78 R_CURLY@[80; 81) "}"
79 WHITESPACE@[81; 82) " "
80 EQ@[82; 83) "="
81 WHITESPACE@[83; 84) " "
82 TUPLE_EXPR@[84; 86)
83 L_PAREN@[84; 85) "("
84 R_PAREN@[85; 86) ")"
85 SEMI@[86; 87) ";"
86 WHITESPACE@[87; 92) "\n "
87 LET_STMT@[92; 115)
88 LET_KW@[92; 95) "let"
89 WHITESPACE@[95; 96) " "
90 BOX_PAT@[96; 109)
91 BOX_KW@[96; 99) "box"
92 WHITESPACE@[99; 100) " "
93 BIND_PAT@[100; 109)
94 REF_KW@[100; 103) "ref"
95 WHITESPACE@[103; 104) " "
96 MUT_KW@[104; 107) "mut"
97 WHITESPACE@[107; 108) " "
98 NAME@[108; 109)
99 IDENT@[108; 109) "i"
100 WHITESPACE@[109; 110) " "
101 EQ@[110; 111) "="
102 WHITESPACE@[111; 112) " "
103 TUPLE_EXPR@[112; 114)
104 L_PAREN@[112; 113) "("
105 R_PAREN@[113; 114) ")"
106 SEMI@[114; 115) ";"
107 WHITESPACE@[115; 116) "\n"
108 R_CURLY@[116; 117) "}"
109 WHITESPACE@[117; 118) "\n"