]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/parse/diagnostics.rs
Rollup merge of #61121 - RalfJung:miri-value-printing, r=oli-obk
[rust.git] / src / libsyntax / parse / diagnostics.rs
1 use crate::ast;
2 use crate::ast::{
3     BlockCheckMode, Expr, ExprKind, Item, ItemKind, Pat, PatKind, QSelf, Ty, TyKind, VariantData,
4 };
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;
10 use crate::ptr::P;
11 use crate::source_map::Spanned;
12 use crate::symbol::kw;
13 use crate::ThinVec;
14 use errors::{Applicability, DiagnosticBuilder};
15 use log::debug;
16 use syntax_pos::{Span, DUMMY_SP};
17
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;
22 }
23
24 impl RecoverQPath for Ty {
25     const PATH_STYLE: PathStyle = PathStyle::Type;
26     fn to_ty(&self) -> Option<P<Ty>> {
27         Some(P(self.clone()))
28     }
29     fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
30         Self {
31             span: path.span,
32             node: TyKind::Path(qself, path),
33             id: ast::DUMMY_NODE_ID,
34         }
35     }
36 }
37
38 impl RecoverQPath for Pat {
39     fn to_ty(&self) -> Option<P<Ty>> {
40         self.to_ty()
41     }
42     fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
43         Self {
44             span: path.span,
45             node: PatKind::Path(qself, path),
46             id: ast::DUMMY_NODE_ID,
47         }
48     }
49 }
50
51 impl RecoverQPath for Expr {
52     fn to_ty(&self) -> Option<P<Ty>> {
53         self.to_ty()
54     }
55     fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
56         Self {
57             span: path.span,
58             node: ExprKind::Path(qself, path),
59             attrs: ThinVec::new(),
60             id: ast::DUMMY_NODE_ID,
61         }
62     }
63 }
64
65 impl<'a> Parser<'a> {
66     crate fn maybe_report_ambiguous_plus(
67         &mut self,
68         allow_plus: bool,
69         impl_dyn_multi: bool,
70         ty: &Ty,
71     ) {
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")
75                 .span_suggestion(
76                     ty.span,
77                     "use parentheses to disambiguate",
78                     sum_with_parens,
79                     Applicability::MachineApplicable,
80                 )
81                 .emit();
82         }
83     }
84
85     crate fn maybe_report_invalid_custom_discriminants(
86         &mut self,
87         discriminant_spans: Vec<Span>,
88         variants: &[Spanned<ast::Variant_>],
89     ) {
90         let has_fields = variants.iter().any(|variant| match variant.node.data {
91             VariantData::Tuple(..) | VariantData::Struct(..) => true,
92             VariantData::Unit(..) => false,
93         });
94
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",
99             );
100             for sp in discriminant_spans {
101                 err.span_label(sp, "invalid custom discriminant");
102             }
103             for variant in variants.iter() {
104                 if let VariantData::Struct(fields, ..) | VariantData::Tuple(fields, ..) =
105                     &variant.node.data
106                 {
107                     let fields = if fields.len() > 1 {
108                         "fields"
109                     } else {
110                         "a field"
111                     };
112                     err.span_label(
113                         variant.span,
114                         &format!("variant with {fields} defined here", fields = fields),
115                     );
116
117                 }
118             }
119             err.emit();
120         }
121     }
122
123     crate fn maybe_recover_from_bad_type_plus(
124         &mut self,
125         allow_plus: bool,
126         ty: &Ty,
127     ) -> PResult<'a, ()> {
128         // Do not add `+` to expected tokens.
129         if !allow_plus || !self.token.is_like_plus() {
130             return Ok(());
131         }
132
133         self.bump(); // `+`
134         let bounds = self.parse_generic_bounds(None)?;
135         let sum_span = ty.span.to(self.prev_span);
136
137         let mut err = struct_span_err!(
138             self.sess.span_diagnostic,
139             sum_span,
140             E0178,
141             "expected a path on the left-hand side of `+`, not `{}`",
142             pprust::ty_to_string(ty)
143         );
144
145         match ty.node {
146             TyKind::Rptr(ref lifetime, ref mut_ty) => {
147                 let sum_with_parens = pprust::to_string(|s| {
148                     use crate::print::pprust::PrintState;
149
150                     s.s.word("&")?;
151                     s.print_opt_lifetime(lifetime)?;
152                     s.print_mutability(mut_ty.mutbl)?;
153                     s.popen()?;
154                     s.print_type(&mut_ty.ty)?;
155                     s.print_type_bounds(" +", &bounds)?;
156                     s.pclose()
157                 });
158                 err.span_suggestion(
159                     sum_span,
160                     "try adding parentheses",
161                     sum_with_parens,
162                     Applicability::MachineApplicable,
163                 );
164             }
165             TyKind::Ptr(..) | TyKind::BareFn(..) => {
166                 err.span_label(sum_span, "perhaps you forgot parentheses?");
167             }
168             _ => {
169                 err.span_label(sum_span, "expected a path");
170             }
171         }
172         err.emit();
173         Ok(())
174     }
175
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>(
180         &mut self,
181         base: P<T>,
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);
188             }
189         }
190         Ok(base)
191     }
192
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>(
196         &mut self,
197         ty_span: Span,
198         ty: P<Ty>,
199     ) -> PResult<'a, P<T>> {
200         self.expect(&token::ModSep)?;
201
202         let mut path = ast::Path {
203             segments: Vec::new(),
204             span: DUMMY_SP,
205         };
206         self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?;
207         path.span = ty_span.to(self.prev_span);
208
209         let ty_str = self
210             .sess
211             .source_map()
212             .span_to_snippet(ty_span)
213             .unwrap_or_else(|_| pprust::ty_to_string(&ty));
214         self.diagnostic()
215             .struct_span_err(path.span, "missing angle brackets in associated item path")
216             .span_suggestion(
217                 // this is a best-effort recovery
218                 path.span,
219                 "try",
220                 format!("<{}>::{}", ty_str, path),
221                 Applicability::MaybeIncorrect,
222             )
223             .emit();
224
225         let path_span = ty_span.shrink_to_hi(); // use an empty path since `position` == 0
226         Ok(P(T::recovered(
227             Some(QSelf {
228                 ty,
229                 path_span,
230                 position: 0,
231             }),
232             path,
233         )))
234     }
235
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(
240                 self.prev_span,
241                 "remove this semicolon",
242                 String::new(),
243                 Applicability::MachineApplicable,
244             );
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"),
254                     _ => None,
255                 };
256                 if let Some(name) = previous_item_kind_name {
257                     err.help(&format!(
258                         "{} declarations are not followed by a semicolon",
259                         name
260                     ));
261                 }
262             }
263             err.emit();
264             true
265         } else {
266             false
267         }
268     }
269
270     /// Create a `DiagnosticBuilder` for an unexpected token `t` and try to recover if it is a
271     /// closing delimiter.
272     pub fn unexpected_try_recover(
273         &mut self,
274         t: &token::Token,
275     ) -> PResult<'a, bool /* recovered */> {
276         let token_str = pprust::token_to_string(t);
277         let this_token_str = self.this_token_descr();
278         let (prev_sp, sp) = match (&self.token, self.subparser_name) {
279             // Point at the end of the macro call when reaching end of macro arguments.
280             (token::Token::Eof, Some(_)) => {
281                 let sp = self.sess.source_map().next_point(self.span);
282                 (sp, sp)
283             }
284             // We don't want to point at the following span after DUMMY_SP.
285             // This happens when the parser finds an empty TokenStream.
286             _ if self.prev_span == DUMMY_SP => (self.span, self.span),
287             // EOF, don't want to point at the following char, but rather the last token.
288             (token::Token::Eof, None) => (self.prev_span, self.span),
289             _ => (self.sess.source_map().next_point(self.prev_span), self.span),
290         };
291         let msg = format!(
292             "expected `{}`, found {}",
293             token_str,
294             match (&self.token, self.subparser_name) {
295                 (token::Token::Eof, Some(origin)) => format!("end of {}", origin),
296                 _ => this_token_str,
297             },
298         );
299         let mut err = self.struct_span_err(sp, &msg);
300         let label_exp = format!("expected `{}`", token_str);
301         match self.recover_closing_delimiter(&[t.clone()], err) {
302             Err(e) => err = e,
303             Ok(recovered) => {
304                 return Ok(recovered);
305             }
306         }
307         let cm = self.sess.source_map();
308         match (cm.lookup_line(prev_sp.lo()), cm.lookup_line(sp.lo())) {
309             (Ok(ref a), Ok(ref b)) if a.line == b.line => {
310                 // When the spans are in the same line, it means that the only content
311                 // between them is whitespace, point only at the found token.
312                 err.span_label(sp, label_exp);
313             }
314             _ => {
315                 err.span_label(prev_sp, label_exp);
316                 err.span_label(sp, "unexpected token");
317             }
318         }
319         Err(err)
320     }
321
322     /// Consume alternative await syntaxes like `await <expr>`, `await? <expr>`, `await(<expr>)`
323     /// and `await { <expr> }`.
324     crate fn parse_incorrect_await_syntax(
325         &mut self,
326         lo: Span,
327         await_sp: Span,
328     ) -> PResult<'a, (Span, ExprKind)> {
329         let is_question = self.eat(&token::Question); // Handle `await? <expr>`.
330         let expr = if self.token == token::OpenDelim(token::Brace) {
331             // Handle `await { <expr> }`.
332             // This needs to be handled separatedly from the next arm to avoid
333             // interpreting `await { <expr> }?` as `<expr>?.await`.
334             self.parse_block_expr(
335                 None,
336                 self.span,
337                 BlockCheckMode::Default,
338                 ThinVec::new(),
339             )
340         } else {
341             self.parse_expr()
342         }.map_err(|mut err| {
343             err.span_label(await_sp, "while parsing this incorrect await expression");
344             err
345         })?;
346         let expr_str = self.sess.source_map().span_to_snippet(expr.span)
347             .unwrap_or_else(|_| pprust::expr_to_string(&expr));
348         let suggestion = format!("{}.await{}", expr_str, if is_question { "?" } else { "" });
349         let sp = lo.to(expr.span);
350         let app = match expr.node {
351             ExprKind::Try(_) => Applicability::MaybeIncorrect, // `await <expr>?`
352             _ => Applicability::MachineApplicable,
353         };
354         self.struct_span_err(sp, "incorrect use of `await`")
355             .span_suggestion(sp, "`await` is a postfix operation", suggestion, app)
356             .emit();
357         Ok((sp, ExprKind::Await(ast::AwaitOrigin::FieldLike, expr)))
358     }
359
360     /// If encountering `future.await()`, consume and emit error.
361     crate fn recover_from_await_method_call(&mut self) {
362         if self.token == token::OpenDelim(token::Paren) &&
363             self.look_ahead(1, |t| t == &token::CloseDelim(token::Paren))
364         {
365             // future.await()
366             let lo = self.span;
367             self.bump(); // (
368             let sp = lo.to(self.span);
369             self.bump(); // )
370             self.struct_span_err(sp, "incorrect use of `await`")
371                 .span_suggestion(
372                     sp,
373                     "`await` is not a method call, remove the parentheses",
374                     String::new(),
375                     Applicability::MachineApplicable,
376                 ).emit()
377         }
378     }
379
380     crate fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool {
381         self.token.is_ident() &&
382             if let ast::ExprKind::Path(..) = node { true } else { false } &&
383             !self.token.is_reserved_ident() &&           // v `foo:bar(baz)`
384             self.look_ahead(1, |t| t == &token::OpenDelim(token::Paren)) ||
385             self.look_ahead(1, |t| t == &token::Lt) &&     // `foo:bar<baz`
386             self.look_ahead(2, |t| t.is_ident()) ||
387             self.look_ahead(1, |t| t == &token::Colon) &&  // `foo:bar:baz`
388             self.look_ahead(2, |t| t.is_ident()) ||
389             self.look_ahead(1, |t| t == &token::ModSep) &&  // `foo:bar::baz`
390             self.look_ahead(2, |t| t.is_ident())
391     }
392
393     crate fn bad_type_ascription(
394         &self,
395         err: &mut DiagnosticBuilder<'a>,
396         lhs_span: Span,
397         cur_op_span: Span,
398         next_sp: Span,
399         maybe_path: bool,
400     ) {
401         err.span_label(self.span, "expecting a type here because of type ascription");
402         let cm = self.sess.source_map();
403         let next_pos = cm.lookup_char_pos(next_sp.lo());
404         let op_pos = cm.lookup_char_pos(cur_op_span.hi());
405         if op_pos.line != next_pos.line {
406             err.span_suggestion(
407                 cur_op_span,
408                 "try using a semicolon",
409                 ";".to_string(),
410                 Applicability::MaybeIncorrect,
411             );
412         } else {
413             if maybe_path {
414                 err.span_suggestion(
415                     cur_op_span,
416                     "maybe you meant to write a path separator here",
417                     "::".to_string(),
418                     Applicability::MaybeIncorrect,
419                 );
420             } else {
421                 err.note("type ascription is a nightly-only feature that lets \
422                           you annotate an expression with a type: `<expr>: <type>`")
423                     .span_note(
424                         lhs_span,
425                         "this expression expects an ascribed type after the colon",
426                     )
427                     .help("this might be indicative of a syntax error elsewhere");
428             }
429         }
430     }
431
432     crate fn recover_seq_parse_error(
433         &mut self,
434         delim: token::DelimToken,
435         lo: Span,
436         result: PResult<'a, P<Expr>>,
437     ) -> P<Expr> {
438         match result {
439             Ok(x) => x,
440             Err(mut err) => {
441                 err.emit();
442                 // recover from parse error
443                 self.consume_block(delim);
444                 self.mk_expr(lo.to(self.prev_span), ExprKind::Err, ThinVec::new())
445             }
446         }
447     }
448
449     crate fn recover_closing_delimiter(
450         &mut self,
451         tokens: &[token::Token],
452         mut err: DiagnosticBuilder<'a>,
453     ) -> PResult<'a, bool> {
454         let mut pos = None;
455         // we want to use the last closing delim that would apply
456         for (i, unmatched) in self.unclosed_delims.iter().enumerate().rev() {
457             if tokens.contains(&token::CloseDelim(unmatched.expected_delim))
458                 && Some(self.span) > unmatched.unclosed_span
459             {
460                 pos = Some(i);
461             }
462         }
463         match pos {
464             Some(pos) => {
465                 // Recover and assume that the detected unclosed delimiter was meant for
466                 // this location. Emit the diagnostic and act as if the delimiter was
467                 // present for the parser's sake.
468
469                  // Don't attempt to recover from this unclosed delimiter more than once.
470                 let unmatched = self.unclosed_delims.remove(pos);
471                 let delim = TokenType::Token(token::CloseDelim(unmatched.expected_delim));
472
473                  // We want to suggest the inclusion of the closing delimiter where it makes
474                 // the most sense, which is immediately after the last token:
475                 //
476                 //  {foo(bar {}}
477                 //      -      ^
478                 //      |      |
479                 //      |      help: `)` may belong here (FIXME: #58270)
480                 //      |
481                 //      unclosed delimiter
482                 if let Some(sp) = unmatched.unclosed_span {
483                     err.span_label(sp, "unclosed delimiter");
484                 }
485                 err.span_suggestion_short(
486                     self.sess.source_map().next_point(self.prev_span),
487                     &format!("{} may belong here", delim.to_string()),
488                     delim.to_string(),
489                     Applicability::MaybeIncorrect,
490                 );
491                 err.emit();
492                 self.expected_tokens.clear();  // reduce errors
493                 Ok(true)
494             }
495             _ => Err(err),
496         }
497     }
498
499     /// Recover from `pub` keyword in places where it seems _reasonable_ but isn't valid.
500     crate fn eat_bad_pub(&mut self) {
501         if self.token.is_keyword(kw::Pub) {
502             match self.parse_visibility(false) {
503                 Ok(vis) => {
504                     self.diagnostic()
505                         .struct_span_err(vis.span, "unnecessary visibility qualifier")
506                         .span_label(vis.span, "`pub` not permitted here")
507                         .emit();
508                 }
509                 Err(mut err) => err.emit(),
510             }
511         }
512     }
513
514     // Eat tokens until we can be relatively sure we reached the end of the
515     // statement. This is something of a best-effort heuristic.
516     //
517     // We terminate when we find an unmatched `}` (without consuming it).
518     crate fn recover_stmt(&mut self) {
519         self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore)
520     }
521
522     // If `break_on_semi` is `Break`, then we will stop consuming tokens after
523     // finding (and consuming) a `;` outside of `{}` or `[]` (note that this is
524     // approximate - it can mean we break too early due to macros, but that
525     // should only lead to sub-optimal recovery, not inaccurate parsing).
526     //
527     // If `break_on_block` is `Break`, then we will stop consuming tokens
528     // after finding (and consuming) a brace-delimited block.
529     crate fn recover_stmt_(&mut self, break_on_semi: SemiColonMode, break_on_block: BlockMode) {
530         let mut brace_depth = 0;
531         let mut bracket_depth = 0;
532         let mut in_block = false;
533         debug!("recover_stmt_ enter loop (semi={:?}, block={:?})",
534                break_on_semi, break_on_block);
535         loop {
536             debug!("recover_stmt_ loop {:?}", self.token);
537             match self.token {
538                 token::OpenDelim(token::DelimToken::Brace) => {
539                     brace_depth += 1;
540                     self.bump();
541                     if break_on_block == BlockMode::Break &&
542                        brace_depth == 1 &&
543                        bracket_depth == 0 {
544                         in_block = true;
545                     }
546                 }
547                 token::OpenDelim(token::DelimToken::Bracket) => {
548                     bracket_depth += 1;
549                     self.bump();
550                 }
551                 token::CloseDelim(token::DelimToken::Brace) => {
552                     if brace_depth == 0 {
553                         debug!("recover_stmt_ return - close delim {:?}", self.token);
554                         break;
555                     }
556                     brace_depth -= 1;
557                     self.bump();
558                     if in_block && bracket_depth == 0 && brace_depth == 0 {
559                         debug!("recover_stmt_ return - block end {:?}", self.token);
560                         break;
561                     }
562                 }
563                 token::CloseDelim(token::DelimToken::Bracket) => {
564                     bracket_depth -= 1;
565                     if bracket_depth < 0 {
566                         bracket_depth = 0;
567                     }
568                     self.bump();
569                 }
570                 token::Eof => {
571                     debug!("recover_stmt_ return - Eof");
572                     break;
573                 }
574                 token::Semi => {
575                     self.bump();
576                     if break_on_semi == SemiColonMode::Break &&
577                        brace_depth == 0 &&
578                        bracket_depth == 0 {
579                         debug!("recover_stmt_ return - Semi");
580                         break;
581                     }
582                 }
583                 token::Comma if break_on_semi == SemiColonMode::Comma &&
584                        brace_depth == 0 &&
585                        bracket_depth == 0 =>
586                 {
587                     debug!("recover_stmt_ return - Semi");
588                     break;
589                 }
590                 _ => {
591                     self.bump()
592                 }
593             }
594         }
595     }
596
597     crate fn consume_block(&mut self, delim: token::DelimToken) {
598         let mut brace_depth = 0;
599         loop {
600             if self.eat(&token::OpenDelim(delim)) {
601                 brace_depth += 1;
602             } else if self.eat(&token::CloseDelim(delim)) {
603                 if brace_depth == 0 {
604                     return;
605                 } else {
606                     brace_depth -= 1;
607                     continue;
608                 }
609             } else if self.token == token::Eof || self.eat(&token::CloseDelim(token::NoDelim)) {
610                 return;
611             } else {
612                 self.bump();
613             }
614         }
615     }
616
617     crate fn expected_expression_found(&self) -> DiagnosticBuilder<'a> {
618         let (span, msg) = match (&self.token, self.subparser_name) {
619             (&token::Token::Eof, Some(origin)) => {
620                 let sp = self.sess.source_map().next_point(self.span);
621                 (sp, format!("expected expression, found end of {}", origin))
622             }
623             _ => (self.span, format!(
624                 "expected expression, found {}",
625                 self.this_token_descr(),
626             )),
627         };
628         let mut err = self.struct_span_err(span, &msg);
629         let sp = self.sess.source_map().start_point(self.span);
630         if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) {
631             self.sess.expr_parentheses_needed(&mut err, *sp, None);
632         }
633         err.span_label(span, "expected expression");
634         err
635     }
636 }