]>
Commit | Line | Data |
---|---|---|
ba9703b0 | 1 | use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN; |
29967ef6 | 2 | use super::diagnostics::{AttemptLocalParseRecovery, Error}; |
416331ca | 3 | use super::expr::LhsExpr; |
cdc7bbd5 | 4 | use super::pat::RecoverComma; |
dfeec247 | 5 | use super::path::PathStyle; |
6a06907d XL |
6 | use super::TrailingToken; |
7 | use super::{AttrWrapper, BlockMode, ForceCollect, Parser, Restrictions, SemiColonMode}; | |
8 | use crate::maybe_whole; | |
60c5eb7d | 9 | |
3dfed10e | 10 | use rustc_ast as ast; |
74b04a01 XL |
11 | use rustc_ast::ptr::P; |
12 | use rustc_ast::token::{self, TokenKind}; | |
13 | use rustc_ast::util::classify; | |
94222f64 XL |
14 | use rustc_ast::{ |
15 | AstLike, AttrStyle, AttrVec, Attribute, LocalKind, MacCall, MacCallStmt, MacStmtStyle, | |
16 | }; | |
6a06907d XL |
17 | use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt}; |
18 | use rustc_ast::{StmtKind, DUMMY_NODE_ID}; | |
3c0e092e | 19 | use rustc_errors::{Applicability, DiagnosticBuilder, PResult}; |
74b04a01 XL |
20 | use rustc_span::source_map::{BytePos, Span}; |
21 | use rustc_span::symbol::{kw, sym}; | |
416331ca XL |
22 | |
23 | use std::mem; | |
416331ca XL |
24 | |
25 | impl<'a> Parser<'a> { | |
e1599b0c | 26 | /// Parses a statement. This stops just before trailing semicolons on everything but items. |
416331ca | 27 | /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed. |
1b1a35ee | 28 | // Public for rustfmt usage. |
5869c6ff XL |
29 | pub fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Stmt>> { |
30 | Ok(self.parse_stmt_without_recovery(false, force_collect).unwrap_or_else(|mut e| { | |
416331ca XL |
31 | e.emit(); |
32 | self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); | |
33 | None | |
dfeec247 | 34 | })) |
416331ca XL |
35 | } |
36 | ||
5869c6ff XL |
37 | /// If `force_capture` is true, forces collection of tokens regardless of whether |
38 | /// or not we have attributes | |
6a06907d | 39 | crate fn parse_stmt_without_recovery( |
5869c6ff XL |
40 | &mut self, |
41 | capture_semi: bool, | |
42 | force_collect: ForceCollect, | |
43 | ) -> PResult<'a, Option<Stmt>> { | |
6a06907d | 44 | let attrs = self.parse_outer_attributes()?; |
416331ca XL |
45 | let lo = self.token.span; |
46 | ||
6a06907d XL |
47 | // Don't use `maybe_whole` so that we have precise control |
48 | // over when we bump the parser | |
49 | if let token::Interpolated(nt) = &self.token.kind { | |
50 | if let token::NtStmt(stmt) = &**nt { | |
51 | let mut stmt = stmt.clone(); | |
cdc7bbd5 XL |
52 | self.bump(); |
53 | stmt.visit_attrs(|stmt_attrs| { | |
54 | attrs.prepend_to_nt_inner(stmt_attrs); | |
55 | }); | |
56 | return Ok(Some(stmt)); | |
6a06907d XL |
57 | } |
58 | } | |
fc512014 | 59 | |
5869c6ff | 60 | Ok(Some(if self.token.is_keyword(kw::Let) { |
6a06907d | 61 | self.parse_local_mk(lo, attrs, capture_semi, force_collect)? |
5869c6ff | 62 | } else if self.is_kw_followed_by_ident(kw::Mut) { |
cdc7bbd5 | 63 | self.recover_stmt_local(lo, attrs, "missing keyword", "let mut")? |
5869c6ff XL |
64 | } else if self.is_kw_followed_by_ident(kw::Auto) { |
65 | self.bump(); // `auto` | |
66 | let msg = "write `let` instead of `auto` to introduce a new variable"; | |
cdc7bbd5 | 67 | self.recover_stmt_local(lo, attrs, msg, "let")? |
5869c6ff XL |
68 | } else if self.is_kw_followed_by_ident(sym::var) { |
69 | self.bump(); // `var` | |
70 | let msg = "write `let` instead of `var` to introduce a new variable"; | |
cdc7bbd5 | 71 | self.recover_stmt_local(lo, attrs, msg, "let")? |
5869c6ff XL |
72 | } else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() { |
73 | // We have avoided contextual keywords like `union`, items with `crate` visibility, | |
74 | // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something | |
75 | // that starts like a path (1 token), but it fact not a path. | |
76 | // Also, we avoid stealing syntax from `parse_item_`. | |
17df50a5 XL |
77 | if force_collect == ForceCollect::Yes { |
78 | self.collect_tokens_no_attrs(|this| this.parse_stmt_path_start(lo, attrs)) | |
79 | } else { | |
80 | self.parse_stmt_path_start(lo, attrs) | |
81 | }? | |
5869c6ff XL |
82 | } else if let Some(item) = |
83 | self.parse_item_common(attrs.clone(), false, true, |_| true, force_collect)? | |
84 | { | |
85 | // FIXME: Bad copy of attrs | |
86 | self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item))) | |
87 | } else if self.eat(&token::Semi) { | |
88 | // Do not attempt to parse an expression if we're done here. | |
6a06907d | 89 | self.error_outer_attrs(&attrs.take_for_recovery()); |
5869c6ff XL |
90 | self.mk_stmt(lo, StmtKind::Empty) |
91 | } else if self.token != token::CloseDelim(token::Brace) { | |
92 | // Remainder are line-expr stmts. | |
17df50a5 XL |
93 | let e = if force_collect == ForceCollect::Yes { |
94 | self.collect_tokens_no_attrs(|this| { | |
95 | this.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs)) | |
96 | }) | |
97 | } else { | |
98 | self.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs)) | |
99 | }?; | |
5869c6ff | 100 | self.mk_stmt(lo.to(e.span), StmtKind::Expr(e)) |
74b04a01 | 101 | } else { |
6a06907d | 102 | self.error_outer_attrs(&attrs.take_for_recovery()); |
5869c6ff XL |
103 | return Ok(None); |
104 | })) | |
74b04a01 | 105 | } |
dfeec247 | 106 | |
17df50a5 XL |
107 | fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> { |
108 | let stmt = self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { | |
5869c6ff | 109 | let path = this.parse_path(PathStyle::Expr)?; |
dfeec247 | 110 | |
5869c6ff XL |
111 | if this.eat(&token::Not) { |
112 | let stmt_mac = this.parse_stmt_mac(lo, attrs.into(), path)?; | |
113 | if this.token == token::Semi { | |
114 | return Ok((stmt_mac, TrailingToken::Semi)); | |
115 | } else { | |
116 | return Ok((stmt_mac, TrailingToken::None)); | |
117 | } | |
118 | } | |
dfeec247 | 119 | |
5869c6ff | 120 | let expr = if this.eat(&token::OpenDelim(token::Brace)) { |
17df50a5 | 121 | this.parse_struct_expr(None, path, AttrVec::new(), true)? |
5869c6ff XL |
122 | } else { |
123 | let hi = this.prev_token.span; | |
124 | this.mk_expr(lo.to(hi), ExprKind::Path(None, path), AttrVec::new()) | |
125 | }; | |
dfeec247 | 126 | |
5869c6ff | 127 | let expr = this.with_res(Restrictions::STMT_EXPR, |this| { |
cdc7bbd5 XL |
128 | this.parse_dot_or_call_expr_with(expr, lo, attrs) |
129 | })?; | |
130 | // `DUMMY_SP` will get overwritten later in this function | |
131 | Ok((this.mk_stmt(rustc_span::DUMMY_SP, StmtKind::Expr(expr)), TrailingToken::None)) | |
132 | })?; | |
133 | ||
134 | if let StmtKind::Expr(expr) = stmt.kind { | |
135 | // Perform this outside of the `collect_tokens_trailing_token` closure, | |
136 | // since our outer attributes do not apply to this part of the expression | |
137 | let expr = self.with_res(Restrictions::STMT_EXPR, |this| { | |
5869c6ff XL |
138 | this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr)) |
139 | })?; | |
cdc7bbd5 XL |
140 | Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr))) |
141 | } else { | |
142 | Ok(stmt) | |
143 | } | |
dfeec247 XL |
144 | } |
145 | ||
146 | /// Parses a statement macro `mac!(args)` provided a `path` representing `mac`. | |
147 | /// At this point, the `!` token after the path has already been eaten. | |
74b04a01 | 148 | fn parse_stmt_mac(&mut self, lo: Span, attrs: AttrVec, path: ast::Path) -> PResult<'a, Stmt> { |
dfeec247 XL |
149 | let args = self.parse_mac_args()?; |
150 | let delim = args.delim(); | |
74b04a01 | 151 | let hi = self.prev_token.span; |
dfeec247 XL |
152 | |
153 | let style = | |
154 | if delim == token::Brace { MacStmtStyle::Braces } else { MacStmtStyle::NoBraces }; | |
155 | ||
ba9703b0 | 156 | let mac = MacCall { path, args, prior_type_ascription: self.last_type_ascription }; |
dfeec247 | 157 | |
c295e0f8 XL |
158 | let kind = |
159 | if (delim == token::Brace && self.token != token::Dot && self.token != token::Question) | |
160 | || self.token == token::Semi | |
161 | || self.token == token::Eof | |
162 | { | |
163 | StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None })) | |
164 | } else { | |
165 | // Since none of the above applied, this is an expression statement macro. | |
166 | let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac), AttrVec::new()); | |
167 | let e = self.maybe_recover_from_bad_qpath(e, true)?; | |
168 | let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?; | |
169 | let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?; | |
170 | StmtKind::Expr(e) | |
171 | }; | |
74b04a01 | 172 | Ok(self.mk_stmt(lo.to(hi), kind)) |
dfeec247 | 173 | } |
416331ca | 174 | |
dfeec247 XL |
175 | /// Error on outer attributes in this context. |
176 | /// Also error if the previous token was a doc comment. | |
177 | fn error_outer_attrs(&self, attrs: &[Attribute]) { | |
74b04a01 XL |
178 | if let [.., last] = attrs { |
179 | if last.is_doc_comment() { | |
17df50a5 | 180 | self.span_err(last.span, Error::UselessDocComment).emit(); |
dfeec247 | 181 | } else if attrs.iter().any(|a| a.style == AttrStyle::Outer) { |
74b04a01 | 182 | self.struct_span_err(last.span, "expected statement after outer attribute").emit(); |
416331ca | 183 | } |
dfeec247 XL |
184 | } |
185 | } | |
186 | ||
dfeec247 XL |
187 | fn recover_stmt_local( |
188 | &mut self, | |
189 | lo: Span, | |
cdc7bbd5 | 190 | attrs: AttrWrapper, |
dfeec247 XL |
191 | msg: &str, |
192 | sugg: &str, | |
74b04a01 | 193 | ) -> PResult<'a, Stmt> { |
5869c6ff | 194 | let stmt = self.recover_local_after_let(lo, attrs)?; |
dfeec247 XL |
195 | self.struct_span_err(lo, "invalid variable declaration") |
196 | .span_suggestion(lo, msg, sugg.to_string(), Applicability::MachineApplicable) | |
197 | .emit(); | |
74b04a01 | 198 | Ok(stmt) |
dfeec247 XL |
199 | } |
200 | ||
5869c6ff XL |
201 | fn parse_local_mk( |
202 | &mut self, | |
203 | lo: Span, | |
6a06907d | 204 | attrs: AttrWrapper, |
5869c6ff XL |
205 | capture_semi: bool, |
206 | force_collect: ForceCollect, | |
207 | ) -> PResult<'a, Stmt> { | |
6a06907d | 208 | self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| { |
5869c6ff XL |
209 | this.expect_keyword(kw::Let)?; |
210 | let local = this.parse_local(attrs.into())?; | |
211 | let trailing = if capture_semi && this.token.kind == token::Semi { | |
212 | TrailingToken::Semi | |
213 | } else { | |
214 | TrailingToken::None | |
215 | }; | |
216 | Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Local(local)), trailing)) | |
217 | }) | |
218 | } | |
219 | ||
cdc7bbd5 XL |
220 | fn recover_local_after_let(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> { |
221 | self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { | |
222 | let local = this.parse_local(attrs.into())?; | |
223 | // FIXME - maybe capture semicolon in recovery? | |
224 | Ok(( | |
225 | this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Local(local)), | |
226 | TrailingToken::None, | |
227 | )) | |
228 | }) | |
416331ca XL |
229 | } |
230 | ||
231 | /// Parses a local variable declaration. | |
dfeec247 | 232 | fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P<Local>> { |
74b04a01 | 233 | let lo = self.prev_token.span; |
cdc7bbd5 | 234 | let (pat, colon) = self.parse_pat_before_ty(None, RecoverComma::Yes, "`let` bindings")?; |
416331ca | 235 | |
6a06907d | 236 | let (err, ty) = if colon { |
416331ca XL |
237 | // Save the state of the parser before parsing type normally, in case there is a `:` |
238 | // instead of an `=` typo. | |
239 | let parser_snapshot_before_type = self.clone(); | |
74b04a01 | 240 | let colon_sp = self.prev_token.span; |
416331ca XL |
241 | match self.parse_ty() { |
242 | Ok(ty) => (None, Some(ty)), | |
243 | Err(mut err) => { | |
ba9703b0 XL |
244 | if let Ok(snip) = self.span_to_snippet(pat.span) { |
245 | err.span_label(pat.span, format!("while parsing the type for `{}`", snip)); | |
246 | } | |
3dfed10e XL |
247 | let err = if self.check(&token::Eq) { |
248 | err.emit(); | |
249 | None | |
250 | } else { | |
251 | // Rewind to before attempting to parse the type and continue parsing. | |
252 | let parser_snapshot_after_type = | |
253 | mem::replace(self, parser_snapshot_before_type); | |
254 | Some((parser_snapshot_after_type, colon_sp, err)) | |
255 | }; | |
256 | (err, None) | |
416331ca XL |
257 | } |
258 | } | |
259 | } else { | |
260 | (None, None) | |
261 | }; | |
262 | let init = match (self.parse_initializer(err.is_some()), err) { | |
dfeec247 XL |
263 | (Ok(init), None) => { |
264 | // init parsed, ty parsed | |
416331ca XL |
265 | init |
266 | } | |
dfeec247 XL |
267 | (Ok(init), Some((_, colon_sp, mut err))) => { |
268 | // init parsed, ty error | |
416331ca XL |
269 | // Could parse the type as if it were the initializer, it is likely there was a |
270 | // typo in the code: `:` instead of `=`. Add suggestion and emit the error. | |
271 | err.span_suggestion_short( | |
272 | colon_sp, | |
273 | "use `=` if you meant to assign", | |
e74abb32 | 274 | " =".to_string(), |
dfeec247 | 275 | Applicability::MachineApplicable, |
416331ca XL |
276 | ); |
277 | err.emit(); | |
278 | // As this was parsed successfully, continue as if the code has been fixed for the | |
279 | // rest of the file. It will still fail due to the emitted error, but we avoid | |
280 | // extra noise. | |
281 | init | |
282 | } | |
dfeec247 XL |
283 | (Err(mut init_err), Some((snapshot, _, ty_err))) => { |
284 | // init error, ty error | |
416331ca XL |
285 | init_err.cancel(); |
286 | // Couldn't parse the type nor the initializer, only raise the type error and | |
287 | // return to the parser state before parsing the type as the initializer. | |
288 | // let x: <parse_error>; | |
f9f354fc | 289 | *self = snapshot; |
416331ca XL |
290 | return Err(ty_err); |
291 | } | |
dfeec247 XL |
292 | (Err(err), None) => { |
293 | // init error, ty parsed | |
416331ca XL |
294 | // Couldn't parse the initializer and we're not attempting to recover a failed |
295 | // parse of the type, return the error. | |
296 | return Err(err); | |
297 | } | |
298 | }; | |
94222f64 XL |
299 | let kind = match init { |
300 | None => LocalKind::Decl, | |
301 | Some(init) => { | |
302 | if self.eat_keyword(kw::Else) { | |
3c0e092e XL |
303 | if self.token.is_keyword(kw::If) { |
304 | // `let...else if`. Emit the same error that `parse_block()` would, | |
305 | // but explicitly point out that this pattern is not allowed. | |
306 | let msg = "conditional `else if` is not supported for `let...else`"; | |
307 | return Err(self.error_block_no_opening_brace_msg(msg)); | |
308 | } | |
94222f64 XL |
309 | let els = self.parse_block()?; |
310 | self.check_let_else_init_bool_expr(&init); | |
311 | self.check_let_else_init_trailing_brace(&init); | |
312 | LocalKind::InitElse(init, els) | |
313 | } else { | |
314 | LocalKind::Init(init) | |
315 | } | |
316 | } | |
317 | }; | |
74b04a01 | 318 | let hi = if self.token == token::Semi { self.token.span } else { self.prev_token.span }; |
94222f64 XL |
319 | Ok(P(ast::Local { ty, pat, kind, id: DUMMY_NODE_ID, span: lo.to(hi), attrs, tokens: None })) |
320 | } | |
321 | ||
322 | fn check_let_else_init_bool_expr(&self, init: &ast::Expr) { | |
323 | if let ast::ExprKind::Binary(op, ..) = init.kind { | |
324 | if op.node.lazy() { | |
325 | let suggs = vec![ | |
326 | (init.span.shrink_to_lo(), "(".to_string()), | |
327 | (init.span.shrink_to_hi(), ")".to_string()), | |
328 | ]; | |
329 | self.struct_span_err( | |
330 | init.span, | |
331 | &format!( | |
332 | "a `{}` expression cannot be directly assigned in `let...else`", | |
333 | op.node.to_string() | |
334 | ), | |
335 | ) | |
336 | .multipart_suggestion( | |
3c0e092e | 337 | "wrap the expression in parentheses", |
94222f64 XL |
338 | suggs, |
339 | Applicability::MachineApplicable, | |
340 | ) | |
341 | .emit(); | |
342 | } | |
343 | } | |
344 | } | |
345 | ||
346 | fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) { | |
347 | if let Some(trailing) = classify::expr_trailing_brace(init) { | |
348 | let err_span = trailing.span.with_lo(trailing.span.hi() - BytePos(1)); | |
349 | let suggs = vec![ | |
350 | (trailing.span.shrink_to_lo(), "(".to_string()), | |
351 | (trailing.span.shrink_to_hi(), ")".to_string()), | |
352 | ]; | |
353 | self.struct_span_err( | |
354 | err_span, | |
355 | "right curly brace `}` before `else` in a `let...else` statement not allowed", | |
356 | ) | |
357 | .multipart_suggestion( | |
3c0e092e | 358 | "try wrapping the expression in parentheses", |
94222f64 XL |
359 | suggs, |
360 | Applicability::MachineApplicable, | |
361 | ) | |
362 | .emit(); | |
363 | } | |
416331ca XL |
364 | } |
365 | ||
6a06907d | 366 | /// Parses the RHS of a local variable declaration (e.g., `= 14;`). |
f035d41b XL |
367 | fn parse_initializer(&mut self, eq_optional: bool) -> PResult<'a, Option<P<Expr>>> { |
368 | let eq_consumed = match self.token.kind { | |
369 | token::BinOpEq(..) => { | |
370 | // Recover `let x <op>= 1` as `let x = 1` | |
371 | self.struct_span_err( | |
372 | self.token.span, | |
373 | "can't reassign to an uninitialized variable", | |
374 | ) | |
375 | .span_suggestion_short( | |
376 | self.token.span, | |
377 | "initialize the variable", | |
378 | "=".to_string(), | |
379 | Applicability::MaybeIncorrect, | |
380 | ) | |
6a06907d | 381 | .help("if you meant to overwrite, remove the `let` binding") |
f035d41b XL |
382 | .emit(); |
383 | self.bump(); | |
384 | true | |
385 | } | |
386 | _ => self.eat(&token::Eq), | |
387 | }; | |
388 | ||
389 | Ok(if eq_consumed || eq_optional { Some(self.parse_expr()?) } else { None }) | |
416331ca XL |
390 | } |
391 | ||
416331ca | 392 | /// Parses a block. No inner attributes are allowed. |
3dfed10e | 393 | pub(super) fn parse_block(&mut self) -> PResult<'a, P<Block>> { |
ba9703b0 XL |
394 | let (attrs, block) = self.parse_inner_attrs_and_block()?; |
395 | if let [.., last] = &*attrs { | |
396 | self.error_on_forbidden_inner_attr(last.span, DEFAULT_INNER_ATTR_FORBIDDEN); | |
dfeec247 | 397 | } |
ba9703b0 | 398 | Ok(block) |
dfeec247 XL |
399 | } |
400 | ||
3c0e092e | 401 | fn error_block_no_opening_brace_msg(&mut self, msg: &str) -> DiagnosticBuilder<'a> { |
dfeec247 | 402 | let sp = self.token.span; |
3c0e092e | 403 | let mut e = self.struct_span_err(sp, msg); |
dfeec247 XL |
404 | let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon; |
405 | ||
406 | // Check to see if the user has written something like | |
407 | // | |
408 | // if (cond) | |
409 | // bar; | |
410 | // | |
411 | // which is valid in other languages, but not Rust. | |
5869c6ff | 412 | match self.parse_stmt_without_recovery(false, ForceCollect::No) { |
ba9703b0 XL |
413 | // If the next token is an open brace (e.g., `if a b {`), the place- |
414 | // inside-a-block suggestion would be more likely wrong than right. | |
415 | Ok(Some(_)) | |
dfeec247 | 416 | if self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace)) |
ba9703b0 XL |
417 | || do_not_suggest_help => {} |
418 | Ok(Some(stmt)) => { | |
419 | let stmt_own_line = self.sess.source_map().is_line_before_span_empty(sp); | |
420 | let stmt_span = if stmt_own_line && self.eat(&token::Semi) { | |
dfeec247 | 421 | // Expand the span to include the semicolon. |
74b04a01 | 422 | stmt.span.with_hi(self.prev_token.span.hi()) |
dfeec247 XL |
423 | } else { |
424 | stmt.span | |
425 | }; | |
426 | if let Ok(snippet) = self.span_to_snippet(stmt_span) { | |
427 | e.span_suggestion( | |
428 | stmt_span, | |
429 | "try placing this code inside a block", | |
430 | format!("{{ {} }}", snippet), | |
431 | // Speculative; has been misleading in the past (#46836). | |
432 | Applicability::MaybeIncorrect, | |
433 | ); | |
416331ca | 434 | } |
416331ca | 435 | } |
dfeec247 XL |
436 | Err(mut e) => { |
437 | self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); | |
438 | e.cancel(); | |
439 | } | |
440 | _ => {} | |
416331ca | 441 | } |
dfeec247 | 442 | e.span_label(sp, "expected `{`"); |
3c0e092e XL |
443 | e |
444 | } | |
445 | ||
446 | fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> { | |
447 | let tok = super::token_descr(&self.token); | |
448 | let msg = format!("expected `{{`, found {}", tok); | |
449 | Err(self.error_block_no_opening_brace_msg(&msg)) | |
416331ca XL |
450 | } |
451 | ||
452 | /// Parses a block. Inner attributes are allowed. | |
e74abb32 | 453 | pub(super) fn parse_inner_attrs_and_block( |
dfeec247 | 454 | &mut self, |
ba9703b0 XL |
455 | ) -> PResult<'a, (Vec<Attribute>, P<Block>)> { |
456 | self.parse_block_common(self.token.span, BlockCheckMode::Default) | |
457 | } | |
458 | ||
459 | /// Parses a block. Inner attributes are allowed. | |
460 | pub(super) fn parse_block_common( | |
461 | &mut self, | |
462 | lo: Span, | |
463 | blk_mode: BlockCheckMode, | |
e74abb32 | 464 | ) -> PResult<'a, (Vec<Attribute>, P<Block>)> { |
416331ca XL |
465 | maybe_whole!(self, NtBlock, |x| (Vec::new(), x)); |
466 | ||
ba9703b0 XL |
467 | if !self.eat(&token::OpenDelim(token::Brace)) { |
468 | return self.error_block_no_opening_brace(); | |
469 | } | |
470 | ||
29967ef6 XL |
471 | let attrs = self.parse_inner_attributes()?; |
472 | let tail = if let Some(tail) = self.maybe_suggest_struct_literal(lo, blk_mode) { | |
473 | tail? | |
474 | } else { | |
475 | self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)? | |
476 | }; | |
477 | Ok((attrs, tail)) | |
416331ca XL |
478 | } |
479 | ||
480 | /// Parses the rest of a block expression or function body. | |
481 | /// Precondition: already parsed the '{'. | |
29967ef6 XL |
482 | crate fn parse_block_tail( |
483 | &mut self, | |
484 | lo: Span, | |
485 | s: BlockCheckMode, | |
486 | recover: AttemptLocalParseRecovery, | |
487 | ) -> PResult<'a, P<Block>> { | |
416331ca XL |
488 | let mut stmts = vec![]; |
489 | while !self.eat(&token::CloseDelim(token::Brace)) { | |
490 | if self.token == token::Eof { | |
491 | break; | |
492 | } | |
29967ef6 XL |
493 | let stmt = match self.parse_full_stmt(recover) { |
494 | Err(mut err) if recover.yes() => { | |
60c5eb7d | 495 | self.maybe_annotate_with_ascription(&mut err, false); |
416331ca XL |
496 | err.emit(); |
497 | self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore); | |
dfeec247 | 498 | Some(self.mk_stmt_err(self.token.span)) |
416331ca XL |
499 | } |
500 | Ok(stmt) => stmt, | |
29967ef6 | 501 | Err(err) => return Err(err), |
416331ca XL |
502 | }; |
503 | if let Some(stmt) = stmt { | |
504 | stmts.push(stmt); | |
505 | } else { | |
506 | // Found only `;` or `}`. | |
507 | continue; | |
508 | }; | |
509 | } | |
74b04a01 | 510 | Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span))) |
416331ca XL |
511 | } |
512 | ||
513 | /// Parses a statement, including the trailing semicolon. | |
29967ef6 XL |
514 | pub fn parse_full_stmt( |
515 | &mut self, | |
516 | recover: AttemptLocalParseRecovery, | |
517 | ) -> PResult<'a, Option<Stmt>> { | |
e1599b0c | 518 | // Skip looking for a trailing semicolon when we have an interpolated statement. |
416331ca XL |
519 | maybe_whole!(self, NtStmt, |x| Some(x)); |
520 | ||
5869c6ff | 521 | let mut stmt = match self.parse_stmt_without_recovery(true, ForceCollect::No)? { |
416331ca XL |
522 | Some(stmt) => stmt, |
523 | None => return Ok(None), | |
524 | }; | |
525 | ||
e74abb32 XL |
526 | let mut eat_semi = true; |
527 | match stmt.kind { | |
74b04a01 | 528 | // Expression without semicolon. |
1b1a35ee | 529 | StmtKind::Expr(ref mut expr) |
74b04a01 XL |
530 | if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) => |
531 | { | |
532 | // Just check for errors and recover; do not eat semicolon yet. | |
533 | if let Err(mut e) = | |
534 | self.expect_one_of(&[], &[token::Semi, token::CloseDelim(token::Brace)]) | |
535 | { | |
536 | if let TokenKind::DocComment(..) = self.token.kind { | |
537 | if let Ok(snippet) = self.span_to_snippet(self.token.span) { | |
538 | let sp = self.token.span; | |
539 | let marker = &snippet[..3]; | |
540 | let (comment_marker, doc_comment_marker) = marker.split_at(2); | |
541 | ||
542 | e.span_suggestion( | |
543 | sp.with_hi(sp.lo() + BytePos(marker.len() as u32)), | |
544 | &format!( | |
545 | "add a space before `{}` to use a regular comment", | |
546 | doc_comment_marker, | |
547 | ), | |
548 | format!("{} {}", comment_marker, doc_comment_marker), | |
549 | Applicability::MaybeIncorrect, | |
550 | ); | |
551 | } | |
416331ca | 552 | } |
1b1a35ee XL |
553 | if let Err(mut e) = |
554 | self.check_mistyped_turbofish_with_multiple_type_params(e, expr) | |
555 | { | |
29967ef6 XL |
556 | if recover.no() { |
557 | return Err(e); | |
558 | } | |
1b1a35ee XL |
559 | e.emit(); |
560 | self.recover_stmt(); | |
561 | } | |
74b04a01 XL |
562 | // Don't complain about type errors in body tail after parse error (#57383). |
563 | let sp = expr.span.to(self.prev_token.span); | |
1b1a35ee | 564 | *expr = self.mk_expr_err(sp); |
416331ca XL |
565 | } |
566 | } | |
fc512014 | 567 | StmtKind::Expr(_) | StmtKind::MacCall(_) => {} |
94222f64 XL |
568 | StmtKind::Local(ref mut local) if let Err(e) = self.expect_semi() => { |
569 | // We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover. | |
570 | match &mut local.kind { | |
571 | LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => { | |
1b1a35ee XL |
572 | self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?; |
573 | // We found `foo<bar, baz>`, have we fully recovered? | |
574 | self.expect_semi()?; | |
575 | } | |
94222f64 | 576 | LocalKind::Decl => return Err(e), |
1b1a35ee | 577 | } |
74b04a01 | 578 | eat_semi = false; |
416331ca | 579 | } |
94222f64 | 580 | StmtKind::Empty | StmtKind::Item(_) | StmtKind::Local(_) | StmtKind::Semi(_) => eat_semi = false, |
416331ca XL |
581 | } |
582 | ||
e74abb32 | 583 | if eat_semi && self.eat(&token::Semi) { |
416331ca XL |
584 | stmt = stmt.add_trailing_semicolon(); |
585 | } | |
74b04a01 | 586 | stmt.span = stmt.span.to(self.prev_token.span); |
416331ca XL |
587 | Ok(Some(stmt)) |
588 | } | |
589 | ||
dfeec247 | 590 | pub(super) fn mk_block(&self, stmts: Vec<Stmt>, rules: BlockCheckMode, span: Span) -> P<Block> { |
94222f64 XL |
591 | P(Block { |
592 | stmts, | |
593 | id: DUMMY_NODE_ID, | |
594 | rules, | |
595 | span, | |
596 | tokens: None, | |
597 | could_be_bare_literal: false, | |
598 | }) | |
dfeec247 XL |
599 | } |
600 | ||
601 | pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt { | |
fc512014 | 602 | Stmt { id: DUMMY_NODE_ID, kind, span } |
dfeec247 XL |
603 | } |
604 | ||
29967ef6 | 605 | pub(super) fn mk_stmt_err(&self, span: Span) -> Stmt { |
dfeec247 XL |
606 | self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span))) |
607 | } | |
608 | ||
609 | pub(super) fn mk_block_err(&self, span: Span) -> P<Block> { | |
610 | self.mk_block(vec![self.mk_stmt_err(span)], BlockCheckMode::Default, span) | |
416331ca XL |
611 | } |
612 | } |