]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/parse/diagnostics.rs
Rollup merge of #61057 - sfackler:revert-next-back, r=alexcrichton
[rust.git] / src / libsyntax / parse / diagnostics.rs
1 use crate::ast;
2 use crate::ast::{BlockCheckMode, Expr, ExprKind, Item, ItemKind, Pat, PatKind, QSelf, Ty, TyKind};
3 use crate::parse::parser::{BlockMode, PathStyle, TokenType, SemiColonMode};
4 use crate::parse::token;
5 use crate::parse::PResult;
6 use crate::parse::Parser;
7 use crate::print::pprust;
8 use crate::ptr::P;
9 use crate::symbol::kw;
10 use crate::ThinVec;
11 use errors::{Applicability, DiagnosticBuilder};
12 use syntax_pos::Span;
13 use log::debug;
14
15 pub trait RecoverQPath: Sized + 'static {
16     const PATH_STYLE: PathStyle = PathStyle::Expr;
17     fn to_ty(&self) -> Option<P<Ty>>;
18     fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self;
19 }
20
21 impl RecoverQPath for Ty {
22     const PATH_STYLE: PathStyle = PathStyle::Type;
23     fn to_ty(&self) -> Option<P<Ty>> {
24         Some(P(self.clone()))
25     }
26     fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
27         Self {
28             span: path.span,
29             node: TyKind::Path(qself, path),
30             id: ast::DUMMY_NODE_ID,
31         }
32     }
33 }
34
35 impl RecoverQPath for Pat {
36     fn to_ty(&self) -> Option<P<Ty>> {
37         self.to_ty()
38     }
39     fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
40         Self {
41             span: path.span,
42             node: PatKind::Path(qself, path),
43             id: ast::DUMMY_NODE_ID,
44         }
45     }
46 }
47
48 impl RecoverQPath for Expr {
49     fn to_ty(&self) -> Option<P<Ty>> {
50         self.to_ty()
51     }
52     fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
53         Self {
54             span: path.span,
55             node: ExprKind::Path(qself, path),
56             attrs: ThinVec::new(),
57             id: ast::DUMMY_NODE_ID,
58         }
59     }
60 }
61
62 impl<'a> Parser<'a> {
63     crate fn maybe_report_ambiguous_plus(
64         &mut self,
65         allow_plus: bool,
66         impl_dyn_multi: bool,
67         ty: &Ty,
68     ) {
69         if !allow_plus && impl_dyn_multi {
70             let sum_with_parens = format!("({})", pprust::ty_to_string(&ty));
71             self.struct_span_err(ty.span, "ambiguous `+` in a type")
72                 .span_suggestion(
73                     ty.span,
74                     "use parentheses to disambiguate",
75                     sum_with_parens,
76                     Applicability::MachineApplicable,
77                 )
78                 .emit();
79         }
80     }
81
82     crate fn maybe_recover_from_bad_type_plus(
83         &mut self,
84         allow_plus: bool,
85         ty: &Ty,
86     ) -> PResult<'a, ()> {
87         // Do not add `+` to expected tokens.
88         if !allow_plus || !self.token.is_like_plus() {
89             return Ok(());
90         }
91
92         self.bump(); // `+`
93         let bounds = self.parse_generic_bounds(None)?;
94         let sum_span = ty.span.to(self.prev_span);
95
96         let mut err = struct_span_err!(
97             self.sess.span_diagnostic,
98             sum_span,
99             E0178,
100             "expected a path on the left-hand side of `+`, not `{}`",
101             pprust::ty_to_string(ty)
102         );
103
104         match ty.node {
105             TyKind::Rptr(ref lifetime, ref mut_ty) => {
106                 let sum_with_parens = pprust::to_string(|s| {
107                     use crate::print::pprust::PrintState;
108
109                     s.s.word("&")?;
110                     s.print_opt_lifetime(lifetime)?;
111                     s.print_mutability(mut_ty.mutbl)?;
112                     s.popen()?;
113                     s.print_type(&mut_ty.ty)?;
114                     s.print_type_bounds(" +", &bounds)?;
115                     s.pclose()
116                 });
117                 err.span_suggestion(
118                     sum_span,
119                     "try adding parentheses",
120                     sum_with_parens,
121                     Applicability::MachineApplicable,
122                 );
123             }
124             TyKind::Ptr(..) | TyKind::BareFn(..) => {
125                 err.span_label(sum_span, "perhaps you forgot parentheses?");
126             }
127             _ => {
128                 err.span_label(sum_span, "expected a path");
129             }
130         }
131         err.emit();
132         Ok(())
133     }
134
135     /// Try to recover from associated item paths like `[T]::AssocItem`/`(T, U)::AssocItem`.
136     /// Attempt to convert the base expression/pattern/type into a type, parse the `::AssocItem`
137     /// tail, and combine them into a `<Ty>::AssocItem` expression/pattern/type.
138     crate fn maybe_recover_from_bad_qpath<T: RecoverQPath>(
139         &mut self,
140         base: P<T>,
141         allow_recovery: bool,
142     ) -> PResult<'a, P<T>> {
143         // Do not add `::` to expected tokens.
144         if allow_recovery && self.token == token::ModSep {
145             if let Some(ty) = base.to_ty() {
146                 return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty);
147             }
148         }
149         Ok(base)
150     }
151
152     /// Given an already parsed `Ty` parse the `::AssocItem` tail and
153     /// combine them into a `<Ty>::AssocItem` expression/pattern/type.
154     crate fn maybe_recover_from_bad_qpath_stage_2<T: RecoverQPath>(
155         &mut self,
156         ty_span: Span,
157         ty: P<Ty>,
158     ) -> PResult<'a, P<T>> {
159         self.expect(&token::ModSep)?;
160
161         let mut path = ast::Path {
162             segments: Vec::new(),
163             span: syntax_pos::DUMMY_SP,
164         };
165         self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?;
166         path.span = ty_span.to(self.prev_span);
167
168         let ty_str = self
169             .sess
170             .source_map()
171             .span_to_snippet(ty_span)
172             .unwrap_or_else(|_| pprust::ty_to_string(&ty));
173         self.diagnostic()
174             .struct_span_err(path.span, "missing angle brackets in associated item path")
175             .span_suggestion(
176                 // this is a best-effort recovery
177                 path.span,
178                 "try",
179                 format!("<{}>::{}", ty_str, path),
180                 Applicability::MaybeIncorrect,
181             )
182             .emit();
183
184         let path_span = ty_span.shrink_to_hi(); // use an empty path since `position` == 0
185         Ok(P(T::recovered(
186             Some(QSelf {
187                 ty,
188                 path_span,
189                 position: 0,
190             }),
191             path,
192         )))
193     }
194
195     crate fn maybe_consume_incorrect_semicolon(&mut self, items: &[P<Item>]) -> bool {
196         if self.eat(&token::Semi) {
197             let mut err = self.struct_span_err(self.prev_span, "expected item, found `;`");
198             err.span_suggestion_short(
199                 self.prev_span,
200                 "remove this semicolon",
201                 String::new(),
202                 Applicability::MachineApplicable,
203             );
204             if !items.is_empty() {
205                 let previous_item = &items[items.len() - 1];
206                 let previous_item_kind_name = match previous_item.node {
207                     // say "braced struct" because tuple-structs and
208                     // braceless-empty-struct declarations do take a semicolon
209                     ItemKind::Struct(..) => Some("braced struct"),
210                     ItemKind::Enum(..) => Some("enum"),
211                     ItemKind::Trait(..) => Some("trait"),
212                     ItemKind::Union(..) => Some("union"),
213                     _ => None,
214                 };
215                 if let Some(name) = previous_item_kind_name {
216                     err.help(&format!(
217                         "{} declarations are not followed by a semicolon",
218                         name
219                     ));
220                 }
221             }
222             err.emit();
223             true
224         } else {
225             false
226         }
227     }
228
229     /// Consume alternative await syntaxes like `await <expr>`, `await? <expr>`, `await(<expr>)`
230     /// and `await { <expr> }`.
231     crate fn parse_incorrect_await_syntax(
232         &mut self,
233         lo: Span,
234         await_sp: Span,
235     ) -> PResult<'a, (Span, ExprKind)> {
236         let is_question = self.eat(&token::Question); // Handle `await? <expr>`.
237         let expr = if self.token == token::OpenDelim(token::Brace) {
238             // Handle `await { <expr> }`.
239             // This needs to be handled separatedly from the next arm to avoid
240             // interpreting `await { <expr> }?` as `<expr>?.await`.
241             self.parse_block_expr(
242                 None,
243                 self.span,
244                 BlockCheckMode::Default,
245                 ThinVec::new(),
246             )
247         } else {
248             self.parse_expr()
249         }.map_err(|mut err| {
250             err.span_label(await_sp, "while parsing this incorrect await expression");
251             err
252         })?;
253         let expr_str = self.sess.source_map().span_to_snippet(expr.span)
254             .unwrap_or_else(|_| pprust::expr_to_string(&expr));
255         let suggestion = format!("{}.await{}", expr_str, if is_question { "?" } else { "" });
256         let sp = lo.to(expr.span);
257         let app = match expr.node {
258             ExprKind::Try(_) => Applicability::MaybeIncorrect, // `await <expr>?`
259             _ => Applicability::MachineApplicable,
260         };
261         self.struct_span_err(sp, "incorrect use of `await`")
262             .span_suggestion(sp, "`await` is a postfix operation", suggestion, app)
263             .emit();
264         Ok((sp, ExprKind::Await(ast::AwaitOrigin::FieldLike, expr)))
265     }
266
267     /// If encountering `future.await()`, consume and emit error.
268     crate fn recover_from_await_method_call(&mut self) {
269         if self.token == token::OpenDelim(token::Paren) &&
270             self.look_ahead(1, |t| t == &token::CloseDelim(token::Paren))
271         {
272             // future.await()
273             let lo = self.span;
274             self.bump(); // (
275             let sp = lo.to(self.span);
276             self.bump(); // )
277             self.struct_span_err(sp, "incorrect use of `await`")
278                 .span_suggestion(
279                     sp,
280                     "`await` is not a method call, remove the parentheses",
281                     String::new(),
282                     Applicability::MachineApplicable,
283                 ).emit()
284         }
285     }
286
287     crate fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool {
288         self.token.is_ident() &&
289             if let ast::ExprKind::Path(..) = node { true } else { false } &&
290             !self.token.is_reserved_ident() &&           // v `foo:bar(baz)`
291             self.look_ahead(1, |t| t == &token::OpenDelim(token::Paren)) ||
292             self.look_ahead(1, |t| t == &token::Lt) &&     // `foo:bar<baz`
293             self.look_ahead(2, |t| t.is_ident()) ||
294             self.look_ahead(1, |t| t == &token::Colon) &&  // `foo:bar:baz`
295             self.look_ahead(2, |t| t.is_ident()) ||
296             self.look_ahead(1, |t| t == &token::ModSep) &&  // `foo:bar::baz`
297             self.look_ahead(2, |t| t.is_ident())
298     }
299
300     crate fn bad_type_ascription(
301         &self,
302         err: &mut DiagnosticBuilder<'a>,
303         lhs_span: Span,
304         cur_op_span: Span,
305         next_sp: Span,
306         maybe_path: bool,
307     ) {
308         err.span_label(self.span, "expecting a type here because of type ascription");
309         let cm = self.sess.source_map();
310         let next_pos = cm.lookup_char_pos(next_sp.lo());
311         let op_pos = cm.lookup_char_pos(cur_op_span.hi());
312         if op_pos.line != next_pos.line {
313             err.span_suggestion(
314                 cur_op_span,
315                 "try using a semicolon",
316                 ";".to_string(),
317                 Applicability::MaybeIncorrect,
318             );
319         } else {
320             if maybe_path {
321                 err.span_suggestion(
322                     cur_op_span,
323                     "maybe you meant to write a path separator here",
324                     "::".to_string(),
325                     Applicability::MaybeIncorrect,
326                 );
327             } else {
328                 err.note("type ascription is a nightly-only feature that lets \
329                           you annotate an expression with a type: `<expr>: <type>`")
330                     .span_note(
331                         lhs_span,
332                         "this expression expects an ascribed type after the colon",
333                     )
334                     .help("this might be indicative of a syntax error elsewhere");
335             }
336         }
337     }
338
339     crate fn recover_seq_parse_error(
340         &mut self,
341         delim: token::DelimToken,
342         lo: Span,
343         result: PResult<'a, P<Expr>>,
344     ) -> P<Expr> {
345         match result {
346             Ok(x) => x,
347             Err(mut err) => {
348                 err.emit();
349                 // recover from parse error
350                 self.consume_block(delim);
351                 self.mk_expr(lo.to(self.prev_span), ExprKind::Err, ThinVec::new())
352             }
353         }
354     }
355
356     crate fn recover_closing_delimiter(
357         &mut self,
358         tokens: &[token::Token],
359         mut err: DiagnosticBuilder<'a>,
360     ) -> PResult<'a, bool> {
361         let mut pos = None;
362         // we want to use the last closing delim that would apply
363         for (i, unmatched) in self.unclosed_delims.iter().enumerate().rev() {
364             if tokens.contains(&token::CloseDelim(unmatched.expected_delim))
365                 && Some(self.span) > unmatched.unclosed_span
366             {
367                 pos = Some(i);
368             }
369         }
370         match pos {
371             Some(pos) => {
372                 // Recover and assume that the detected unclosed delimiter was meant for
373                 // this location. Emit the diagnostic and act as if the delimiter was
374                 // present for the parser's sake.
375
376                  // Don't attempt to recover from this unclosed delimiter more than once.
377                 let unmatched = self.unclosed_delims.remove(pos);
378                 let delim = TokenType::Token(token::CloseDelim(unmatched.expected_delim));
379
380                  // We want to suggest the inclusion of the closing delimiter where it makes
381                 // the most sense, which is immediately after the last token:
382                 //
383                 //  {foo(bar {}}
384                 //      -      ^
385                 //      |      |
386                 //      |      help: `)` may belong here (FIXME: #58270)
387                 //      |
388                 //      unclosed delimiter
389                 if let Some(sp) = unmatched.unclosed_span {
390                     err.span_label(sp, "unclosed delimiter");
391                 }
392                 err.span_suggestion_short(
393                     self.sess.source_map().next_point(self.prev_span),
394                     &format!("{} may belong here", delim.to_string()),
395                     delim.to_string(),
396                     Applicability::MaybeIncorrect,
397                 );
398                 err.emit();
399                 self.expected_tokens.clear();  // reduce errors
400                 Ok(true)
401             }
402             _ => Err(err),
403         }
404     }
405
406     /// Recover from `pub` keyword in places where it seems _reasonable_ but isn't valid.
407     crate fn eat_bad_pub(&mut self) {
408         if self.token.is_keyword(kw::Pub) {
409             match self.parse_visibility(false) {
410                 Ok(vis) => {
411                     self.diagnostic()
412                         .struct_span_err(vis.span, "unnecessary visibility qualifier")
413                         .span_label(vis.span, "`pub` not permitted here")
414                         .emit();
415                 }
416                 Err(mut err) => err.emit(),
417             }
418         }
419     }
420
421     // Eat tokens until we can be relatively sure we reached the end of the
422     // statement. This is something of a best-effort heuristic.
423     //
424     // We terminate when we find an unmatched `}` (without consuming it).
425     crate fn recover_stmt(&mut self) {
426         self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore)
427     }
428
429     // If `break_on_semi` is `Break`, then we will stop consuming tokens after
430     // finding (and consuming) a `;` outside of `{}` or `[]` (note that this is
431     // approximate - it can mean we break too early due to macros, but that
432     // should only lead to sub-optimal recovery, not inaccurate parsing).
433     //
434     // If `break_on_block` is `Break`, then we will stop consuming tokens
435     // after finding (and consuming) a brace-delimited block.
436     crate fn recover_stmt_(&mut self, break_on_semi: SemiColonMode, break_on_block: BlockMode) {
437         let mut brace_depth = 0;
438         let mut bracket_depth = 0;
439         let mut in_block = false;
440         debug!("recover_stmt_ enter loop (semi={:?}, block={:?})",
441                break_on_semi, break_on_block);
442         loop {
443             debug!("recover_stmt_ loop {:?}", self.token);
444             match self.token {
445                 token::OpenDelim(token::DelimToken::Brace) => {
446                     brace_depth += 1;
447                     self.bump();
448                     if break_on_block == BlockMode::Break &&
449                        brace_depth == 1 &&
450                        bracket_depth == 0 {
451                         in_block = true;
452                     }
453                 }
454                 token::OpenDelim(token::DelimToken::Bracket) => {
455                     bracket_depth += 1;
456                     self.bump();
457                 }
458                 token::CloseDelim(token::DelimToken::Brace) => {
459                     if brace_depth == 0 {
460                         debug!("recover_stmt_ return - close delim {:?}", self.token);
461                         break;
462                     }
463                     brace_depth -= 1;
464                     self.bump();
465                     if in_block && bracket_depth == 0 && brace_depth == 0 {
466                         debug!("recover_stmt_ return - block end {:?}", self.token);
467                         break;
468                     }
469                 }
470                 token::CloseDelim(token::DelimToken::Bracket) => {
471                     bracket_depth -= 1;
472                     if bracket_depth < 0 {
473                         bracket_depth = 0;
474                     }
475                     self.bump();
476                 }
477                 token::Eof => {
478                     debug!("recover_stmt_ return - Eof");
479                     break;
480                 }
481                 token::Semi => {
482                     self.bump();
483                     if break_on_semi == SemiColonMode::Break &&
484                        brace_depth == 0 &&
485                        bracket_depth == 0 {
486                         debug!("recover_stmt_ return - Semi");
487                         break;
488                     }
489                 }
490                 token::Comma if break_on_semi == SemiColonMode::Comma &&
491                        brace_depth == 0 &&
492                        bracket_depth == 0 =>
493                 {
494                     debug!("recover_stmt_ return - Semi");
495                     break;
496                 }
497                 _ => {
498                     self.bump()
499                 }
500             }
501         }
502     }
503
504     crate fn consume_block(&mut self, delim: token::DelimToken) {
505         let mut brace_depth = 0;
506         loop {
507             if self.eat(&token::OpenDelim(delim)) {
508                 brace_depth += 1;
509             } else if self.eat(&token::CloseDelim(delim)) {
510                 if brace_depth == 0 {
511                     return;
512                 } else {
513                     brace_depth -= 1;
514                     continue;
515                 }
516             } else if self.token == token::Eof || self.eat(&token::CloseDelim(token::NoDelim)) {
517                 return;
518             } else {
519                 self.bump();
520             }
521         }
522     }
523
524 }