3 BlockCheckMode, Expr, ExprKind, Item, ItemKind, Pat, PatKind, QSelf, Ty, TyKind, VariantData,
5 use crate::parse::parser::{BlockMode, PathStyle, SemiColonMode, TokenType};
6 use crate::parse::token;
7 use crate::parse::PResult;
8 use crate::parse::Parser;
9 use crate::print::pprust;
11 use crate::source_map::Spanned;
12 use crate::symbol::kw;
14 use errors::{Applicability, DiagnosticBuilder};
18 pub trait RecoverQPath: Sized + 'static {
19 const PATH_STYLE: PathStyle = PathStyle::Expr;
20 fn to_ty(&self) -> Option<P<Ty>>;
21 fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self;
24 impl RecoverQPath for Ty {
25 const PATH_STYLE: PathStyle = PathStyle::Type;
26 fn to_ty(&self) -> Option<P<Ty>> {
29 fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
32 node: TyKind::Path(qself, path),
33 id: ast::DUMMY_NODE_ID,
38 impl RecoverQPath for Pat {
39 fn to_ty(&self) -> Option<P<Ty>> {
42 fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
45 node: PatKind::Path(qself, path),
46 id: ast::DUMMY_NODE_ID,
51 impl RecoverQPath for Expr {
52 fn to_ty(&self) -> Option<P<Ty>> {
55 fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
58 node: ExprKind::Path(qself, path),
59 attrs: ThinVec::new(),
60 id: ast::DUMMY_NODE_ID,
66 crate fn maybe_report_ambiguous_plus(
72 if !allow_plus && impl_dyn_multi {
73 let sum_with_parens = format!("({})", pprust::ty_to_string(&ty));
74 self.struct_span_err(ty.span, "ambiguous `+` in a type")
77 "use parentheses to disambiguate",
79 Applicability::MachineApplicable,
85 crate fn maybe_report_invalid_custom_discriminants(
87 discriminant_spans: Vec<Span>,
88 variants: &[Spanned<ast::Variant_>],
90 let has_fields = variants.iter().any(|variant| match variant.node.data {
91 VariantData::Tuple(..) | VariantData::Struct(..) => true,
92 VariantData::Unit(..) => false,
95 if !discriminant_spans.is_empty() && has_fields {
96 let mut err = self.struct_span_err(
97 discriminant_spans.clone(),
98 "custom discriminant values are not allowed in enums with fields",
100 for sp in discriminant_spans {
101 err.span_label(sp, "invalid custom discriminant");
103 for variant in variants.iter() {
104 if let VariantData::Struct(fields, ..) | VariantData::Tuple(fields, ..) =
107 let fields = if fields.len() > 1 {
114 &format!("variant with {fields} defined here", fields = fields),
123 crate fn maybe_recover_from_bad_type_plus(
127 ) -> PResult<'a, ()> {
128 // Do not add `+` to expected tokens.
129 if !allow_plus || !self.token.is_like_plus() {
134 let bounds = self.parse_generic_bounds(None)?;
135 let sum_span = ty.span.to(self.prev_span);
137 let mut err = struct_span_err!(
138 self.sess.span_diagnostic,
141 "expected a path on the left-hand side of `+`, not `{}`",
142 pprust::ty_to_string(ty)
146 TyKind::Rptr(ref lifetime, ref mut_ty) => {
147 let sum_with_parens = pprust::to_string(|s| {
148 use crate::print::pprust::PrintState;
151 s.print_opt_lifetime(lifetime)?;
152 s.print_mutability(mut_ty.mutbl)?;
154 s.print_type(&mut_ty.ty)?;
155 s.print_type_bounds(" +", &bounds)?;
160 "try adding parentheses",
162 Applicability::MachineApplicable,
165 TyKind::Ptr(..) | TyKind::BareFn(..) => {
166 err.span_label(sum_span, "perhaps you forgot parentheses?");
169 err.span_label(sum_span, "expected a path");
176 /// Try to recover from associated item paths like `[T]::AssocItem`/`(T, U)::AssocItem`.
177 /// Attempt to convert the base expression/pattern/type into a type, parse the `::AssocItem`
178 /// tail, and combine them into a `<Ty>::AssocItem` expression/pattern/type.
179 crate fn maybe_recover_from_bad_qpath<T: RecoverQPath>(
182 allow_recovery: bool,
183 ) -> PResult<'a, P<T>> {
184 // Do not add `::` to expected tokens.
185 if allow_recovery && self.token == token::ModSep {
186 if let Some(ty) = base.to_ty() {
187 return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty);
193 /// Given an already parsed `Ty` parse the `::AssocItem` tail and
194 /// combine them into a `<Ty>::AssocItem` expression/pattern/type.
195 crate fn maybe_recover_from_bad_qpath_stage_2<T: RecoverQPath>(
199 ) -> PResult<'a, P<T>> {
200 self.expect(&token::ModSep)?;
202 let mut path = ast::Path {
203 segments: Vec::new(),
204 span: syntax_pos::DUMMY_SP,
206 self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?;
207 path.span = ty_span.to(self.prev_span);
212 .span_to_snippet(ty_span)
213 .unwrap_or_else(|_| pprust::ty_to_string(&ty));
215 .struct_span_err(path.span, "missing angle brackets in associated item path")
217 // this is a best-effort recovery
220 format!("<{}>::{}", ty_str, path),
221 Applicability::MaybeIncorrect,
225 let path_span = ty_span.shrink_to_hi(); // use an empty path since `position` == 0
236 crate fn maybe_consume_incorrect_semicolon(&mut self, items: &[P<Item>]) -> bool {
237 if self.eat(&token::Semi) {
238 let mut err = self.struct_span_err(self.prev_span, "expected item, found `;`");
239 err.span_suggestion_short(
241 "remove this semicolon",
243 Applicability::MachineApplicable,
245 if !items.is_empty() {
246 let previous_item = &items[items.len() - 1];
247 let previous_item_kind_name = match previous_item.node {
248 // say "braced struct" because tuple-structs and
249 // braceless-empty-struct declarations do take a semicolon
250 ItemKind::Struct(..) => Some("braced struct"),
251 ItemKind::Enum(..) => Some("enum"),
252 ItemKind::Trait(..) => Some("trait"),
253 ItemKind::Union(..) => Some("union"),
256 if let Some(name) = previous_item_kind_name {
258 "{} declarations are not followed by a semicolon",
270 /// Consume alternative await syntaxes like `await <expr>`, `await? <expr>`, `await(<expr>)`
271 /// and `await { <expr> }`.
272 crate fn parse_incorrect_await_syntax(
276 ) -> PResult<'a, (Span, ExprKind)> {
277 let is_question = self.eat(&token::Question); // Handle `await? <expr>`.
278 let expr = if self.token == token::OpenDelim(token::Brace) {
279 // Handle `await { <expr> }`.
280 // This needs to be handled separatedly from the next arm to avoid
281 // interpreting `await { <expr> }?` as `<expr>?.await`.
282 self.parse_block_expr(
285 BlockCheckMode::Default,
290 }.map_err(|mut err| {
291 err.span_label(await_sp, "while parsing this incorrect await expression");
294 let expr_str = self.sess.source_map().span_to_snippet(expr.span)
295 .unwrap_or_else(|_| pprust::expr_to_string(&expr));
296 let suggestion = format!("{}.await{}", expr_str, if is_question { "?" } else { "" });
297 let sp = lo.to(expr.span);
298 let app = match expr.node {
299 ExprKind::Try(_) => Applicability::MaybeIncorrect, // `await <expr>?`
300 _ => Applicability::MachineApplicable,
302 self.struct_span_err(sp, "incorrect use of `await`")
303 .span_suggestion(sp, "`await` is a postfix operation", suggestion, app)
305 Ok((sp, ExprKind::Await(ast::AwaitOrigin::FieldLike, expr)))
308 /// If encountering `future.await()`, consume and emit error.
309 crate fn recover_from_await_method_call(&mut self) {
310 if self.token == token::OpenDelim(token::Paren) &&
311 self.look_ahead(1, |t| t == &token::CloseDelim(token::Paren))
316 let sp = lo.to(self.span);
318 self.struct_span_err(sp, "incorrect use of `await`")
321 "`await` is not a method call, remove the parentheses",
323 Applicability::MachineApplicable,
328 crate fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool {
329 self.token.is_ident() &&
330 if let ast::ExprKind::Path(..) = node { true } else { false } &&
331 !self.token.is_reserved_ident() && // v `foo:bar(baz)`
332 self.look_ahead(1, |t| t == &token::OpenDelim(token::Paren)) ||
333 self.look_ahead(1, |t| t == &token::Lt) && // `foo:bar<baz`
334 self.look_ahead(2, |t| t.is_ident()) ||
335 self.look_ahead(1, |t| t == &token::Colon) && // `foo:bar:baz`
336 self.look_ahead(2, |t| t.is_ident()) ||
337 self.look_ahead(1, |t| t == &token::ModSep) && // `foo:bar::baz`
338 self.look_ahead(2, |t| t.is_ident())
341 crate fn bad_type_ascription(
343 err: &mut DiagnosticBuilder<'a>,
349 err.span_label(self.span, "expecting a type here because of type ascription");
350 let cm = self.sess.source_map();
351 let next_pos = cm.lookup_char_pos(next_sp.lo());
352 let op_pos = cm.lookup_char_pos(cur_op_span.hi());
353 if op_pos.line != next_pos.line {
356 "try using a semicolon",
358 Applicability::MaybeIncorrect,
364 "maybe you meant to write a path separator here",
366 Applicability::MaybeIncorrect,
369 err.note("type ascription is a nightly-only feature that lets \
370 you annotate an expression with a type: `<expr>: <type>`")
373 "this expression expects an ascribed type after the colon",
375 .help("this might be indicative of a syntax error elsewhere");
380 crate fn recover_seq_parse_error(
382 delim: token::DelimToken,
384 result: PResult<'a, P<Expr>>,
390 // recover from parse error
391 self.consume_block(delim);
392 self.mk_expr(lo.to(self.prev_span), ExprKind::Err, ThinVec::new())
397 crate fn recover_closing_delimiter(
399 tokens: &[token::Token],
400 mut err: DiagnosticBuilder<'a>,
401 ) -> PResult<'a, bool> {
403 // we want to use the last closing delim that would apply
404 for (i, unmatched) in self.unclosed_delims.iter().enumerate().rev() {
405 if tokens.contains(&token::CloseDelim(unmatched.expected_delim))
406 && Some(self.span) > unmatched.unclosed_span
413 // Recover and assume that the detected unclosed delimiter was meant for
414 // this location. Emit the diagnostic and act as if the delimiter was
415 // present for the parser's sake.
417 // Don't attempt to recover from this unclosed delimiter more than once.
418 let unmatched = self.unclosed_delims.remove(pos);
419 let delim = TokenType::Token(token::CloseDelim(unmatched.expected_delim));
421 // We want to suggest the inclusion of the closing delimiter where it makes
422 // the most sense, which is immediately after the last token:
427 // | help: `)` may belong here (FIXME: #58270)
429 // unclosed delimiter
430 if let Some(sp) = unmatched.unclosed_span {
431 err.span_label(sp, "unclosed delimiter");
433 err.span_suggestion_short(
434 self.sess.source_map().next_point(self.prev_span),
435 &format!("{} may belong here", delim.to_string()),
437 Applicability::MaybeIncorrect,
440 self.expected_tokens.clear(); // reduce errors
447 /// Recover from `pub` keyword in places where it seems _reasonable_ but isn't valid.
448 crate fn eat_bad_pub(&mut self) {
449 if self.token.is_keyword(kw::Pub) {
450 match self.parse_visibility(false) {
453 .struct_span_err(vis.span, "unnecessary visibility qualifier")
454 .span_label(vis.span, "`pub` not permitted here")
457 Err(mut err) => err.emit(),
462 // Eat tokens until we can be relatively sure we reached the end of the
463 // statement. This is something of a best-effort heuristic.
465 // We terminate when we find an unmatched `}` (without consuming it).
466 crate fn recover_stmt(&mut self) {
467 self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore)
470 // If `break_on_semi` is `Break`, then we will stop consuming tokens after
471 // finding (and consuming) a `;` outside of `{}` or `[]` (note that this is
472 // approximate - it can mean we break too early due to macros, but that
473 // should only lead to sub-optimal recovery, not inaccurate parsing).
475 // If `break_on_block` is `Break`, then we will stop consuming tokens
476 // after finding (and consuming) a brace-delimited block.
477 crate fn recover_stmt_(&mut self, break_on_semi: SemiColonMode, break_on_block: BlockMode) {
478 let mut brace_depth = 0;
479 let mut bracket_depth = 0;
480 let mut in_block = false;
481 debug!("recover_stmt_ enter loop (semi={:?}, block={:?})",
482 break_on_semi, break_on_block);
484 debug!("recover_stmt_ loop {:?}", self.token);
486 token::OpenDelim(token::DelimToken::Brace) => {
489 if break_on_block == BlockMode::Break &&
495 token::OpenDelim(token::DelimToken::Bracket) => {
499 token::CloseDelim(token::DelimToken::Brace) => {
500 if brace_depth == 0 {
501 debug!("recover_stmt_ return - close delim {:?}", self.token);
506 if in_block && bracket_depth == 0 && brace_depth == 0 {
507 debug!("recover_stmt_ return - block end {:?}", self.token);
511 token::CloseDelim(token::DelimToken::Bracket) => {
513 if bracket_depth < 0 {
519 debug!("recover_stmt_ return - Eof");
524 if break_on_semi == SemiColonMode::Break &&
527 debug!("recover_stmt_ return - Semi");
531 token::Comma if break_on_semi == SemiColonMode::Comma &&
533 bracket_depth == 0 =>
535 debug!("recover_stmt_ return - Semi");
545 crate fn consume_block(&mut self, delim: token::DelimToken) {
546 let mut brace_depth = 0;
548 if self.eat(&token::OpenDelim(delim)) {
550 } else if self.eat(&token::CloseDelim(delim)) {
551 if brace_depth == 0 {
557 } else if self.token == token::Eof || self.eat(&token::CloseDelim(token::NoDelim)) {