]>
Commit | Line | Data |
---|---|---|
416331ca XL |
1 | use super::{Parser, PResult, Restrictions, PrevTokenKind, SemiColonMode, BlockMode}; |
2 | use super::expr::LhsExpr; | |
3 | use super::path::PathStyle; | |
e1599b0c | 4 | use super::pat::GateOr; |
e74abb32 | 5 | use super::diagnostics::Error; |
416331ca XL |
6 | |
7 | use crate::ptr::P; | |
8 | use crate::{maybe_whole, ThinVec}; | |
e1599b0c XL |
9 | use crate::ast::{self, DUMMY_NODE_ID, Stmt, StmtKind, Local, Block, BlockCheckMode, Expr, ExprKind}; |
10 | use crate::ast::{Attribute, AttrStyle, VisibilityKind, MacStmtStyle, Mac, MacDelimiter}; | |
416331ca | 11 | use crate::parse::{classify, DirectoryOwnership}; |
e1599b0c | 12 | use crate::parse::token; |
416331ca XL |
13 | use crate::source_map::{respan, Span}; |
14 | use crate::symbol::{kw, sym}; | |
15 | ||
16 | use std::mem; | |
17 | use errors::Applicability; | |
18 | ||
19 | impl<'a> Parser<'a> { | |
e1599b0c | 20 | /// Parses a statement. This stops just before trailing semicolons on everything but items. |
416331ca XL |
21 | /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed. |
22 | pub fn parse_stmt(&mut self) -> PResult<'a, Option<Stmt>> { | |
23 | Ok(self.parse_stmt_(true)) | |
24 | } | |
25 | ||
26 | fn parse_stmt_(&mut self, macro_legacy_warnings: bool) -> Option<Stmt> { | |
27 | self.parse_stmt_without_recovery(macro_legacy_warnings).unwrap_or_else(|mut e| { | |
28 | e.emit(); | |
29 | self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); | |
30 | None | |
31 | }) | |
32 | } | |
33 | ||
34 | fn parse_stmt_without_recovery( | |
35 | &mut self, | |
36 | macro_legacy_warnings: bool, | |
37 | ) -> PResult<'a, Option<Stmt>> { | |
38 | maybe_whole!(self, NtStmt, |x| Some(x)); | |
39 | ||
40 | let attrs = self.parse_outer_attributes()?; | |
41 | let lo = self.token.span; | |
42 | ||
43 | Ok(Some(if self.eat_keyword(kw::Let) { | |
44 | Stmt { | |
e1599b0c | 45 | id: DUMMY_NODE_ID, |
e74abb32 | 46 | kind: StmtKind::Local(self.parse_local(attrs.into())?), |
416331ca XL |
47 | span: lo.to(self.prev_span), |
48 | } | |
49 | } else if let Some(macro_def) = self.eat_macro_def( | |
50 | &attrs, | |
51 | &respan(lo, VisibilityKind::Inherited), | |
52 | lo, | |
53 | )? { | |
54 | Stmt { | |
e1599b0c | 55 | id: DUMMY_NODE_ID, |
e74abb32 | 56 | kind: StmtKind::Item(macro_def), |
416331ca XL |
57 | span: lo.to(self.prev_span), |
58 | } | |
59 | // Starts like a simple path, being careful to avoid contextual keywords | |
60 | // such as a union items, item with `crate` visibility or auto trait items. | |
61 | // Our goal here is to parse an arbitrary path `a::b::c` but not something that starts | |
62 | // like a path (1 token), but it fact not a path. | |
63 | // `union::b::c` - path, `union U { ... }` - not a path. | |
64 | // `crate::b::c` - path, `crate struct S;` - not a path. | |
65 | } else if self.token.is_path_start() && | |
66 | !self.token.is_qpath_start() && | |
67 | !self.is_union_item() && | |
68 | !self.is_crate_vis() && | |
69 | !self.is_auto_trait_item() && | |
70 | !self.is_async_fn() { | |
71 | let path = self.parse_path(PathStyle::Expr)?; | |
72 | ||
73 | if !self.eat(&token::Not) { | |
74 | let expr = if self.check(&token::OpenDelim(token::Brace)) { | |
75 | self.parse_struct_expr(lo, path, ThinVec::new())? | |
76 | } else { | |
77 | let hi = self.prev_span; | |
78 | self.mk_expr(lo.to(hi), ExprKind::Path(None, path), ThinVec::new()) | |
79 | }; | |
80 | ||
81 | let expr = self.with_res(Restrictions::STMT_EXPR, |this| { | |
82 | let expr = this.parse_dot_or_call_expr_with(expr, lo, attrs.into())?; | |
83 | this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr)) | |
84 | })?; | |
85 | ||
86 | return Ok(Some(Stmt { | |
e1599b0c | 87 | id: DUMMY_NODE_ID, |
e74abb32 | 88 | kind: StmtKind::Expr(expr), |
416331ca XL |
89 | span: lo.to(self.prev_span), |
90 | })); | |
91 | } | |
92 | ||
93 | let (delim, tts) = self.expect_delimited_token_tree()?; | |
94 | let hi = self.prev_span; | |
95 | ||
96 | let style = if delim == MacDelimiter::Brace { | |
97 | MacStmtStyle::Braces | |
98 | } else { | |
99 | MacStmtStyle::NoBraces | |
100 | }; | |
101 | ||
e1599b0c | 102 | let mac = Mac { |
416331ca XL |
103 | path, |
104 | tts, | |
105 | delim, | |
e1599b0c | 106 | span: lo.to(hi), |
416331ca | 107 | prior_type_ascription: self.last_type_ascription, |
e1599b0c | 108 | }; |
e74abb32 | 109 | let kind = if delim == MacDelimiter::Brace || |
416331ca XL |
110 | self.token == token::Semi || self.token == token::Eof { |
111 | StmtKind::Mac(P((mac, style, attrs.into()))) | |
112 | } | |
113 | // We used to incorrectly stop parsing macro-expanded statements here. | |
114 | // If the next token will be an error anyway but could have parsed with the | |
115 | // earlier behavior, stop parsing here and emit a warning to avoid breakage. | |
e1599b0c XL |
116 | else if macro_legacy_warnings && self.token.can_begin_expr() && |
117 | match self.token.kind { | |
118 | // These can continue an expression, so we can't stop parsing and warn. | |
119 | token::OpenDelim(token::Paren) | token::OpenDelim(token::Bracket) | | |
120 | token::BinOp(token::Minus) | token::BinOp(token::Star) | | |
121 | token::BinOp(token::And) | token::BinOp(token::Or) | | |
122 | token::AndAnd | token::OrOr | | |
123 | token::DotDot | token::DotDotDot | token::DotDotEq => false, | |
124 | _ => true, | |
125 | } | |
126 | { | |
416331ca XL |
127 | self.warn_missing_semicolon(); |
128 | StmtKind::Mac(P((mac, style, attrs.into()))) | |
129 | } else { | |
130 | let e = self.mk_expr(mac.span, ExprKind::Mac(mac), ThinVec::new()); | |
131 | let e = self.maybe_recover_from_bad_qpath(e, true)?; | |
132 | let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?; | |
133 | let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?; | |
134 | StmtKind::Expr(e) | |
135 | }; | |
136 | Stmt { | |
e1599b0c | 137 | id: DUMMY_NODE_ID, |
416331ca | 138 | span: lo.to(hi), |
e74abb32 | 139 | kind, |
416331ca XL |
140 | } |
141 | } else { | |
142 | // FIXME: Bad copy of attrs | |
143 | let old_directory_ownership = | |
144 | mem::replace(&mut self.directory.ownership, DirectoryOwnership::UnownedViaBlock); | |
145 | let item = self.parse_item_(attrs.clone(), false, true)?; | |
146 | self.directory.ownership = old_directory_ownership; | |
147 | ||
148 | match item { | |
149 | Some(i) => Stmt { | |
e1599b0c | 150 | id: DUMMY_NODE_ID, |
416331ca | 151 | span: lo.to(i.span), |
e74abb32 | 152 | kind: StmtKind::Item(i), |
416331ca XL |
153 | }, |
154 | None => { | |
155 | let unused_attrs = |attrs: &[Attribute], s: &mut Self| { | |
156 | if !attrs.is_empty() { | |
157 | if s.prev_token_kind == PrevTokenKind::DocComment { | |
158 | s.span_fatal_err(s.prev_span, Error::UselessDocComment).emit(); | |
159 | } else if attrs.iter().any(|a| a.style == AttrStyle::Outer) { | |
160 | s.span_err( | |
161 | s.token.span, "expected statement after outer attribute" | |
162 | ); | |
163 | } | |
164 | } | |
165 | }; | |
166 | ||
167 | // Do not attempt to parse an expression if we're done here. | |
168 | if self.token == token::Semi { | |
169 | unused_attrs(&attrs, self); | |
170 | self.bump(); | |
e1599b0c XL |
171 | let mut last_semi = lo; |
172 | while self.token == token::Semi { | |
173 | last_semi = self.token.span; | |
174 | self.bump(); | |
175 | } | |
176 | // We are encoding a string of semicolons as an | |
177 | // an empty tuple that spans the excess semicolons | |
178 | // to preserve this info until the lint stage | |
179 | return Ok(Some(Stmt { | |
180 | id: DUMMY_NODE_ID, | |
181 | span: lo.to(last_semi), | |
e74abb32 | 182 | kind: StmtKind::Semi(self.mk_expr(lo.to(last_semi), |
e1599b0c XL |
183 | ExprKind::Tup(Vec::new()), |
184 | ThinVec::new() | |
185 | )), | |
186 | })); | |
416331ca XL |
187 | } |
188 | ||
189 | if self.token == token::CloseDelim(token::Brace) { | |
190 | unused_attrs(&attrs, self); | |
191 | return Ok(None); | |
192 | } | |
193 | ||
194 | // Remainder are line-expr stmts. | |
195 | let e = self.parse_expr_res( | |
196 | Restrictions::STMT_EXPR, Some(attrs.into()))?; | |
197 | Stmt { | |
e1599b0c | 198 | id: DUMMY_NODE_ID, |
416331ca | 199 | span: lo.to(e.span), |
e74abb32 | 200 | kind: StmtKind::Expr(e), |
416331ca XL |
201 | } |
202 | } | |
203 | } | |
204 | })) | |
205 | } | |
206 | ||
207 | /// Parses a local variable declaration. | |
208 | fn parse_local(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Local>> { | |
209 | let lo = self.prev_span; | |
e1599b0c | 210 | let pat = self.parse_top_pat(GateOr::Yes)?; |
416331ca XL |
211 | |
212 | let (err, ty) = if self.eat(&token::Colon) { | |
213 | // Save the state of the parser before parsing type normally, in case there is a `:` | |
214 | // instead of an `=` typo. | |
215 | let parser_snapshot_before_type = self.clone(); | |
216 | let colon_sp = self.prev_span; | |
217 | match self.parse_ty() { | |
218 | Ok(ty) => (None, Some(ty)), | |
219 | Err(mut err) => { | |
e1599b0c | 220 | // Rewind to before attempting to parse the type and continue parsing. |
416331ca XL |
221 | let parser_snapshot_after_type = self.clone(); |
222 | mem::replace(self, parser_snapshot_before_type); | |
223 | ||
224 | let snippet = self.span_to_snippet(pat.span).unwrap(); | |
225 | err.span_label(pat.span, format!("while parsing the type for `{}`", snippet)); | |
226 | (Some((parser_snapshot_after_type, colon_sp, err)), None) | |
227 | } | |
228 | } | |
229 | } else { | |
230 | (None, None) | |
231 | }; | |
232 | let init = match (self.parse_initializer(err.is_some()), err) { | |
233 | (Ok(init), None) => { // init parsed, ty parsed | |
234 | init | |
235 | } | |
236 | (Ok(init), Some((_, colon_sp, mut err))) => { // init parsed, ty error | |
237 | // Could parse the type as if it were the initializer, it is likely there was a | |
238 | // typo in the code: `:` instead of `=`. Add suggestion and emit the error. | |
239 | err.span_suggestion_short( | |
240 | colon_sp, | |
241 | "use `=` if you meant to assign", | |
e74abb32 | 242 | " =".to_string(), |
416331ca XL |
243 | Applicability::MachineApplicable |
244 | ); | |
245 | err.emit(); | |
246 | // As this was parsed successfully, continue as if the code has been fixed for the | |
247 | // rest of the file. It will still fail due to the emitted error, but we avoid | |
248 | // extra noise. | |
249 | init | |
250 | } | |
251 | (Err(mut init_err), Some((snapshot, _, ty_err))) => { // init error, ty error | |
252 | init_err.cancel(); | |
253 | // Couldn't parse the type nor the initializer, only raise the type error and | |
254 | // return to the parser state before parsing the type as the initializer. | |
255 | // let x: <parse_error>; | |
256 | mem::replace(self, snapshot); | |
257 | return Err(ty_err); | |
258 | } | |
259 | (Err(err), None) => { // init error, ty parsed | |
260 | // Couldn't parse the initializer and we're not attempting to recover a failed | |
261 | // parse of the type, return the error. | |
262 | return Err(err); | |
263 | } | |
264 | }; | |
265 | let hi = if self.token == token::Semi { | |
266 | self.token.span | |
267 | } else { | |
268 | self.prev_span | |
269 | }; | |
270 | Ok(P(ast::Local { | |
271 | ty, | |
272 | pat, | |
273 | init, | |
e1599b0c | 274 | id: DUMMY_NODE_ID, |
416331ca XL |
275 | span: lo.to(hi), |
276 | attrs, | |
277 | })) | |
278 | } | |
279 | ||
280 | /// Parses the RHS of a local variable declaration (e.g., '= 14;'). | |
281 | fn parse_initializer(&mut self, skip_eq: bool) -> PResult<'a, Option<P<Expr>>> { | |
282 | if self.eat(&token::Eq) { | |
283 | Ok(Some(self.parse_expr()?)) | |
284 | } else if skip_eq { | |
285 | Ok(Some(self.parse_expr()?)) | |
286 | } else { | |
287 | Ok(None) | |
288 | } | |
289 | } | |
290 | ||
291 | fn is_auto_trait_item(&self) -> bool { | |
292 | // auto trait | |
293 | (self.token.is_keyword(kw::Auto) && | |
294 | self.is_keyword_ahead(1, &[kw::Trait])) | |
295 | || // unsafe auto trait | |
296 | (self.token.is_keyword(kw::Unsafe) && | |
297 | self.is_keyword_ahead(1, &[kw::Auto]) && | |
298 | self.is_keyword_ahead(2, &[kw::Trait])) | |
299 | } | |
300 | ||
301 | /// Parses a block. No inner attributes are allowed. | |
302 | pub fn parse_block(&mut self) -> PResult<'a, P<Block>> { | |
303 | maybe_whole!(self, NtBlock, |x| x); | |
304 | ||
305 | let lo = self.token.span; | |
306 | ||
307 | if !self.eat(&token::OpenDelim(token::Brace)) { | |
308 | let sp = self.token.span; | |
309 | let tok = self.this_token_descr(); | |
310 | let mut e = self.span_fatal(sp, &format!("expected `{{`, found {}", tok)); | |
311 | let do_not_suggest_help = | |
312 | self.token.is_keyword(kw::In) || self.token == token::Colon; | |
313 | ||
314 | if self.token.is_ident_named(sym::and) { | |
315 | e.span_suggestion_short( | |
316 | self.token.span, | |
317 | "use `&&` instead of `and` for the boolean operator", | |
318 | "&&".to_string(), | |
319 | Applicability::MaybeIncorrect, | |
320 | ); | |
321 | } | |
322 | if self.token.is_ident_named(sym::or) { | |
323 | e.span_suggestion_short( | |
324 | self.token.span, | |
325 | "use `||` instead of `or` for the boolean operator", | |
326 | "||".to_string(), | |
327 | Applicability::MaybeIncorrect, | |
328 | ); | |
329 | } | |
330 | ||
331 | // Check to see if the user has written something like | |
332 | // | |
333 | // if (cond) | |
334 | // bar; | |
335 | // | |
e1599b0c | 336 | // which is valid in other languages, but not Rust. |
416331ca XL |
337 | match self.parse_stmt_without_recovery(false) { |
338 | Ok(Some(stmt)) => { | |
339 | if self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace)) | |
340 | || do_not_suggest_help { | |
e1599b0c XL |
341 | // If the next token is an open brace (e.g., `if a b {`), the place- |
342 | // inside-a-block suggestion would be more likely wrong than right. | |
416331ca XL |
343 | e.span_label(sp, "expected `{`"); |
344 | return Err(e); | |
345 | } | |
346 | let mut stmt_span = stmt.span; | |
e1599b0c | 347 | // Expand the span to include the semicolon, if it exists. |
416331ca XL |
348 | if self.eat(&token::Semi) { |
349 | stmt_span = stmt_span.with_hi(self.prev_span.hi()); | |
350 | } | |
351 | if let Ok(snippet) = self.span_to_snippet(stmt_span) { | |
352 | e.span_suggestion( | |
353 | stmt_span, | |
354 | "try placing this code inside a block", | |
355 | format!("{{ {} }}", snippet), | |
e1599b0c | 356 | // Speculative; has been misleading in the past (#46836). |
416331ca XL |
357 | Applicability::MaybeIncorrect, |
358 | ); | |
359 | } | |
360 | } | |
361 | Err(mut e) => { | |
362 | self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); | |
e1599b0c | 363 | e.cancel(); |
416331ca XL |
364 | } |
365 | _ => () | |
366 | } | |
367 | e.span_label(sp, "expected `{`"); | |
368 | return Err(e); | |
369 | } | |
370 | ||
371 | self.parse_block_tail(lo, BlockCheckMode::Default) | |
372 | } | |
373 | ||
374 | /// Parses a block. Inner attributes are allowed. | |
e74abb32 XL |
375 | pub(super) fn parse_inner_attrs_and_block( |
376 | &mut self | |
377 | ) -> PResult<'a, (Vec<Attribute>, P<Block>)> { | |
416331ca XL |
378 | maybe_whole!(self, NtBlock, |x| (Vec::new(), x)); |
379 | ||
380 | let lo = self.token.span; | |
381 | self.expect(&token::OpenDelim(token::Brace))?; | |
382 | Ok((self.parse_inner_attributes()?, | |
383 | self.parse_block_tail(lo, BlockCheckMode::Default)?)) | |
384 | } | |
385 | ||
386 | /// Parses the rest of a block expression or function body. | |
387 | /// Precondition: already parsed the '{'. | |
388 | pub(super) fn parse_block_tail( | |
389 | &mut self, | |
390 | lo: Span, | |
391 | s: BlockCheckMode | |
392 | ) -> PResult<'a, P<Block>> { | |
393 | let mut stmts = vec![]; | |
394 | while !self.eat(&token::CloseDelim(token::Brace)) { | |
395 | if self.token == token::Eof { | |
396 | break; | |
397 | } | |
398 | let stmt = match self.parse_full_stmt(false) { | |
399 | Err(mut err) => { | |
400 | err.emit(); | |
401 | self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore); | |
402 | Some(Stmt { | |
e1599b0c | 403 | id: DUMMY_NODE_ID, |
e74abb32 | 404 | kind: StmtKind::Expr(self.mk_expr_err(self.token.span)), |
416331ca XL |
405 | span: self.token.span, |
406 | }) | |
407 | } | |
408 | Ok(stmt) => stmt, | |
409 | }; | |
410 | if let Some(stmt) = stmt { | |
411 | stmts.push(stmt); | |
412 | } else { | |
413 | // Found only `;` or `}`. | |
414 | continue; | |
415 | }; | |
416 | } | |
417 | Ok(P(ast::Block { | |
418 | stmts, | |
e1599b0c | 419 | id: DUMMY_NODE_ID, |
416331ca XL |
420 | rules: s, |
421 | span: lo.to(self.prev_span), | |
422 | })) | |
423 | } | |
424 | ||
425 | /// Parses a statement, including the trailing semicolon. | |
e74abb32 | 426 | pub fn parse_full_stmt(&mut self, macro_legacy_warnings: bool) -> PResult<'a, Option<Stmt>> { |
e1599b0c | 427 | // Skip looking for a trailing semicolon when we have an interpolated statement. |
416331ca XL |
428 | maybe_whole!(self, NtStmt, |x| Some(x)); |
429 | ||
430 | let mut stmt = match self.parse_stmt_without_recovery(macro_legacy_warnings)? { | |
431 | Some(stmt) => stmt, | |
432 | None => return Ok(None), | |
433 | }; | |
434 | ||
e74abb32 XL |
435 | let mut eat_semi = true; |
436 | match stmt.kind { | |
416331ca XL |
437 | StmtKind::Expr(ref expr) if self.token != token::Eof => { |
438 | // expression without semicolon | |
439 | if classify::expr_requires_semi_to_be_stmt(expr) { | |
440 | // Just check for errors and recover; do not eat semicolon yet. | |
441 | if let Err(mut e) = | |
442 | self.expect_one_of(&[], &[token::Semi, token::CloseDelim(token::Brace)]) | |
443 | { | |
444 | e.emit(); | |
445 | self.recover_stmt(); | |
446 | // Don't complain about type errors in body tail after parse error (#57383). | |
447 | let sp = expr.span.to(self.prev_span); | |
e74abb32 | 448 | stmt.kind = StmtKind::Expr(self.mk_expr_err(sp)); |
416331ca XL |
449 | } |
450 | } | |
451 | } | |
452 | StmtKind::Local(..) => { | |
453 | // We used to incorrectly allow a macro-expanded let statement to lack a semicolon. | |
454 | if macro_legacy_warnings && self.token != token::Semi { | |
455 | self.warn_missing_semicolon(); | |
456 | } else { | |
e74abb32 XL |
457 | self.expect_semi()?; |
458 | eat_semi = false; | |
416331ca XL |
459 | } |
460 | } | |
461 | _ => {} | |
462 | } | |
463 | ||
e74abb32 | 464 | if eat_semi && self.eat(&token::Semi) { |
416331ca XL |
465 | stmt = stmt.add_trailing_semicolon(); |
466 | } | |
467 | stmt.span = stmt.span.to(self.prev_span); | |
468 | Ok(Some(stmt)) | |
469 | } | |
470 | ||
471 | fn warn_missing_semicolon(&self) { | |
472 | self.diagnostic().struct_span_warn(self.token.span, { | |
473 | &format!("expected `;`, found {}", self.this_token_descr()) | |
474 | }).note({ | |
e1599b0c | 475 | "this was erroneously allowed and will become a hard error in a future release" |
416331ca XL |
476 | }).emit(); |
477 | } | |
478 | } |