diff options
| author | Tim Diekmann <21277928+TimDiekmann@users.noreply.github.com> | 2020-10-16 08:54:38 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-10-16 08:54:38 +0200 |
| commit | 955b37b3059a7e06842559af54e1685e33b0cf6c (patch) | |
| tree | f11d1e90021475b287dcef9ab05f3f3bfc004ea8 /compiler/rustc_parse/src/parser | |
| parent | d5720bba8f5b278616a2fbd0da39478879a3f68b (diff) | |
| parent | 8e6f69afc9b0943003ce51a53d1f59611e6601a3 (diff) | |
| download | rust-955b37b3059a7e06842559af54e1685e33b0cf6c.tar.gz rust-955b37b3059a7e06842559af54e1685e33b0cf6c.zip | |
Merge branch 'master' into box-alloc
Diffstat (limited to 'compiler/rustc_parse/src/parser')
| -rw-r--r-- | compiler/rustc_parse/src/parser/diagnostics.rs | 87 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 80 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/item.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/mod.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/stmt.rs | 36 |
5 files changed, 177 insertions, 31 deletions
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 9ab13db4b5f..1ea01d95a13 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -5,8 +5,9 @@ use rustc_ast::ptr::P; use rustc_ast::token::{self, Lit, LitKind, TokenKind}; use rustc_ast::util::parser::AssocOp; use rustc_ast::{ - self as ast, AngleBracketedArgs, AttrVec, BinOpKind, BindingMode, BlockCheckMode, Expr, - ExprKind, Item, ItemKind, Mutability, Param, Pat, PatKind, PathSegment, QSelf, Ty, TyKind, + self as ast, AngleBracketedArgs, AttrVec, BinOpKind, BindingMode, Block, BlockCheckMode, Expr, + ExprKind, Item, ItemKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QSelf, Ty, + TyKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; @@ -119,6 +120,28 @@ crate enum ConsumeClosingDelim { No, } +#[derive(Clone, Copy)] +pub enum AttemptLocalParseRecovery { + Yes, + No, +} + +impl AttemptLocalParseRecovery { + pub fn yes(&self) -> bool { + match self { + AttemptLocalParseRecovery::Yes => true, + AttemptLocalParseRecovery::No => false, + } + } + + pub fn no(&self) -> bool { + match self { + AttemptLocalParseRecovery::Yes => false, + AttemptLocalParseRecovery::No => true, + } + } +} + impl<'a> Parser<'a> { pub(super) fn span_fatal_err<S: Into<MultiSpan>>( &self, @@ -321,6 +344,66 @@ impl<'a> Parser<'a> { } } + pub fn maybe_suggest_struct_literal( + &mut self, + lo: Span, + s: BlockCheckMode, + ) -> Option<PResult<'a, P<Block>>> { + if self.token.is_ident() && self.look_ahead(1, |t| t == &token::Colon) { + // We might be having a struct literal where people forgot to include the path: + // fn foo() -> Foo { + // field: value, + // } + let mut snapshot = self.clone(); + let path = + Path { segments: vec![], span: self.prev_token.span.shrink_to_lo(), tokens: None }; + let struct_expr = snapshot.parse_struct_expr(path, AttrVec::new(), false); + let block_tail = self.parse_block_tail(lo, s, AttemptLocalParseRecovery::No); + return Some(match (struct_expr, block_tail) { + (Ok(expr), Err(mut err)) => { + // We have encountered the following: + // fn foo() -> Foo { + // field: value, + // } + // Suggest: + // fn foo() -> Foo { Path { + // field: value, + // } } + err.delay_as_bug(); + self.struct_span_err(expr.span, "struct literal body without path") + .multipart_suggestion( + "you might have forgotten to add the struct literal inside the block", + vec![ + (expr.span.shrink_to_lo(), "{ SomeStruct ".to_string()), + (expr.span.shrink_to_hi(), " }".to_string()), + ], + Applicability::MaybeIncorrect, + ) + .emit(); + *self = snapshot; + Ok(self.mk_block( + vec![self.mk_stmt_err(expr.span)], + s, + lo.to(self.prev_token.span), + )) + } + (Err(mut err), Ok(tail)) => { + // We have a block tail that contains a somehow valid type ascription expr. + err.cancel(); + Ok(tail) + } + (Err(mut snapshot_err), Err(err)) => { + // We don't know what went wrong, emit the normal error. + snapshot_err.cancel(); + self.consume_block(token::Brace, ConsumeClosingDelim::Yes); + Err(err) + } + (Ok(_), Ok(tail)) => Ok(tail), + }); + } + None + } + pub fn maybe_annotate_with_ascription( &mut self, err: &mut DiagnosticBuilder<'_>, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 69d13b5cf53..17cbaf65420 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -16,6 +16,7 @@ use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, DiagnosticBuilder, PResult}; use rustc_span::source_map::{self, Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::{BytePos, Pos}; use std::mem; use tracing::debug; @@ -245,11 +246,7 @@ impl<'a> Parser<'a> { this.parse_assoc_expr_with(prec + prec_adjustment, LhsExpr::NotYetParsed) })?; - // Make sure that the span of the parent node is larger than the span of lhs and rhs, - // including the attributes. - let lhs_span = - lhs.attrs.iter().find(|a| a.style == AttrStyle::Outer).map_or(lhs_span, |a| a.span); - let span = lhs_span.to(rhs.span); + let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); lhs = match op { AssocOp::Add | AssocOp::Subtract @@ -410,7 +407,7 @@ impl<'a> Parser<'a> { None }; let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span); - let span = lhs.span.to(rhs_span); + let span = self.mk_expr_sp(&lhs, lhs.span, rhs_span); let limits = if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed }; Ok(self.mk_expr(span, self.mk_range(Some(lhs), rhs, limits)?, AttrVec::new())) @@ -570,7 +567,11 @@ impl<'a> Parser<'a> { expr_kind: fn(P<Expr>, P<Ty>) -> ExprKind, ) -> PResult<'a, P<Expr>> { let mk_expr = |this: &mut Self, rhs: P<Ty>| { - this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs), AttrVec::new()) + this.mk_expr( + this.mk_expr_sp(&lhs, lhs_span, rhs.span), + expr_kind(lhs, rhs), + AttrVec::new(), + ) }; // Save the state of the parser before parsing type normally, in case there is a @@ -839,9 +840,10 @@ impl<'a> Parser<'a> { } use FloatComponent::*; + let float_str = float.as_str(); let mut components = Vec::new(); let mut ident_like = String::new(); - for c in float.as_str().chars() { + for c in float_str.chars() { if c == '_' || c.is_ascii_alphanumeric() { ident_like.push(c); } else if matches!(c, '.' | '+' | '-') { @@ -857,8 +859,13 @@ impl<'a> Parser<'a> { components.push(IdentLike(ident_like)); } - // FIXME: Make the span more precise. + // With proc macros the span can refer to anything, the source may be too short, + // or too long, or non-ASCII. It only makes sense to break our span into components + // if its underlying text is identical to our float literal. let span = self.token.span; + let can_take_span_apart = + || self.span_to_snippet(span).as_deref() == Ok(float_str).as_deref(); + match &*components { // 1e2 [IdentLike(i)] => { @@ -866,21 +873,40 @@ impl<'a> Parser<'a> { } // 1. [IdentLike(i), Punct('.')] => { + let (ident_span, dot_span) = if can_take_span_apart() { + let (span, ident_len) = (span.data(), BytePos::from_usize(i.len())); + let ident_span = span.with_hi(span.lo + ident_len); + let dot_span = span.with_lo(span.lo + ident_len); + (ident_span, dot_span) + } else { + (span, span) + }; assert!(suffix.is_none()); let symbol = Symbol::intern(&i); - self.token = Token::new(token::Ident(symbol, false), span); - let next_token = Token::new(token::Dot, span); + self.token = Token::new(token::Ident(symbol, false), ident_span); + let next_token = Token::new(token::Dot, dot_span); self.parse_tuple_field_access_expr(lo, base, symbol, None, Some(next_token)) } // 1.2 | 1.2e3 [IdentLike(i1), Punct('.'), IdentLike(i2)] => { + let (ident1_span, dot_span, ident2_span) = if can_take_span_apart() { + let (span, ident1_len) = (span.data(), BytePos::from_usize(i1.len())); + let ident1_span = span.with_hi(span.lo + ident1_len); + let dot_span = span + .with_lo(span.lo + ident1_len) + .with_hi(span.lo + ident1_len + BytePos(1)); + let ident2_span = self.token.span.with_lo(span.lo + ident1_len + BytePos(1)); + (ident1_span, dot_span, ident2_span) + } else { + (span, span, span) + }; let symbol1 = Symbol::intern(&i1); - self.token = Token::new(token::Ident(symbol1, false), span); - let next_token1 = Token::new(token::Dot, span); + self.token = Token::new(token::Ident(symbol1, false), ident1_span); + let next_token1 = Token::new(token::Dot, dot_span); let base1 = self.parse_tuple_field_access_expr(lo, base, symbol1, None, Some(next_token1)); let symbol2 = Symbol::intern(&i2); - let next_token2 = Token::new(token::Ident(symbol2, false), span); + let next_token2 = Token::new(token::Ident(symbol2, false), ident2_span); self.bump_with(next_token2); // `.` self.parse_tuple_field_access_expr(lo, base1, symbol2, suffix, None) } @@ -2015,9 +2041,12 @@ impl<'a> Parser<'a> { ) -> Option<PResult<'a, P<Expr>>> { let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL); if struct_allowed || self.is_certainly_not_a_block() { - // This is a struct literal, but we don't can't accept them here. - let expr = self.parse_struct_expr(path.clone(), attrs.clone()); + if let Err(err) = self.expect(&token::OpenDelim(token::Brace)) { + return Some(Err(err)); + } + let expr = self.parse_struct_expr(path.clone(), attrs.clone(), true); if let (Ok(expr), false) = (&expr, struct_allowed) { + // This is a struct literal, but we don't can't accept them here. self.error_struct_lit_not_allowed_here(path.span, expr.span); } return Some(expr); @@ -2035,12 +2064,13 @@ impl<'a> Parser<'a> { .emit(); } + /// Precondition: already parsed the '{'. pub(super) fn parse_struct_expr( &mut self, pth: ast::Path, mut attrs: AttrVec, + recover: bool, ) -> PResult<'a, P<Expr>> { - self.bump(); let mut fields = Vec::new(); let mut base = None; let mut recover_async = false; @@ -2059,10 +2089,11 @@ impl<'a> Parser<'a> { let exp_span = self.prev_token.span; match self.parse_expr() { Ok(e) => base = Some(e), - Err(mut e) => { + Err(mut e) if recover => { e.emit(); self.recover_stmt(); } + Err(e) => return Err(e), } self.recover_struct_comma_after_dotdot(exp_span); break; @@ -2114,6 +2145,9 @@ impl<'a> Parser<'a> { ); } } + if !recover { + return Err(e); + } e.emit(); self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore); self.eat(&token::Comma); @@ -2290,4 +2324,14 @@ impl<'a> Parser<'a> { pub(super) fn mk_expr_err(&self, span: Span) -> P<Expr> { self.mk_expr(span, ExprKind::Err, AttrVec::new()) } + + /// Create expression span ensuring the span of the parent node + /// is larger than the span of lhs and rhs, including the attributes. + fn mk_expr_sp(&self, lhs: &P<Expr>, lhs_span: Span, rhs_span: Span) -> Span { + lhs.attrs + .iter() + .find(|a| a.style == AttrStyle::Outer) + .map_or(lhs_span, |a| a.span) + .to(rhs_span) + } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 26ca9980127..48341f71d33 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1744,7 +1744,7 @@ impl<'a> Parser<'a> { } }; - let span = lo.to(self.token.span); + let span = lo.until(self.token.span); Ok(Param { attrs: attrs.into(), diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 7340c574480..c1094681221 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -10,6 +10,7 @@ mod stmt; mod ty; use crate::lexer::UnmatchedBrace; +pub use diagnostics::AttemptLocalParseRecovery; use diagnostics::Error; pub use path::PathStyle; @@ -386,7 +387,7 @@ impl<'a> Parser<'a> { next } - crate fn unexpected<T>(&mut self) -> PResult<'a, T> { + pub fn unexpected<T>(&mut self) -> PResult<'a, T> { match self.expect_one_of(&[], &[]) { Err(e) => Err(e), // We can get `Ok(true)` from `recover_closing_delimiter` diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index fd1c6b25aec..131ff1ae6b3 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -1,5 +1,5 @@ use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN; -use super::diagnostics::Error; +use super::diagnostics::{AttemptLocalParseRecovery, Error}; use super::expr::LhsExpr; use super::pat::GateOr; use super::path::PathStyle; @@ -79,8 +79,8 @@ impl<'a> Parser<'a> { return self.parse_stmt_mac(lo, attrs.into(), path); } - let expr = if self.check(&token::OpenDelim(token::Brace)) { - self.parse_struct_expr(path, AttrVec::new())? + let expr = if self.eat(&token::OpenDelim(token::Brace)) { + self.parse_struct_expr(path, AttrVec::new(), true)? } else { let hi = self.prev_token.span; self.mk_expr(lo.to(hi), ExprKind::Path(None, path), AttrVec::new()) @@ -321,25 +321,37 @@ impl<'a> Parser<'a> { return self.error_block_no_opening_brace(); } - Ok((self.parse_inner_attributes()?, self.parse_block_tail(lo, blk_mode)?)) + let attrs = self.parse_inner_attributes()?; + let tail = if let Some(tail) = self.maybe_suggest_struct_literal(lo, blk_mode) { + tail? + } else { + self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)? + }; + Ok((attrs, tail)) } /// Parses the rest of a block expression or function body. /// Precondition: already parsed the '{'. - fn parse_block_tail(&mut self, lo: Span, s: BlockCheckMode) -> PResult<'a, P<Block>> { + crate fn parse_block_tail( + &mut self, + lo: Span, + s: BlockCheckMode, + recover: AttemptLocalParseRecovery, + ) -> PResult<'a, P<Block>> { let mut stmts = vec![]; while !self.eat(&token::CloseDelim(token::Brace)) { if self.token == token::Eof { break; } - let stmt = match self.parse_full_stmt() { - Err(mut err) => { + let stmt = match self.parse_full_stmt(recover) { + Err(mut err) if recover.yes() => { self.maybe_annotate_with_ascription(&mut err, false); err.emit(); self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore); Some(self.mk_stmt_err(self.token.span)) } Ok(stmt) => stmt, + Err(err) => return Err(err), }; if let Some(stmt) = stmt { stmts.push(stmt); @@ -352,7 +364,10 @@ impl<'a> Parser<'a> { } /// Parses a statement, including the trailing semicolon. - pub fn parse_full_stmt(&mut self) -> PResult<'a, Option<Stmt>> { + pub fn parse_full_stmt( + &mut self, + recover: AttemptLocalParseRecovery, + ) -> PResult<'a, Option<Stmt>> { // Skip looking for a trailing semicolon when we have an interpolated statement. maybe_whole!(self, NtStmt, |x| Some(x)); @@ -391,6 +406,9 @@ impl<'a> Parser<'a> { if let Err(mut e) = self.check_mistyped_turbofish_with_multiple_type_params(e, expr) { + if recover.no() { + return Err(e); + } e.emit(); self.recover_stmt(); } @@ -432,7 +450,7 @@ impl<'a> Parser<'a> { Stmt { id: DUMMY_NODE_ID, kind, span, tokens: None } } - fn mk_stmt_err(&self, span: Span) -> Stmt { + pub(super) fn mk_stmt_err(&self, span: Span) -> Stmt { self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span))) } |
