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