]>
Commit | Line | Data |
---|---|---|
ba9703b0 | 1 | use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN; |
dfeec247 | 2 | use super::diagnostics::Error; |
416331ca | 3 | use super::expr::LhsExpr; |
e1599b0c | 4 | use super::pat::GateOr; |
dfeec247 | 5 | use super::path::PathStyle; |
74b04a01 | 6 | use super::{BlockMode, Parser, Restrictions, SemiColonMode}; |
60c5eb7d | 7 | use crate::maybe_whole; |
60c5eb7d | 8 | |
74b04a01 | 9 | use rustc_ast::ast; |
ba9703b0 | 10 | use rustc_ast::ast::{AttrStyle, AttrVec, Attribute, MacCall, MacStmtStyle}; |
74b04a01 XL |
11 | use rustc_ast::ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt, StmtKind, DUMMY_NODE_ID}; |
12 | use rustc_ast::ptr::P; | |
13 | use rustc_ast::token::{self, TokenKind}; | |
14 | use rustc_ast::util::classify; | |
dfeec247 | 15 | use rustc_errors::{Applicability, PResult}; |
74b04a01 XL |
16 | use rustc_span::source_map::{BytePos, Span}; |
17 | use rustc_span::symbol::{kw, sym}; | |
416331ca XL |
18 | |
19 | use std::mem; | |
416331ca XL |
20 | |
21 | impl<'a> Parser<'a> { | |
e1599b0c | 22 | /// Parses a statement. This stops just before trailing semicolons on everything but items. |
416331ca XL |
23 | /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed. |
24 | pub fn parse_stmt(&mut self) -> PResult<'a, Option<Stmt>> { | |
74b04a01 | 25 | Ok(self.parse_stmt_without_recovery().unwrap_or_else(|mut e| { |
416331ca XL |
26 | e.emit(); |
27 | self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); | |
28 | None | |
dfeec247 | 29 | })) |
416331ca XL |
30 | } |
31 | ||
74b04a01 | 32 | fn parse_stmt_without_recovery(&mut self) -> PResult<'a, Option<Stmt>> { |
416331ca XL |
33 | maybe_whole!(self, NtStmt, |x| Some(x)); |
34 | ||
35 | let attrs = self.parse_outer_attributes()?; | |
36 | let lo = self.token.span; | |
37 | ||
74b04a01 XL |
38 | let stmt = if self.eat_keyword(kw::Let) { |
39 | self.parse_local_mk(lo, attrs.into())? | |
40 | } else if self.is_kw_followed_by_ident(kw::Mut) { | |
41 | self.recover_stmt_local(lo, attrs.into(), "missing keyword", "let mut")? | |
42 | } else if self.is_kw_followed_by_ident(kw::Auto) { | |
dfeec247 XL |
43 | self.bump(); // `auto` |
44 | let msg = "write `let` instead of `auto` to introduce a new variable"; | |
74b04a01 XL |
45 | self.recover_stmt_local(lo, attrs.into(), msg, "let")? |
46 | } else if self.is_kw_followed_by_ident(sym::var) { | |
dfeec247 XL |
47 | self.bump(); // `var` |
48 | let msg = "write `let` instead of `var` to introduce a new variable"; | |
74b04a01 | 49 | self.recover_stmt_local(lo, attrs.into(), msg, "let")? |
ba9703b0 | 50 | } else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() { |
74b04a01 XL |
51 | // We have avoided contextual keywords like `union`, items with `crate` visibility, |
52 | // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something | |
53 | // that starts like a path (1 token), but it fact not a path. | |
54 | // Also, we avoid stealing syntax from `parse_item_`. | |
55 | self.parse_stmt_path_start(lo, attrs)? | |
ba9703b0 | 56 | } else if let Some(item) = self.parse_item_common(attrs.clone(), false, true, |_| true)? { |
74b04a01 XL |
57 | // FIXME: Bad copy of attrs |
58 | self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item))) | |
59 | } else if self.eat(&token::Semi) { | |
60 | // Do not attempt to parse an expression if we're done here. | |
61 | self.error_outer_attrs(&attrs); | |
62 | self.mk_stmt(lo, StmtKind::Empty) | |
63 | } else if self.token != token::CloseDelim(token::Brace) { | |
64 | // Remainder are line-expr stmts. | |
65 | let e = self.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs.into()))?; | |
66 | self.mk_stmt(lo.to(e.span), StmtKind::Expr(e)) | |
67 | } else { | |
68 | self.error_outer_attrs(&attrs); | |
69 | return Ok(None); | |
70 | }; | |
71 | Ok(Some(stmt)) | |
72 | } | |
dfeec247 | 73 | |
74b04a01 XL |
74 | fn parse_stmt_path_start(&mut self, lo: Span, attrs: Vec<Attribute>) -> PResult<'a, Stmt> { |
75 | let path = self.parse_path(PathStyle::Expr)?; | |
dfeec247 | 76 | |
74b04a01 XL |
77 | if self.eat(&token::Not) { |
78 | return self.parse_stmt_mac(lo, attrs.into(), path); | |
dfeec247 XL |
79 | } |
80 | ||
74b04a01 XL |
81 | let expr = if self.check(&token::OpenDelim(token::Brace)) { |
82 | self.parse_struct_expr(lo, path, AttrVec::new())? | |
83 | } else { | |
84 | let hi = self.prev_token.span; | |
85 | self.mk_expr(lo.to(hi), ExprKind::Path(None, path), AttrVec::new()) | |
86 | }; | |
dfeec247 | 87 | |
74b04a01 XL |
88 | let expr = self.with_res(Restrictions::STMT_EXPR, |this| { |
89 | let expr = this.parse_dot_or_call_expr_with(expr, lo, attrs.into())?; | |
90 | this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr)) | |
91 | })?; | |
92 | Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr))) | |
dfeec247 XL |
93 | } |
94 | ||
95 | /// Parses a statement macro `mac!(args)` provided a `path` representing `mac`. | |
96 | /// At this point, the `!` token after the path has already been eaten. | |
74b04a01 | 97 | fn parse_stmt_mac(&mut self, lo: Span, attrs: AttrVec, path: ast::Path) -> PResult<'a, Stmt> { |
dfeec247 XL |
98 | let args = self.parse_mac_args()?; |
99 | let delim = args.delim(); | |
74b04a01 | 100 | let hi = self.prev_token.span; |
dfeec247 XL |
101 | |
102 | let style = | |
103 | if delim == token::Brace { MacStmtStyle::Braces } else { MacStmtStyle::NoBraces }; | |
104 | ||
ba9703b0 | 105 | let mac = MacCall { path, args, prior_type_ascription: self.last_type_ascription }; |
dfeec247 XL |
106 | |
107 | let kind = if delim == token::Brace || self.token == token::Semi || self.token == token::Eof | |
108 | { | |
ba9703b0 | 109 | StmtKind::MacCall(P((mac, style, attrs))) |
416331ca | 110 | } else { |
dfeec247 | 111 | // Since none of the above applied, this is an expression statement macro. |
ba9703b0 | 112 | let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac), AttrVec::new()); |
dfeec247 XL |
113 | let e = self.maybe_recover_from_bad_qpath(e, true)?; |
114 | let e = self.parse_dot_or_call_expr_with(e, lo, attrs)?; | |
115 | let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?; | |
116 | StmtKind::Expr(e) | |
117 | }; | |
74b04a01 | 118 | Ok(self.mk_stmt(lo.to(hi), kind)) |
dfeec247 | 119 | } |
416331ca | 120 | |
dfeec247 XL |
121 | /// Error on outer attributes in this context. |
122 | /// Also error if the previous token was a doc comment. | |
123 | fn error_outer_attrs(&self, attrs: &[Attribute]) { | |
74b04a01 XL |
124 | if let [.., last] = attrs { |
125 | if last.is_doc_comment() { | |
126 | self.span_fatal_err(last.span, Error::UselessDocComment).emit(); | |
dfeec247 | 127 | } else if attrs.iter().any(|a| a.style == AttrStyle::Outer) { |
74b04a01 | 128 | self.struct_span_err(last.span, "expected statement after outer attribute").emit(); |
416331ca | 129 | } |
dfeec247 XL |
130 | } |
131 | } | |
132 | ||
dfeec247 XL |
133 | fn recover_stmt_local( |
134 | &mut self, | |
135 | lo: Span, | |
136 | attrs: AttrVec, | |
137 | msg: &str, | |
138 | sugg: &str, | |
74b04a01 | 139 | ) -> PResult<'a, Stmt> { |
dfeec247 XL |
140 | let stmt = self.parse_local_mk(lo, attrs)?; |
141 | self.struct_span_err(lo, "invalid variable declaration") | |
142 | .span_suggestion(lo, msg, sugg.to_string(), Applicability::MachineApplicable) | |
143 | .emit(); | |
74b04a01 | 144 | Ok(stmt) |
dfeec247 XL |
145 | } |
146 | ||
147 | fn parse_local_mk(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, Stmt> { | |
74b04a01 XL |
148 | let local = self.parse_local(attrs)?; |
149 | Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Local(local))) | |
416331ca XL |
150 | } |
151 | ||
152 | /// Parses a local variable declaration. | |
dfeec247 | 153 | fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P<Local>> { |
74b04a01 | 154 | let lo = self.prev_token.span; |
e1599b0c | 155 | let pat = self.parse_top_pat(GateOr::Yes)?; |
416331ca XL |
156 | |
157 | let (err, ty) = if self.eat(&token::Colon) { | |
158 | // Save the state of the parser before parsing type normally, in case there is a `:` | |
159 | // instead of an `=` typo. | |
160 | let parser_snapshot_before_type = self.clone(); | |
74b04a01 | 161 | let colon_sp = self.prev_token.span; |
416331ca XL |
162 | match self.parse_ty() { |
163 | Ok(ty) => (None, Some(ty)), | |
164 | Err(mut err) => { | |
e1599b0c | 165 | // Rewind to before attempting to parse the type and continue parsing. |
416331ca XL |
166 | let parser_snapshot_after_type = self.clone(); |
167 | mem::replace(self, parser_snapshot_before_type); | |
ba9703b0 XL |
168 | if let Ok(snip) = self.span_to_snippet(pat.span) { |
169 | err.span_label(pat.span, format!("while parsing the type for `{}`", snip)); | |
170 | } | |
416331ca XL |
171 | (Some((parser_snapshot_after_type, colon_sp, err)), None) |
172 | } | |
173 | } | |
174 | } else { | |
175 | (None, None) | |
176 | }; | |
177 | let init = match (self.parse_initializer(err.is_some()), err) { | |
dfeec247 XL |
178 | (Ok(init), None) => { |
179 | // init parsed, ty parsed | |
416331ca XL |
180 | init |
181 | } | |
dfeec247 XL |
182 | (Ok(init), Some((_, colon_sp, mut err))) => { |
183 | // init parsed, ty error | |
416331ca XL |
184 | // Could parse the type as if it were the initializer, it is likely there was a |
185 | // typo in the code: `:` instead of `=`. Add suggestion and emit the error. | |
186 | err.span_suggestion_short( | |
187 | colon_sp, | |
188 | "use `=` if you meant to assign", | |
e74abb32 | 189 | " =".to_string(), |
dfeec247 | 190 | Applicability::MachineApplicable, |
416331ca XL |
191 | ); |
192 | err.emit(); | |
193 | // As this was parsed successfully, continue as if the code has been fixed for the | |
194 | // rest of the file. It will still fail due to the emitted error, but we avoid | |
195 | // extra noise. | |
196 | init | |
197 | } | |
dfeec247 XL |
198 | (Err(mut init_err), Some((snapshot, _, ty_err))) => { |
199 | // init error, ty error | |
416331ca XL |
200 | init_err.cancel(); |
201 | // Couldn't parse the type nor the initializer, only raise the type error and | |
202 | // return to the parser state before parsing the type as the initializer. | |
203 | // let x: <parse_error>; | |
204 | mem::replace(self, snapshot); | |
205 | return Err(ty_err); | |
206 | } | |
dfeec247 XL |
207 | (Err(err), None) => { |
208 | // init error, ty parsed | |
416331ca XL |
209 | // Couldn't parse the initializer and we're not attempting to recover a failed |
210 | // parse of the type, return the error. | |
211 | return Err(err); | |
212 | } | |
213 | }; | |
74b04a01 | 214 | let hi = if self.token == token::Semi { self.token.span } else { self.prev_token.span }; |
dfeec247 | 215 | Ok(P(ast::Local { ty, pat, init, id: DUMMY_NODE_ID, span: lo.to(hi), attrs })) |
416331ca XL |
216 | } |
217 | ||
218 | /// Parses the RHS of a local variable declaration (e.g., '= 14;'). | |
219 | fn parse_initializer(&mut self, skip_eq: bool) -> PResult<'a, Option<P<Expr>>> { | |
ba9703b0 | 220 | if self.eat(&token::Eq) || skip_eq { Ok(Some(self.parse_expr()?)) } else { Ok(None) } |
416331ca XL |
221 | } |
222 | ||
416331ca XL |
223 | /// Parses a block. No inner attributes are allowed. |
224 | pub fn parse_block(&mut self) -> PResult<'a, P<Block>> { | |
ba9703b0 XL |
225 | let (attrs, block) = self.parse_inner_attrs_and_block()?; |
226 | if let [.., last] = &*attrs { | |
227 | self.error_on_forbidden_inner_attr(last.span, DEFAULT_INNER_ATTR_FORBIDDEN); | |
dfeec247 | 228 | } |
ba9703b0 | 229 | Ok(block) |
dfeec247 XL |
230 | } |
231 | ||
232 | fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> { | |
233 | let sp = self.token.span; | |
234 | let tok = super::token_descr(&self.token); | |
235 | let mut e = self.struct_span_err(sp, &format!("expected `{{`, found {}", tok)); | |
236 | let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon; | |
237 | ||
238 | // Check to see if the user has written something like | |
239 | // | |
240 | // if (cond) | |
241 | // bar; | |
242 | // | |
243 | // which is valid in other languages, but not Rust. | |
74b04a01 | 244 | match self.parse_stmt_without_recovery() { |
ba9703b0 XL |
245 | // If the next token is an open brace (e.g., `if a b {`), the place- |
246 | // inside-a-block suggestion would be more likely wrong than right. | |
247 | Ok(Some(_)) | |
dfeec247 | 248 | if self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace)) |
ba9703b0 XL |
249 | || do_not_suggest_help => {} |
250 | Ok(Some(stmt)) => { | |
251 | let stmt_own_line = self.sess.source_map().is_line_before_span_empty(sp); | |
252 | let stmt_span = if stmt_own_line && self.eat(&token::Semi) { | |
dfeec247 | 253 | // Expand the span to include the semicolon. |
74b04a01 | 254 | stmt.span.with_hi(self.prev_token.span.hi()) |
dfeec247 XL |
255 | } else { |
256 | stmt.span | |
257 | }; | |
258 | if let Ok(snippet) = self.span_to_snippet(stmt_span) { | |
259 | e.span_suggestion( | |
260 | stmt_span, | |
261 | "try placing this code inside a block", | |
262 | format!("{{ {} }}", snippet), | |
263 | // Speculative; has been misleading in the past (#46836). | |
264 | Applicability::MaybeIncorrect, | |
265 | ); | |
416331ca | 266 | } |
416331ca | 267 | } |
dfeec247 XL |
268 | Err(mut e) => { |
269 | self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); | |
270 | e.cancel(); | |
271 | } | |
272 | _ => {} | |
416331ca | 273 | } |
dfeec247 | 274 | e.span_label(sp, "expected `{`"); |
ba9703b0 | 275 | Err(e) |
416331ca XL |
276 | } |
277 | ||
278 | /// Parses a block. Inner attributes are allowed. | |
e74abb32 | 279 | pub(super) fn parse_inner_attrs_and_block( |
dfeec247 | 280 | &mut self, |
ba9703b0 XL |
281 | ) -> PResult<'a, (Vec<Attribute>, P<Block>)> { |
282 | self.parse_block_common(self.token.span, BlockCheckMode::Default) | |
283 | } | |
284 | ||
285 | /// Parses a block. Inner attributes are allowed. | |
286 | pub(super) fn parse_block_common( | |
287 | &mut self, | |
288 | lo: Span, | |
289 | blk_mode: BlockCheckMode, | |
e74abb32 | 290 | ) -> PResult<'a, (Vec<Attribute>, P<Block>)> { |
416331ca XL |
291 | maybe_whole!(self, NtBlock, |x| (Vec::new(), x)); |
292 | ||
ba9703b0 XL |
293 | if !self.eat(&token::OpenDelim(token::Brace)) { |
294 | return self.error_block_no_opening_brace(); | |
295 | } | |
296 | ||
297 | Ok((self.parse_inner_attributes()?, self.parse_block_tail(lo, blk_mode)?)) | |
416331ca XL |
298 | } |
299 | ||
300 | /// Parses the rest of a block expression or function body. | |
301 | /// Precondition: already parsed the '{'. | |
ba9703b0 | 302 | fn parse_block_tail(&mut self, lo: Span, s: BlockCheckMode) -> PResult<'a, P<Block>> { |
416331ca XL |
303 | let mut stmts = vec![]; |
304 | while !self.eat(&token::CloseDelim(token::Brace)) { | |
305 | if self.token == token::Eof { | |
306 | break; | |
307 | } | |
74b04a01 | 308 | let stmt = match self.parse_full_stmt() { |
416331ca | 309 | Err(mut err) => { |
60c5eb7d | 310 | self.maybe_annotate_with_ascription(&mut err, false); |
416331ca XL |
311 | err.emit(); |
312 | self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore); | |
dfeec247 | 313 | Some(self.mk_stmt_err(self.token.span)) |
416331ca XL |
314 | } |
315 | Ok(stmt) => stmt, | |
316 | }; | |
317 | if let Some(stmt) = stmt { | |
318 | stmts.push(stmt); | |
319 | } else { | |
320 | // Found only `;` or `}`. | |
321 | continue; | |
322 | }; | |
323 | } | |
74b04a01 | 324 | Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span))) |
416331ca XL |
325 | } |
326 | ||
327 | /// Parses a statement, including the trailing semicolon. | |
74b04a01 | 328 | pub fn parse_full_stmt(&mut self) -> PResult<'a, Option<Stmt>> { |
e1599b0c | 329 | // Skip looking for a trailing semicolon when we have an interpolated statement. |
416331ca XL |
330 | maybe_whole!(self, NtStmt, |x| Some(x)); |
331 | ||
74b04a01 | 332 | let mut stmt = match self.parse_stmt_without_recovery()? { |
416331ca XL |
333 | Some(stmt) => stmt, |
334 | None => return Ok(None), | |
335 | }; | |
336 | ||
e74abb32 XL |
337 | let mut eat_semi = true; |
338 | match stmt.kind { | |
74b04a01 XL |
339 | // Expression without semicolon. |
340 | StmtKind::Expr(ref expr) | |
341 | if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) => | |
342 | { | |
343 | // Just check for errors and recover; do not eat semicolon yet. | |
344 | if let Err(mut e) = | |
345 | self.expect_one_of(&[], &[token::Semi, token::CloseDelim(token::Brace)]) | |
346 | { | |
347 | if let TokenKind::DocComment(..) = self.token.kind { | |
348 | if let Ok(snippet) = self.span_to_snippet(self.token.span) { | |
349 | let sp = self.token.span; | |
350 | let marker = &snippet[..3]; | |
351 | let (comment_marker, doc_comment_marker) = marker.split_at(2); | |
352 | ||
353 | e.span_suggestion( | |
354 | sp.with_hi(sp.lo() + BytePos(marker.len() as u32)), | |
355 | &format!( | |
356 | "add a space before `{}` to use a regular comment", | |
357 | doc_comment_marker, | |
358 | ), | |
359 | format!("{} {}", comment_marker, doc_comment_marker), | |
360 | Applicability::MaybeIncorrect, | |
361 | ); | |
362 | } | |
416331ca | 363 | } |
74b04a01 XL |
364 | e.emit(); |
365 | self.recover_stmt(); | |
366 | // Don't complain about type errors in body tail after parse error (#57383). | |
367 | let sp = expr.span.to(self.prev_token.span); | |
368 | stmt.kind = StmtKind::Expr(self.mk_expr_err(sp)); | |
416331ca XL |
369 | } |
370 | } | |
371 | StmtKind::Local(..) => { | |
74b04a01 XL |
372 | self.expect_semi()?; |
373 | eat_semi = false; | |
416331ca | 374 | } |
74b04a01 | 375 | StmtKind::Empty => eat_semi = false, |
416331ca XL |
376 | _ => {} |
377 | } | |
378 | ||
e74abb32 | 379 | if eat_semi && self.eat(&token::Semi) { |
416331ca XL |
380 | stmt = stmt.add_trailing_semicolon(); |
381 | } | |
74b04a01 | 382 | stmt.span = stmt.span.to(self.prev_token.span); |
416331ca XL |
383 | Ok(Some(stmt)) |
384 | } | |
385 | ||
dfeec247 XL |
386 | pub(super) fn mk_block(&self, stmts: Vec<Stmt>, rules: BlockCheckMode, span: Span) -> P<Block> { |
387 | P(Block { stmts, id: DUMMY_NODE_ID, rules, span }) | |
388 | } | |
389 | ||
390 | pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt { | |
391 | Stmt { id: DUMMY_NODE_ID, kind, span } | |
392 | } | |
393 | ||
394 | fn mk_stmt_err(&self, span: Span) -> Stmt { | |
395 | self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span))) | |
396 | } | |
397 | ||
398 | pub(super) fn mk_block_err(&self, span: Span) -> P<Block> { | |
399 | self.mk_block(vec![self.mk_stmt_err(span)], BlockCheckMode::Default, span) | |
416331ca XL |
400 | } |
401 | } |