]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/parse/diagnostics.rs
Rollup merge of #61092 - spastorino:sanitize-place-iterative, 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;
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: syntax_pos::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     /// Consume alternative await syntaxes like `await <expr>`, `await? <expr>`, `await(<expr>)`
271     /// and `await { <expr> }`.
272     crate fn parse_incorrect_await_syntax(
273         &mut self,
274         lo: Span,
275         await_sp: Span,
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(
283                 None,
284                 self.span,
285                 BlockCheckMode::Default,
286                 ThinVec::new(),
287             )
288         } else {
289             self.parse_expr()
290         }.map_err(|mut err| {
291             err.span_label(await_sp, "while parsing this incorrect await expression");
292             err
293         })?;
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,
301         };
302         self.struct_span_err(sp, "incorrect use of `await`")
303             .span_suggestion(sp, "`await` is a postfix operation", suggestion, app)
304             .emit();
305         Ok((sp, ExprKind::Await(ast::AwaitOrigin::FieldLike, expr)))
306     }
307
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))
312         {
313             // future.await()
314             let lo = self.span;
315             self.bump(); // (
316             let sp = lo.to(self.span);
317             self.bump(); // )
318             self.struct_span_err(sp, "incorrect use of `await`")
319                 .span_suggestion(
320                     sp,
321                     "`await` is not a method call, remove the parentheses",
322                     String::new(),
323                     Applicability::MachineApplicable,
324                 ).emit()
325         }
326     }
327
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())
339     }
340
341     crate fn bad_type_ascription(
342         &self,
343         err: &mut DiagnosticBuilder<'a>,
344         lhs_span: Span,
345         cur_op_span: Span,
346         next_sp: Span,
347         maybe_path: bool,
348     ) {
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 {
354             err.span_suggestion(
355                 cur_op_span,
356                 "try using a semicolon",
357                 ";".to_string(),
358                 Applicability::MaybeIncorrect,
359             );
360         } else {
361             if maybe_path {
362                 err.span_suggestion(
363                     cur_op_span,
364                     "maybe you meant to write a path separator here",
365                     "::".to_string(),
366                     Applicability::MaybeIncorrect,
367                 );
368             } else {
369                 err.note("type ascription is a nightly-only feature that lets \
370                           you annotate an expression with a type: `<expr>: <type>`")
371                     .span_note(
372                         lhs_span,
373                         "this expression expects an ascribed type after the colon",
374                     )
375                     .help("this might be indicative of a syntax error elsewhere");
376             }
377         }
378     }
379
380     crate fn recover_seq_parse_error(
381         &mut self,
382         delim: token::DelimToken,
383         lo: Span,
384         result: PResult<'a, P<Expr>>,
385     ) -> P<Expr> {
386         match result {
387             Ok(x) => x,
388             Err(mut err) => {
389                 err.emit();
390                 // recover from parse error
391                 self.consume_block(delim);
392                 self.mk_expr(lo.to(self.prev_span), ExprKind::Err, ThinVec::new())
393             }
394         }
395     }
396
397     crate fn recover_closing_delimiter(
398         &mut self,
399         tokens: &[token::Token],
400         mut err: DiagnosticBuilder<'a>,
401     ) -> PResult<'a, bool> {
402         let mut pos = None;
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
407             {
408                 pos = Some(i);
409             }
410         }
411         match pos {
412             Some(pos) => {
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.
416
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));
420
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:
423                 //
424                 //  {foo(bar {}}
425                 //      -      ^
426                 //      |      |
427                 //      |      help: `)` may belong here (FIXME: #58270)
428                 //      |
429                 //      unclosed delimiter
430                 if let Some(sp) = unmatched.unclosed_span {
431                     err.span_label(sp, "unclosed delimiter");
432                 }
433                 err.span_suggestion_short(
434                     self.sess.source_map().next_point(self.prev_span),
435                     &format!("{} may belong here", delim.to_string()),
436                     delim.to_string(),
437                     Applicability::MaybeIncorrect,
438                 );
439                 err.emit();
440                 self.expected_tokens.clear();  // reduce errors
441                 Ok(true)
442             }
443             _ => Err(err),
444         }
445     }
446
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) {
451                 Ok(vis) => {
452                     self.diagnostic()
453                         .struct_span_err(vis.span, "unnecessary visibility qualifier")
454                         .span_label(vis.span, "`pub` not permitted here")
455                         .emit();
456                 }
457                 Err(mut err) => err.emit(),
458             }
459         }
460     }
461
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.
464     //
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)
468     }
469
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).
474     //
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);
483         loop {
484             debug!("recover_stmt_ loop {:?}", self.token);
485             match self.token {
486                 token::OpenDelim(token::DelimToken::Brace) => {
487                     brace_depth += 1;
488                     self.bump();
489                     if break_on_block == BlockMode::Break &&
490                        brace_depth == 1 &&
491                        bracket_depth == 0 {
492                         in_block = true;
493                     }
494                 }
495                 token::OpenDelim(token::DelimToken::Bracket) => {
496                     bracket_depth += 1;
497                     self.bump();
498                 }
499                 token::CloseDelim(token::DelimToken::Brace) => {
500                     if brace_depth == 0 {
501                         debug!("recover_stmt_ return - close delim {:?}", self.token);
502                         break;
503                     }
504                     brace_depth -= 1;
505                     self.bump();
506                     if in_block && bracket_depth == 0 && brace_depth == 0 {
507                         debug!("recover_stmt_ return - block end {:?}", self.token);
508                         break;
509                     }
510                 }
511                 token::CloseDelim(token::DelimToken::Bracket) => {
512                     bracket_depth -= 1;
513                     if bracket_depth < 0 {
514                         bracket_depth = 0;
515                     }
516                     self.bump();
517                 }
518                 token::Eof => {
519                     debug!("recover_stmt_ return - Eof");
520                     break;
521                 }
522                 token::Semi => {
523                     self.bump();
524                     if break_on_semi == SemiColonMode::Break &&
525                        brace_depth == 0 &&
526                        bracket_depth == 0 {
527                         debug!("recover_stmt_ return - Semi");
528                         break;
529                     }
530                 }
531                 token::Comma if break_on_semi == SemiColonMode::Comma &&
532                        brace_depth == 0 &&
533                        bracket_depth == 0 =>
534                 {
535                     debug!("recover_stmt_ return - Semi");
536                     break;
537                 }
538                 _ => {
539                     self.bump()
540                 }
541             }
542         }
543     }
544
545     crate fn consume_block(&mut self, delim: token::DelimToken) {
546         let mut brace_depth = 0;
547         loop {
548             if self.eat(&token::OpenDelim(delim)) {
549                 brace_depth += 1;
550             } else if self.eat(&token::CloseDelim(delim)) {
551                 if brace_depth == 0 {
552                     return;
553                 } else {
554                     brace_depth -= 1;
555                     continue;
556                 }
557             } else if self.token == token::Eof || self.eat(&token::CloseDelim(token::NoDelim)) {
558                 return;
559             } else {
560                 self.bump();
561             }
562         }
563     }
564
565 }