aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_analysis/src/db.rs2
-rw-r--r--crates/ra_db/src/syntax_ptr.rs2
-rw-r--r--crates/ra_hir/src/db.rs10
-rw-r--r--crates/ra_hir/src/expr.rs507
-rw-r--r--crates/ra_hir/src/lib.rs1
-rw-r--r--crates/ra_hir/src/mock.rs2
-rw-r--r--crates/ra_hir/src/path.rs8
-rw-r--r--crates/ra_syntax/src/ast/generated.rs10
-rw-r--r--crates/ra_syntax/src/grammar.ron4
9 files changed, 542 insertions, 4 deletions
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs
index 5422a400b..074a7a7f6 100644
--- a/crates/ra_analysis/src/db.rs
+++ b/crates/ra_analysis/src/db.rs
@@ -106,6 +106,8 @@ salsa::database_storage! {
106 fn struct_data() for hir::db::StructDataQuery; 106 fn struct_data() for hir::db::StructDataQuery;
107 fn enum_data() for hir::db::EnumDataQuery; 107 fn enum_data() for hir::db::EnumDataQuery;
108 fn impls_in_module() for hir::db::ImplsInModuleQuery; 108 fn impls_in_module() for hir::db::ImplsInModuleQuery;
109 fn body_hir() for hir::db::BodyHirQuery;
110 fn body_syntax_mapping() for hir::db::BodySyntaxMappingQuery;
109 } 111 }
110 } 112 }
111} 113}
diff --git a/crates/ra_db/src/syntax_ptr.rs b/crates/ra_db/src/syntax_ptr.rs
index 744cb2352..5bfcedf2b 100644
--- a/crates/ra_db/src/syntax_ptr.rs
+++ b/crates/ra_db/src/syntax_ptr.rs
@@ -1,6 +1,6 @@
1use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange}; 1use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange};
2 2
3/// A pionter to a syntax node inside a file. 3/// A pointer to a syntax node inside a file.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub struct LocalSyntaxPtr { 5pub struct LocalSyntaxPtr {
6 range: TextRange, 6 range: TextRange,
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index 58296fc6f..188b96872 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -93,6 +93,16 @@ pub trait HirDatabase: SyntaxDatabase
93 type ImplsInModuleQuery; 93 type ImplsInModuleQuery;
94 use fn crate::impl_block::impls_in_module; 94 use fn crate::impl_block::impls_in_module;
95 } 95 }
96
97 fn body_hir(def_id: DefId) -> Cancelable<Arc<crate::expr::Body>> {
98 type BodyHirQuery;
99 use fn crate::expr::body_hir;
100 }
101
102 fn body_syntax_mapping(def_id: DefId) -> Cancelable<Arc<crate::expr::BodySyntaxMapping>> {
103 type BodySyntaxMappingQuery;
104 use fn crate::expr::body_syntax_mapping;
105 }
96} 106}
97 107
98} 108}
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
new file mode 100644
index 000000000..5cdcca082
--- /dev/null
+++ b/crates/ra_hir/src/expr.rs
@@ -0,0 +1,507 @@
1use std::sync::Arc;
2
3use rustc_hash::FxHashMap;
4
5use ra_arena::{Arena, RawId, impl_arena_id};
6use ra_db::{LocalSyntaxPtr, Cancelable};
7use ra_syntax::ast::{self, AstNode, LoopBodyOwner, ArgListOwner};
8
9use crate::{Path, type_ref::{Mutability, TypeRef}, Name, HirDatabase, DefId, Def, name::AsName};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct ExprId(RawId);
13impl_arena_id!(ExprId);
14
15/// The body of an item (function, const etc.).
16#[derive(Debug, Eq, PartialEq)]
17pub struct Body {
18 exprs: Arena<ExprId, Expr>,
19 pats: Arena<PatId, Pat>,
20 /// The patterns for the function's arguments. While the argument types are
21 /// part of the function signature, the patterns are not (they don't change
22 /// the external type of the function).
23 ///
24 /// If this `ExprTable` is for the body of a constant, this will just be
25 /// empty.
26 args: Vec<PatId>,
27 /// The `ExprId` of the actual body expression.
28 body_expr: ExprId,
29}
30
31/// An item body together with the mapping from syntax nodes to HIR expression
32/// IDs. This is needed to go from e.g. a position in a file to the HIR
33/// expression containing it; but for type inference etc., we want to operate on
34/// a structure that is agnostic to the actual positions of expressions in the
35/// file, so that we don't recompute the type inference whenever some whitespace
36/// is typed.
37#[derive(Debug, Eq, PartialEq)]
38pub struct BodySyntaxMapping {
39 body: Arc<Body>,
40 expr_syntax_mapping: FxHashMap<LocalSyntaxPtr, ExprId>,
41 expr_syntax_mapping_back: FxHashMap<ExprId, LocalSyntaxPtr>,
42 pat_syntax_mapping: FxHashMap<LocalSyntaxPtr, PatId>,
43 pat_syntax_mapping_back: FxHashMap<PatId, LocalSyntaxPtr>,
44}
45
46#[derive(Debug, Clone, Eq, PartialEq)]
47pub enum Expr {
48 /// This is produced if syntax tree does not have a required expression piece.
49 Missing,
50 Path(Path),
51 If {
52 condition: ExprId,
53 then_branch: ExprId,
54 else_branch: Option<ExprId>,
55 },
56 Block {
57 statements: Vec<Statement>,
58 tail: Option<ExprId>,
59 },
60 Loop {
61 body: ExprId,
62 },
63 While {
64 condition: ExprId,
65 body: ExprId,
66 },
67 For {
68 iterable: ExprId,
69 pat: PatId,
70 body: ExprId,
71 },
72 Call {
73 callee: ExprId,
74 args: Vec<ExprId>,
75 },
76 MethodCall {
77 receiver: ExprId,
78 method_name: Name,
79 args: Vec<ExprId>,
80 },
81 Match {
82 expr: ExprId,
83 arms: Vec<MatchArm>,
84 },
85 Continue,
86 Break {
87 expr: Option<ExprId>,
88 },
89 Return {
90 expr: Option<ExprId>,
91 },
92 StructLit {
93 path: Option<Path>,
94 fields: Vec<StructLitField>,
95 spread: Option<ExprId>,
96 },
97 Field {
98 expr: ExprId,
99 name: Name,
100 },
101 Try {
102 expr: ExprId,
103 },
104 Cast {
105 expr: ExprId,
106 type_ref: TypeRef,
107 },
108 Ref {
109 expr: ExprId,
110 mutability: Mutability,
111 },
112 UnaryOp {
113 expr: ExprId,
114 op: Option<UnaryOp>,
115 },
116}
117
118pub type UnaryOp = ast::PrefixOp;
119
120#[derive(Debug, Clone, Eq, PartialEq)]
121pub struct MatchArm {
122 pats: Vec<PatId>,
123 // guard: Option<ExprId>, // TODO
124 expr: ExprId,
125}
126
127#[derive(Debug, Clone, Eq, PartialEq)]
128pub struct StructLitField {
129 name: Name,
130 expr: ExprId,
131}
132
133#[derive(Debug, Clone, Eq, PartialEq)]
134pub enum Statement {
135 Let {
136 pat: PatId,
137 type_ref: Option<TypeRef>,
138 initializer: Option<ExprId>,
139 },
140 Expr(ExprId),
141}
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
144pub struct PatId(RawId);
145impl_arena_id!(PatId);
146
147#[derive(Debug, Clone, Eq, PartialEq)]
148pub struct Pat;
149
150// Queries
151
152pub(crate) fn body_hir(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<Body>> {
153 Ok(Arc::clone(&body_syntax_mapping(db, def_id)?.body))
154}
155
156struct ExprCollector {
157 exprs: Arena<ExprId, Expr>,
158 pats: Arena<PatId, Pat>,
159 expr_syntax_mapping: FxHashMap<LocalSyntaxPtr, ExprId>,
160 expr_syntax_mapping_back: FxHashMap<ExprId, LocalSyntaxPtr>,
161 pat_syntax_mapping: FxHashMap<LocalSyntaxPtr, PatId>,
162 pat_syntax_mapping_back: FxHashMap<PatId, LocalSyntaxPtr>,
163}
164
165impl ExprCollector {
166 fn alloc_expr(&mut self, expr: Expr, syntax_ptr: LocalSyntaxPtr) -> ExprId {
167 let id = self.exprs.alloc(expr);
168 self.expr_syntax_mapping.insert(syntax_ptr, id);
169 self.expr_syntax_mapping_back.insert(id, syntax_ptr);
170 id
171 }
172
173 fn alloc_pat(&mut self, pat: Pat, syntax_ptr: LocalSyntaxPtr) -> PatId {
174 let id = self.pats.alloc(pat);
175 self.pat_syntax_mapping.insert(syntax_ptr, id);
176 self.pat_syntax_mapping_back.insert(id, syntax_ptr);
177 id
178 }
179
180 fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
181 let syntax_ptr = LocalSyntaxPtr::new(expr.syntax());
182 match expr {
183 ast::Expr::IfExpr(e) => {
184 let condition = if let Some(condition) = e.condition() {
185 if condition.pat().is_none() {
186 self.collect_expr_opt(condition.expr())
187 } else {
188 // TODO handle if let
189 return self.alloc_expr(Expr::Missing, syntax_ptr);
190 }
191 } else {
192 self.exprs.alloc(Expr::Missing)
193 };
194 let then_branch = self.collect_block_opt(e.then_branch());
195 let else_branch = e.else_branch().map(|e| self.collect_block(e));
196 self.alloc_expr(
197 Expr::If {
198 condition,
199 then_branch,
200 else_branch,
201 },
202 syntax_ptr,
203 )
204 }
205 ast::Expr::BlockExpr(e) => self.collect_block_opt(e.block()),
206 ast::Expr::LoopExpr(e) => {
207 let body = self.collect_block_opt(e.loop_body());
208 self.alloc_expr(Expr::Loop { body }, syntax_ptr)
209 }
210 ast::Expr::WhileExpr(e) => {
211 let condition = if let Some(condition) = e.condition() {
212 if condition.pat().is_none() {
213 self.collect_expr_opt(condition.expr())
214 } else {
215 // TODO handle while let
216 return self.alloc_expr(Expr::Missing, syntax_ptr);
217 }
218 } else {
219 self.exprs.alloc(Expr::Missing)
220 };
221 let body = self.collect_block_opt(e.loop_body());
222 self.alloc_expr(Expr::While { condition, body }, syntax_ptr)
223 }
224 ast::Expr::ForExpr(e) => {
225 let iterable = self.collect_expr_opt(e.iterable());
226 let pat = self.collect_pat_opt(e.pat());
227 let body = self.collect_block_opt(e.loop_body());
228 self.alloc_expr(
229 Expr::For {
230 iterable,
231 pat,
232 body,
233 },
234 syntax_ptr,
235 )
236 }
237 ast::Expr::CallExpr(e) => {
238 let callee = self.collect_expr_opt(e.expr());
239 let args = if let Some(arg_list) = e.arg_list() {
240 arg_list.args().map(|e| self.collect_expr(e)).collect()
241 } else {
242 Vec::new()
243 };
244 self.alloc_expr(Expr::Call { callee, args }, syntax_ptr)
245 }
246 ast::Expr::MethodCallExpr(e) => {
247 let receiver = self.collect_expr_opt(e.expr());
248 let args = if let Some(arg_list) = e.arg_list() {
249 arg_list.args().map(|e| self.collect_expr(e)).collect()
250 } else {
251 Vec::new()
252 };
253 let method_name = e
254 .name_ref()
255 .map(|nr| nr.as_name())
256 .unwrap_or_else(Name::missing);
257 self.alloc_expr(
258 Expr::MethodCall {
259 receiver,
260 method_name,
261 args,
262 },
263 syntax_ptr,
264 )
265 }
266 ast::Expr::MatchExpr(e) => {
267 let expr = self.collect_expr_opt(e.expr());
268 let arms = if let Some(match_arm_list) = e.match_arm_list() {
269 match_arm_list
270 .arms()
271 .map(|arm| MatchArm {
272 pats: arm.pats().map(|p| self.collect_pat(p)).collect(),
273 expr: self.collect_expr_opt(arm.expr()),
274 })
275 .collect()
276 } else {
277 Vec::new()
278 };
279 self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
280 }
281 ast::Expr::PathExpr(e) => {
282 let path = e
283 .path()
284 .and_then(Path::from_ast)
285 .map(Expr::Path)
286 .unwrap_or(Expr::Missing);
287 self.alloc_expr(path, syntax_ptr)
288 }
289 ast::Expr::ContinueExpr(_e) => {
290 // TODO: labels
291 self.alloc_expr(Expr::Continue, syntax_ptr)
292 }
293 ast::Expr::BreakExpr(e) => {
294 let expr = e.expr().map(|e| self.collect_expr(e));
295 self.alloc_expr(Expr::Break { expr }, syntax_ptr)
296 }
297 ast::Expr::ParenExpr(e) => {
298 let inner = self.collect_expr_opt(e.expr());
299 // make the paren expr point to the inner expression as well
300 self.expr_syntax_mapping.insert(syntax_ptr, inner);
301 inner
302 }
303 ast::Expr::ReturnExpr(e) => {
304 let expr = e.expr().map(|e| self.collect_expr(e));
305 self.alloc_expr(Expr::Return { expr }, syntax_ptr)
306 }
307 ast::Expr::StructLit(e) => {
308 let path = e.path().and_then(Path::from_ast);
309 let fields = if let Some(nfl) = e.named_field_list() {
310 nfl.fields()
311 .map(|field| StructLitField {
312 name: field
313 .name_ref()
314 .map(|nr| nr.as_name())
315 .unwrap_or_else(Name::missing),
316 expr: if let Some(e) = field.expr() {
317 self.collect_expr(e)
318 } else if let Some(nr) = field.name_ref() {
319 // field shorthand
320 let id = self.exprs.alloc(Expr::Path(Path::from_name_ref(nr)));
321 self.expr_syntax_mapping
322 .insert(LocalSyntaxPtr::new(nr.syntax()), id);
323 self.expr_syntax_mapping_back
324 .insert(id, LocalSyntaxPtr::new(nr.syntax()));
325 id
326 } else {
327 self.exprs.alloc(Expr::Missing)
328 },
329 })
330 .collect()
331 } else {
332 Vec::new()
333 };
334 let spread = e.spread().map(|s| self.collect_expr(s));
335 self.alloc_expr(
336 Expr::StructLit {
337 path,
338 fields,
339 spread,
340 },
341 syntax_ptr,
342 )
343 }
344 ast::Expr::FieldExpr(e) => {
345 let expr = self.collect_expr_opt(e.expr());
346 let name = e
347 .name_ref()
348 .map(|nr| nr.as_name())
349 .unwrap_or_else(Name::missing);
350 self.alloc_expr(Expr::Field { expr, name }, syntax_ptr)
351 }
352 ast::Expr::TryExpr(e) => {
353 let expr = self.collect_expr_opt(e.expr());
354 self.alloc_expr(Expr::Try { expr }, syntax_ptr)
355 }
356 ast::Expr::CastExpr(e) => {
357 let expr = self.collect_expr_opt(e.expr());
358 let type_ref = TypeRef::from_ast_opt(e.type_ref());
359 self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr)
360 }
361 ast::Expr::RefExpr(e) => {
362 let expr = self.collect_expr_opt(e.expr());
363 let mutability = Mutability::from_mutable(e.is_mut());
364 self.alloc_expr(Expr::Ref { expr, mutability }, syntax_ptr)
365 }
366 ast::Expr::PrefixExpr(e) => {
367 let expr = self.collect_expr_opt(e.expr());
368 let op = e.op();
369 self.alloc_expr(Expr::UnaryOp { expr, op }, syntax_ptr)
370 }
371
372 // We should never get to these because they're handled in MatchExpr resp. StructLit:
373 ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => {
374 panic!("collect_expr called on {:?}", expr)
375 }
376 ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => {
377 panic!("collect_expr called on {:?}", expr)
378 }
379
380 // TODO implement HIR for these:
381 ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
382 ast::Expr::LambdaExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
383 ast::Expr::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
384 ast::Expr::TupleExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
385 ast::Expr::ArrayExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
386 ast::Expr::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
387 ast::Expr::BinExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
388 ast::Expr::Literal(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
389 }
390 }
391
392 fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
393 if let Some(expr) = expr {
394 self.collect_expr(expr)
395 } else {
396 self.exprs.alloc(Expr::Missing)
397 }
398 }
399
400 fn collect_block(&mut self, block: ast::Block) -> ExprId {
401 let statements = block
402 .statements()
403 .map(|s| match s {
404 ast::Stmt::LetStmt(stmt) => {
405 let pat = self.collect_pat_opt(stmt.pat());
406 let type_ref = stmt.type_ref().map(TypeRef::from_ast);
407 let initializer = stmt.initializer().map(|e| self.collect_expr(e));
408 Statement::Let {
409 pat,
410 type_ref,
411 initializer,
412 }
413 }
414 ast::Stmt::ExprStmt(stmt) => Statement::Expr(self.collect_expr_opt(stmt.expr())),
415 })
416 .collect();
417 let tail = block.expr().map(|e| self.collect_expr(e));
418 self.alloc_expr(
419 Expr::Block { statements, tail },
420 LocalSyntaxPtr::new(block.syntax()),
421 )
422 }
423
424 fn collect_block_opt(&mut self, block: Option<ast::Block>) -> ExprId {
425 if let Some(block) = block {
426 self.collect_block(block)
427 } else {
428 self.exprs.alloc(Expr::Missing)
429 }
430 }
431
432 fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
433 let syntax_ptr = LocalSyntaxPtr::new(pat.syntax());
434 // TODO
435 self.alloc_pat(Pat, syntax_ptr)
436 }
437
438 fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
439 if let Some(pat) = pat {
440 self.collect_pat(pat)
441 } else {
442 // TODO
443 self.pats.alloc(Pat)
444 }
445 }
446
447 fn into_body_syntax_mapping(self, args: Vec<PatId>, body_expr: ExprId) -> BodySyntaxMapping {
448 let body = Body {
449 exprs: self.exprs,
450 pats: self.pats,
451 args,
452 body_expr,
453 };
454 BodySyntaxMapping {
455 body: Arc::new(body),
456 expr_syntax_mapping: self.expr_syntax_mapping,
457 expr_syntax_mapping_back: self.expr_syntax_mapping_back,
458 pat_syntax_mapping: self.pat_syntax_mapping,
459 pat_syntax_mapping_back: self.pat_syntax_mapping_back,
460 }
461 }
462}
463
464pub(crate) fn body_syntax_mapping(
465 db: &impl HirDatabase,
466 def_id: DefId,
467) -> Cancelable<Arc<BodySyntaxMapping>> {
468 let def = def_id.resolve(db)?;
469 let mut collector = ExprCollector {
470 exprs: Arena::default(),
471 pats: Arena::default(),
472 expr_syntax_mapping: FxHashMap::default(),
473 expr_syntax_mapping_back: FxHashMap::default(),
474 pat_syntax_mapping: FxHashMap::default(),
475 pat_syntax_mapping_back: FxHashMap::default(),
476 };
477
478 let (body, args) = match def {
479 Def::Function(f) => {
480 let node = f.syntax(db);
481 let node = node.borrowed();
482
483 let args = if let Some(param_list) = node.param_list() {
484 let mut args = Vec::new();
485 // TODO self param
486 for param in param_list.params() {
487 let pat = if let Some(pat) = param.pat() {
488 pat
489 } else {
490 continue;
491 };
492 args.push(collector.collect_pat(pat));
493 }
494 args
495 } else {
496 Vec::new()
497 };
498
499 let body = collector.collect_block_opt(node.body());
500 (body, args)
501 }
502 // TODO: consts, etc.
503 _ => panic!("Trying to get body for item type without body"),
504 };
505
506 Ok(Arc::new(collector.into_body_syntax_mapping(args, body)))
507}
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 2abcec441..fea9e141b 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -32,6 +32,7 @@ mod adt;
32mod type_ref; 32mod type_ref;
33mod ty; 33mod ty;
34mod impl_block; 34mod impl_block;
35mod expr;
35 36
36use crate::{ 37use crate::{
37 db::HirDatabase, 38 db::HirDatabase,
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
index a9db932ff..661a5a26b 100644
--- a/crates/ra_hir/src/mock.rs
+++ b/crates/ra_hir/src/mock.rs
@@ -208,6 +208,8 @@ salsa::database_storage! {
208 fn struct_data() for db::StructDataQuery; 208 fn struct_data() for db::StructDataQuery;
209 fn enum_data() for db::EnumDataQuery; 209 fn enum_data() for db::EnumDataQuery;
210 fn impls_in_module() for db::ImplsInModuleQuery; 210 fn impls_in_module() for db::ImplsInModuleQuery;
211 fn body_hir() for db::BodyHirQuery;
212 fn body_syntax_mapping() for db::BodySyntaxMappingQuery;
211 } 213 }
212 } 214 }
213} 215}
diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs
index 9fdfa0d13..2e42caffe 100644
--- a/crates/ra_hir/src/path.rs
+++ b/crates/ra_hir/src/path.rs
@@ -65,6 +65,14 @@ impl Path {
65 } 65 }
66 } 66 }
67 67
68 /// Converts an `ast::NameRef` into a single-identifier `Path`.
69 pub fn from_name_ref(name_ref: ast::NameRef) -> Path {
70 Path {
71 kind: PathKind::Plain,
72 segments: vec![name_ref.as_name()],
73 }
74 }
75
68 /// `true` is this path is a single identifier, like `foo` 76 /// `true` is this path is a single identifier, like `foo`
69 pub fn is_ident(&self) -> bool { 77 pub fn is_ident(&self) -> bool {
70 self.kind == PathKind::Plain && self.segments.len() == 1 78 self.kind == PathKind::Plain && self.segments.len() == 1
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index 7df6a9c46..deb4dea88 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -378,7 +378,11 @@ impl<R: TreeRoot<RaTypes>> BreakExprNode<R> {
378} 378}
379 379
380 380
381impl<'a> BreakExpr<'a> {} 381impl<'a> BreakExpr<'a> {
382 pub fn expr(self) -> Option<Expr<'a>> {
383 super::child_opt(self)
384 }
385}
382 386
383// Byte 387// Byte
384#[derive(Debug, Clone, Copy,)] 388#[derive(Debug, Clone, Copy,)]
@@ -3880,6 +3884,10 @@ impl<'a> StructLit<'a> {
3880 pub fn named_field_list(self) -> Option<NamedFieldList<'a>> { 3884 pub fn named_field_list(self) -> Option<NamedFieldList<'a>> {
3881 super::child_opt(self) 3885 super::child_opt(self)
3882 } 3886 }
3887
3888 pub fn spread(self) -> Option<Expr<'a>> {
3889 super::child_opt(self)
3890 }
3883} 3891}
3884 3892
3885// StructPat 3893// StructPat
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index c55e9e07a..5bcdf3f1d 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -384,7 +384,7 @@ Grammar(
384 options: [ "Condition" ] 384 options: [ "Condition" ]
385 ), 385 ),
386 "ContinueExpr": (), 386 "ContinueExpr": (),
387 "BreakExpr": (), 387 "BreakExpr": (options: ["Expr"]),
388 "Label": (), 388 "Label": (),
389 "BlockExpr": ( 389 "BlockExpr": (
390 options: [ "Block" ] 390 options: [ "Block" ]
@@ -404,7 +404,7 @@ Grammar(
404 collections: [ [ "pats", "Pat" ] ] 404 collections: [ [ "pats", "Pat" ] ]
405 ), 405 ),
406 "MatchGuard": (), 406 "MatchGuard": (),
407 "StructLit": (options: ["Path", "NamedFieldList"]), 407 "StructLit": (options: ["Path", "NamedFieldList", ["spread", "Expr"]]),
408 "NamedFieldList": (collections: [ ["fields", "NamedField"] ]), 408 "NamedFieldList": (collections: [ ["fields", "NamedField"] ]),
409 "NamedField": (options: ["NameRef", "Expr"]), 409 "NamedField": (options: ["NameRef", "Expr"]),
410 "CallExpr": ( 410 "CallExpr": (