aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorrobojumper <[email protected]>2020-05-31 09:59:40 +0100
committerrobojumper <[email protected]>2020-05-31 10:40:18 +0100
commit1cd78a3355ea70d3070cabb00c80a5d195499752 (patch)
tree91b2b70dbf3ac73a2fe430fd92a26d41df2a60cb /crates
parentfb469c3b31e7da962e91269b53b2f53d672cc4ba (diff)
correctly infer labelled breaks
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir_def/src/body/lower.rs56
-rw-r--r--crates/ra_hir_def/src/body/scope.rs4
-rw-r--r--crates/ra_hir_def/src/expr.rs19
-rw-r--r--crates/ra_hir_expand/src/name.rs5
-rw-r--r--crates/ra_hir_ty/src/infer.rs11
-rw-r--r--crates/ra_hir_ty/src/infer/expr.rs45
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs20
-rw-r--r--crates/ra_syntax/src/ast/generated/nodes.rs1
8 files changed, 116 insertions, 45 deletions
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index 905c0cf5d..dc52c6bd9 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -134,7 +134,7 @@ impl ExprCollector<'_> {
134 self.make_expr(expr, Err(SyntheticSyntax)) 134 self.make_expr(expr, Err(SyntheticSyntax))
135 } 135 }
136 fn empty_block(&mut self) -> ExprId { 136 fn empty_block(&mut self) -> ExprId {
137 self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None }) 137 self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None, label: None })
138 } 138 }
139 fn missing_expr(&mut self) -> ExprId { 139 fn missing_expr(&mut self) -> ExprId {
140 self.alloc_expr_desugared(Expr::Missing) 140 self.alloc_expr_desugared(Expr::Missing)
@@ -215,7 +215,13 @@ impl ExprCollector<'_> {
215 ast::Expr::BlockExpr(e) => self.collect_block(e), 215 ast::Expr::BlockExpr(e) => self.collect_block(e),
216 ast::Expr::LoopExpr(e) => { 216 ast::Expr::LoopExpr(e) => {
217 let body = self.collect_block_opt(e.loop_body()); 217 let body = self.collect_block_opt(e.loop_body());
218 self.alloc_expr(Expr::Loop { body }, syntax_ptr) 218 self.alloc_expr(
219 Expr::Loop {
220 body,
221 label: e.label().and_then(|l| l.lifetime_token()).map(|l| Name::new_lifetime(&l)),
222 },
223 syntax_ptr,
224 )
219 } 225 }
220 ast::Expr::WhileExpr(e) => { 226 ast::Expr::WhileExpr(e) => {
221 let body = self.collect_block_opt(e.loop_body()); 227 let body = self.collect_block_opt(e.loop_body());
@@ -230,25 +236,47 @@ impl ExprCollector<'_> {
230 let pat = self.collect_pat(pat); 236 let pat = self.collect_pat(pat);
231 let match_expr = self.collect_expr_opt(condition.expr()); 237 let match_expr = self.collect_expr_opt(condition.expr());
232 let placeholder_pat = self.missing_pat(); 238 let placeholder_pat = self.missing_pat();
233 let break_ = self.alloc_expr_desugared(Expr::Break { expr: None }); 239 let break_ =
240 self.alloc_expr_desugared(Expr::Break { expr: None, label: None });
234 let arms = vec![ 241 let arms = vec![
235 MatchArm { pat, expr: body, guard: None }, 242 MatchArm { pat, expr: body, guard: None },
236 MatchArm { pat: placeholder_pat, expr: break_, guard: None }, 243 MatchArm { pat: placeholder_pat, expr: break_, guard: None },
237 ]; 244 ];
238 let match_expr = 245 let match_expr =
239 self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms }); 246 self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms });
240 return self.alloc_expr(Expr::Loop { body: match_expr }, syntax_ptr); 247 return self.alloc_expr(
248 Expr::Loop {
249 body: match_expr,
250 label: e.label().and_then(|l| l.lifetime_token()).map(|l| Name::new_lifetime(&l)),
251 },
252 syntax_ptr,
253 );
241 } 254 }
242 }, 255 },
243 }; 256 };
244 257
245 self.alloc_expr(Expr::While { condition, body }, syntax_ptr) 258 self.alloc_expr(
259 Expr::While {
260 condition,
261 body,
262 label: e.label().and_then(|l| l.lifetime_token()).map(|l| Name::new_lifetime(&l)),
263 },
264 syntax_ptr,
265 )
246 } 266 }
247 ast::Expr::ForExpr(e) => { 267 ast::Expr::ForExpr(e) => {
248 let iterable = self.collect_expr_opt(e.iterable()); 268 let iterable = self.collect_expr_opt(e.iterable());
249 let pat = self.collect_pat_opt(e.pat()); 269 let pat = self.collect_pat_opt(e.pat());
250 let body = self.collect_block_opt(e.loop_body()); 270 let body = self.collect_block_opt(e.loop_body());
251 self.alloc_expr(Expr::For { iterable, pat, body }, syntax_ptr) 271 self.alloc_expr(
272 Expr::For {
273 iterable,
274 pat,
275 body,
276 label: e.label().and_then(|l| l.lifetime_token()).map(|l| Name::new_lifetime(&l)),
277 },
278 syntax_ptr,
279 )
252 } 280 }
253 ast::Expr::CallExpr(e) => { 281 ast::Expr::CallExpr(e) => {
254 let callee = self.collect_expr_opt(e.expr()); 282 let callee = self.collect_expr_opt(e.expr());
@@ -301,13 +329,18 @@ impl ExprCollector<'_> {
301 .unwrap_or(Expr::Missing); 329 .unwrap_or(Expr::Missing);
302 self.alloc_expr(path, syntax_ptr) 330 self.alloc_expr(path, syntax_ptr)
303 } 331 }
304 ast::Expr::ContinueExpr(_e) => { 332 ast::Expr::ContinueExpr(e) => {
305 // FIXME: labels 333 self.alloc_expr(
306 self.alloc_expr(Expr::Continue, syntax_ptr) 334 Expr::Continue { label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) },
335 syntax_ptr,
336 )
307 } 337 }
308 ast::Expr::BreakExpr(e) => { 338 ast::Expr::BreakExpr(e) => {
309 let expr = e.expr().map(|e| self.collect_expr(e)); 339 let expr = e.expr().map(|e| self.collect_expr(e));
310 self.alloc_expr(Expr::Break { expr }, syntax_ptr) 340 self.alloc_expr(
341 Expr::Break { expr, label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) },
342 syntax_ptr,
343 )
311 } 344 }
312 ast::Expr::ParenExpr(e) => { 345 ast::Expr::ParenExpr(e) => {
313 let inner = self.collect_expr_opt(e.expr()); 346 let inner = self.collect_expr_opt(e.expr());
@@ -529,7 +562,8 @@ impl ExprCollector<'_> {
529 }) 562 })
530 .collect(); 563 .collect();
531 let tail = block.expr().map(|e| self.collect_expr(e)); 564 let tail = block.expr().map(|e| self.collect_expr(e));
532 self.alloc_expr(Expr::Block { statements, tail }, syntax_node_ptr) 565 let label = block.label().and_then(|l| l.lifetime_token()).map(|t| Name::new_lifetime(&t));
566 self.alloc_expr(Expr::Block { statements, tail, label }, syntax_node_ptr)
533 } 567 }
534 568
535 fn collect_block_items(&mut self, block: &ast::BlockExpr) { 569 fn collect_block_items(&mut self, block: &ast::BlockExpr) {
diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs
index 09e92b74e..e48ff38f9 100644
--- a/crates/ra_hir_def/src/body/scope.rs
+++ b/crates/ra_hir_def/src/body/scope.rs
@@ -138,10 +138,10 @@ fn compute_block_scopes(
138fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) { 138fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) {
139 scopes.set_scope(expr, scope); 139 scopes.set_scope(expr, scope);
140 match &body[expr] { 140 match &body[expr] {
141 Expr::Block { statements, tail } => { 141 Expr::Block { statements, tail, .. } => {
142 compute_block_scopes(&statements, *tail, body, scopes, scope); 142 compute_block_scopes(&statements, *tail, body, scopes, scope);
143 } 143 }
144 Expr::For { iterable, pat, body: body_expr } => { 144 Expr::For { iterable, pat, body: body_expr, .. } => {
145 compute_expr_scopes(*iterable, body, scopes, scope); 145 compute_expr_scopes(*iterable, body, scopes, scope);
146 let scope = scopes.new_scope(scope); 146 let scope = scopes.new_scope(scope);
147 scopes.add_bindings(body, scope, *pat); 147 scopes.add_bindings(body, scope, *pat);
diff --git a/crates/ra_hir_def/src/expr.rs b/crates/ra_hir_def/src/expr.rs
index f25c6f958..8683f6c7f 100644
--- a/crates/ra_hir_def/src/expr.rs
+++ b/crates/ra_hir_def/src/expr.rs
@@ -52,18 +52,22 @@ pub enum Expr {
52 Block { 52 Block {
53 statements: Vec<Statement>, 53 statements: Vec<Statement>,
54 tail: Option<ExprId>, 54 tail: Option<ExprId>,
55 label: Option<Name>,
55 }, 56 },
56 Loop { 57 Loop {
57 body: ExprId, 58 body: ExprId,
59 label: Option<Name>,
58 }, 60 },
59 While { 61 While {
60 condition: ExprId, 62 condition: ExprId,
61 body: ExprId, 63 body: ExprId,
64 label: Option<Name>,
62 }, 65 },
63 For { 66 For {
64 iterable: ExprId, 67 iterable: ExprId,
65 pat: PatId, 68 pat: PatId,
66 body: ExprId, 69 body: ExprId,
70 label: Option<Name>,
67 }, 71 },
68 Call { 72 Call {
69 callee: ExprId, 73 callee: ExprId,
@@ -79,9 +83,12 @@ pub enum Expr {
79 expr: ExprId, 83 expr: ExprId,
80 arms: Vec<MatchArm>, 84 arms: Vec<MatchArm>,
81 }, 85 },
82 Continue, 86 Continue {
87 label: Option<Name>,
88 },
83 Break { 89 Break {
84 expr: Option<ExprId>, 90 expr: Option<ExprId>,
91 label: Option<Name>,
85 }, 92 },
86 Return { 93 Return {
87 expr: Option<ExprId>, 94 expr: Option<ExprId>,
@@ -225,7 +232,7 @@ impl Expr {
225 f(*else_branch); 232 f(*else_branch);
226 } 233 }
227 } 234 }
228 Expr::Block { statements, tail } => { 235 Expr::Block { statements, tail, .. } => {
229 for stmt in statements { 236 for stmt in statements {
230 match stmt { 237 match stmt {
231 Statement::Let { initializer, .. } => { 238 Statement::Let { initializer, .. } => {
@@ -241,8 +248,8 @@ impl Expr {
241 } 248 }
242 } 249 }
243 Expr::TryBlock { body } => f(*body), 250 Expr::TryBlock { body } => f(*body),
244 Expr::Loop { body } => f(*body), 251 Expr::Loop { body, .. } => f(*body),
245 Expr::While { condition, body } => { 252 Expr::While { condition, body, .. } => {
246 f(*condition); 253 f(*condition);
247 f(*body); 254 f(*body);
248 } 255 }
@@ -268,8 +275,8 @@ impl Expr {
268 f(arm.expr); 275 f(arm.expr);
269 } 276 }
270 } 277 }
271 Expr::Continue => {} 278 Expr::Continue { .. } => {},
272 Expr::Break { expr } | Expr::Return { expr } => { 279 Expr::Break { expr, .. } | Expr::Return { expr } => {
273 if let Some(expr) = expr { 280 if let Some(expr) = expr {
274 f(*expr); 281 f(*expr);
275 } 282 }
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index fecce224e..ea495cb11 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -37,6 +37,11 @@ impl Name {
37 Name(Repr::TupleField(idx)) 37 Name(Repr::TupleField(idx))
38 } 38 }
39 39
40 pub fn new_lifetime(lt: &ra_syntax::SyntaxToken) -> Name {
41 assert!(lt.kind() == ra_syntax::SyntaxKind::LIFETIME);
42 Name(Repr::Text(lt.text().clone()))
43 }
44
40 /// Shortcut to create inline plain text name 45 /// Shortcut to create inline plain text name
41 const fn new_inline_ascii(text: &[u8]) -> Name { 46 const fn new_inline_ascii(text: &[u8]) -> Name {
42 Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text)) 47 Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text))
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index 957d6e0b5..dc77e88e5 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -219,6 +219,17 @@ struct InferenceContext<'a> {
219struct BreakableContext { 219struct BreakableContext {
220 pub may_break: bool, 220 pub may_break: bool,
221 pub break_ty: Ty, 221 pub break_ty: Ty,
222 pub label: Option<name::Name>,
223}
224
225fn find_breakable<'c>(
226 ctxs: &'c mut [BreakableContext],
227 label: Option<&name::Name>,
228) -> Option<&'c mut BreakableContext> {
229 match label {
230 Some(_) => ctxs.iter_mut().rev().find(|ctx| ctx.label.as_ref() == label),
231 None => ctxs.last_mut(),
232 }
222} 233}
223 234
224impl<'a> InferenceContext<'a> { 235impl<'a> InferenceContext<'a> {
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 78084cb57..4a98e2deb 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -22,8 +22,8 @@ use crate::{
22}; 22};
23 23
24use super::{ 24use super::{
25 BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, 25 find_breakable, BindingMode, BreakableContext, Diverges, Expectation, InferenceContext,
26 TypeMismatch, 26 InferenceDiagnostic, TypeMismatch,
27}; 27};
28 28
29impl<'a> InferenceContext<'a> { 29impl<'a> InferenceContext<'a> {
@@ -86,16 +86,20 @@ impl<'a> InferenceContext<'a> {
86 86
87 self.coerce_merge_branch(&then_ty, &else_ty) 87 self.coerce_merge_branch(&then_ty, &else_ty)
88 } 88 }
89 Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected), 89 Expr::Block { statements, tail, .. } => {
90 // FIXME: Breakable block inference
91 self.infer_block(statements, *tail, expected)
92 }
90 Expr::TryBlock { body } => { 93 Expr::TryBlock { body } => {
91 let _inner = self.infer_expr(*body, expected); 94 let _inner = self.infer_expr(*body, expected);
92 // FIXME should be std::result::Result<{inner}, _> 95 // FIXME should be std::result::Result<{inner}, _>
93 Ty::Unknown 96 Ty::Unknown
94 } 97 }
95 Expr::Loop { body } => { 98 Expr::Loop { body, label } => {
96 self.breakables.push(BreakableContext { 99 self.breakables.push(BreakableContext {
97 may_break: false, 100 may_break: false,
98 break_ty: self.table.new_type_var(), 101 break_ty: self.table.new_type_var(),
102 label: label.clone(),
99 }); 103 });
100 self.infer_expr(*body, &Expectation::has_type(Ty::unit())); 104 self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
101 105
@@ -110,8 +114,12 @@ impl<'a> InferenceContext<'a> {
110 Ty::simple(TypeCtor::Never) 114 Ty::simple(TypeCtor::Never)
111 } 115 }
112 } 116 }
113 Expr::While { condition, body } => { 117 Expr::While { condition, body, label } => {
114 self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); 118 self.breakables.push(BreakableContext {
119 may_break: false,
120 break_ty: Ty::Unknown,
121 label: label.clone(),
122 });
115 // while let is desugared to a match loop, so this is always simple while 123 // while let is desugared to a match loop, so this is always simple while
116 self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); 124 self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
117 self.infer_expr(*body, &Expectation::has_type(Ty::unit())); 125 self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
@@ -120,10 +128,14 @@ impl<'a> InferenceContext<'a> {
120 self.diverges = Diverges::Maybe; 128 self.diverges = Diverges::Maybe;
121 Ty::unit() 129 Ty::unit()
122 } 130 }
123 Expr::For { iterable, body, pat } => { 131 Expr::For { iterable, body, pat, label } => {
124 let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); 132 let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
125 133
126 self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); 134 self.breakables.push(BreakableContext {
135 may_break: false,
136 break_ty: Ty::Unknown,
137 label: label.clone(),
138 });
127 let pat_ty = 139 let pat_ty =
128 self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); 140 self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
129 141
@@ -236,23 +248,24 @@ impl<'a> InferenceContext<'a> {
236 let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr); 248 let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
237 self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or(Ty::Unknown) 249 self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or(Ty::Unknown)
238 } 250 }
239 Expr::Continue => Ty::simple(TypeCtor::Never), 251 Expr::Continue { .. } => Ty::simple(TypeCtor::Never),
240 Expr::Break { expr } => { 252 Expr::Break { expr, label } => {
241 let val_ty = if let Some(expr) = expr { 253 let val_ty = if let Some(expr) = expr {
242 self.infer_expr(*expr, &Expectation::none()) 254 self.infer_expr(*expr, &Expectation::none())
243 } else { 255 } else {
244 Ty::unit() 256 Ty::unit()
245 }; 257 };
246 258
247 let last_ty = if let Some(ctxt) = self.breakables.last() { 259 let last_ty =
248 ctxt.break_ty.clone() 260 if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
249 } else { 261 ctxt.break_ty.clone()
250 Ty::Unknown 262 } else {
251 }; 263 Ty::Unknown
264 };
252 265
253 let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); 266 let merged_type = self.coerce_merge_branch(&last_ty, &val_ty);
254 267
255 if let Some(ctxt) = self.breakables.last_mut() { 268 if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
256 ctxt.break_ty = merged_type; 269 ctxt.break_ty = merged_type;
257 ctxt.may_break = true; 270 ctxt.may_break = true;
258 } else { 271 } else {
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index beceb8634..88309157b 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -1969,17 +1969,17 @@ fn foo() {
1969 25..333 '|| 'ou... }': || -> bool 1969 25..333 '|| 'ou... }': || -> bool
1970 28..333 ''outer... }': bool 1970 28..333 ''outer... }': bool
1971 41..333 '{ ... }': () 1971 41..333 '{ ... }': ()
1972 55..60 'inner': i32 1972 55..60 'inner': i8
1973 63..301 ''inner... }': i32 1973 63..301 ''inner... }': i8
1974 76..301 '{ ... }': () 1974 76..301 '{ ... }': ()
1975 94..95 'i': i32 1975 94..95 'i': bool
1976 98..114 'Defaul...efault': {unknown} 1976 98..114 'Defaul...efault': {unknown}
1977 98..116 'Defaul...ault()': i32 1977 98..116 'Defaul...ault()': bool
1978 130..270 'if (br... }': () 1978 130..270 'if (br... }': ()
1979 134..148 'break 'outer i': ! 1979 134..148 'break 'outer i': !
1980 147..148 'i': i32 1980 147..148 'i': bool
1981 150..209 '{ ... }': () 1981 150..209 '{ ... }': ()
1982 168..194 'loop {...5i8; }': i8 1982 168..194 'loop {...5i8; }': !
1983 173..194 '{ brea...5i8; }': () 1983 173..194 '{ brea...5i8; }': ()
1984 175..191 'break ...er 5i8': ! 1984 175..191 'break ...er 5i8': !
1985 188..191 '5i8': i8 1985 188..191 '5i8': i8
@@ -1987,13 +1987,13 @@ fn foo() {
1987 218..222 'true': bool 1987 218..222 'true': bool
1988 223..270 '{ ... }': () 1988 223..270 '{ ... }': ()
1989 241..255 'break 'inner 6': ! 1989 241..255 'break 'inner 6': !
1990 254..255 '6': i32 1990 254..255 '6': i8
1991 283..290 'break 7': ! 1991 283..290 'break 7': !
1992 289..290 '7': i32 1992 289..290 '7': i8
1993 311..326 'break inner < 8': ! 1993 311..326 'break inner < 8': !
1994 317..322 'inner': i32 1994 317..322 'inner': i8
1995 317..326 'inner < 8': bool 1995 317..326 'inner < 8': bool
1996 325..326 '8': i32 1996 325..326 '8': i8
1997 "### 1997 "###
1998 ); 1998 );
1999} 1999}
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs
index 255402fbc..cb430ca01 100644
--- a/crates/ra_syntax/src/ast/generated/nodes.rs
+++ b/crates/ra_syntax/src/ast/generated/nodes.rs
@@ -1081,6 +1081,7 @@ pub struct BlockExpr {
1081impl ast::AttrsOwner for BlockExpr {} 1081impl ast::AttrsOwner for BlockExpr {}
1082impl ast::ModuleItemOwner for BlockExpr {} 1082impl ast::ModuleItemOwner for BlockExpr {}
1083impl BlockExpr { 1083impl BlockExpr {
1084 pub fn label(&self) -> Option<Label> { support::child(&self.syntax) }
1084 pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) } 1085 pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) }
1085 pub fn statements(&self) -> AstChildren<Stmt> { support::children(&self.syntax) } 1086 pub fn statements(&self) -> AstChildren<Stmt> { support::children(&self.syntax) }
1086 pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } 1087 pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }