Skip to content

Commit 70b38d1

Browse files
authored
Rollup merge of #60873 - estebank:bad-await, r=Centril
Parse alternative incorrect uses of await and recover Fix #60613. r? @Centril
2 parents 71cd93a + c084d0e commit 70b38d1

13 files changed

+690
-288
lines changed

src/librustc/hir/lowering.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ pub struct LoweringContext<'a> {
9797
is_generator: bool,
9898
is_async_body: bool,
9999

100+
/// Used to get the current `fn`'s def span to point to when using `await`
101+
/// outside of an `async fn`.
102+
current_item: Option<Span>,
103+
100104
catch_scopes: Vec<NodeId>,
101105
loop_scopes: Vec<NodeId>,
102106
is_in_loop_condition: bool,
@@ -250,6 +254,7 @@ pub fn lower_crate(
250254
node_id_to_hir_id: IndexVec::new(),
251255
is_generator: false,
252256
is_async_body: false,
257+
current_item: None,
253258
is_in_trait_impl: false,
254259
lifetimes_to_define: Vec::new(),
255260
is_collecting_in_band_lifetimes: false,
@@ -3116,6 +3121,7 @@ impl<'a> LoweringContext<'a> {
31163121
ItemKind::Fn(ref decl, ref header, ref generics, ref body) => {
31173122
let fn_def_id = self.resolver.definitions().local_def_id(id);
31183123
self.with_new_scopes(|this| {
3124+
this.current_item = Some(ident.span);
31193125
let mut lower_fn = |decl: &FnDecl| {
31203126
// Note: we don't need to change the return type from `T` to
31213127
// `impl Future<Output = T>` here because lower_body
@@ -3654,6 +3660,7 @@ impl<'a> LoweringContext<'a> {
36543660
} else {
36553661
lower_method(sig)
36563662
};
3663+
self.current_item = Some(i.span);
36573664

36583665
(generics, hir::ImplItemKind::Method(sig, body_id))
36593666
}
@@ -4270,6 +4277,7 @@ impl<'a> LoweringContext<'a> {
42704277
let fn_decl = self.lower_fn_decl(decl, None, false, None);
42714278

42724279
self.with_new_scopes(|this| {
4280+
this.current_item = Some(fn_decl_span);
42734281
let mut is_generator = false;
42744282
let body_id = this.lower_body(Some(decl), |this| {
42754283
let e = this.lower_expr(body);
@@ -5551,13 +5559,18 @@ impl<'a> LoweringContext<'a> {
55515559
// }
55525560
// }
55535561
if !self.is_async_body {
5554-
span_err!(
5562+
let mut err = struct_span_err!(
55555563
self.sess,
55565564
await_span,
55575565
E0728,
55585566
"`await` is only allowed inside `async` functions and blocks"
55595567
);
5560-
self.sess.abort_if_errors();
5568+
err.span_label(await_span, "only allowed inside `async` functions and blocks");
5569+
if let Some(item_sp) = self.current_item {
5570+
err.span_label(item_sp, "this is not `async`");
5571+
}
5572+
err.emit();
5573+
return hir::ExprKind::Err;
55615574
}
55625575
let span = self.sess.source_map().mark_span_with_reason(
55635576
CompilerDesugaringKind::Await,

src/libsyntax/parse/diagnostics.rs

+301-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
use crate::ast;
2-
use crate::ast::{Expr, ExprKind, Item, ItemKind, Pat, PatKind, QSelf, Ty, TyKind};
3-
use crate::parse::parser::PathStyle;
2+
use crate::ast::{BlockCheckMode, Expr, ExprKind, Item, ItemKind, Pat, PatKind, QSelf, Ty, TyKind};
3+
use crate::parse::parser::{BlockMode, PathStyle, TokenType, SemiColonMode};
44
use crate::parse::token;
55
use crate::parse::PResult;
66
use crate::parse::Parser;
77
use crate::print::pprust;
88
use crate::ptr::P;
9+
use crate::symbol::keywords;
910
use crate::ThinVec;
10-
use errors::Applicability;
11+
use errors::{Applicability, DiagnosticBuilder};
1112
use syntax_pos::Span;
13+
use log::debug;
1214

1315
pub trait RecoverQPath: Sized + 'static {
1416
const PATH_STYLE: PathStyle = PathStyle::Expr;
@@ -223,4 +225,300 @@ impl<'a> Parser<'a> {
223225
false
224226
}
225227
}
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(keywords::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+
226524
}

0 commit comments

Comments
 (0)