diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs new file mode 100644 index 0000000000000..32e1ee94f0dfb --- /dev/null +++ b/src/libsyntax/parse/diagnostics.rs @@ -0,0 +1,226 @@ +use crate::ast; +use crate::ast::{Expr, ExprKind, Item, ItemKind, Pat, PatKind, QSelf, Ty, TyKind}; +use crate::parse::parser::PathStyle; +use crate::parse::token; +use crate::parse::PResult; +use crate::parse::Parser; +use crate::print::pprust; +use crate::ptr::P; +use crate::ThinVec; +use errors::Applicability; +use syntax_pos::Span; + +pub trait RecoverQPath: Sized + 'static { + const PATH_STYLE: PathStyle = PathStyle::Expr; + fn to_ty(&self) -> Option>; + fn recovered(qself: Option, path: ast::Path) -> Self; +} + +impl RecoverQPath for Ty { + const PATH_STYLE: PathStyle = PathStyle::Type; + fn to_ty(&self) -> Option> { + Some(P(self.clone())) + } + fn recovered(qself: Option, path: ast::Path) -> Self { + Self { + span: path.span, + node: TyKind::Path(qself, path), + id: ast::DUMMY_NODE_ID, + } + } +} + +impl RecoverQPath for Pat { + fn to_ty(&self) -> Option> { + self.to_ty() + } + fn recovered(qself: Option, path: ast::Path) -> Self { + Self { + span: path.span, + node: PatKind::Path(qself, path), + id: ast::DUMMY_NODE_ID, + } + } +} + +impl RecoverQPath for Expr { + fn to_ty(&self) -> Option> { + self.to_ty() + } + fn recovered(qself: Option, path: ast::Path) -> Self { + Self { + span: path.span, + node: ExprKind::Path(qself, path), + attrs: ThinVec::new(), + id: ast::DUMMY_NODE_ID, + } + } +} + +impl<'a> Parser<'a> { + crate fn maybe_report_ambiguous_plus( + &mut self, + allow_plus: bool, + impl_dyn_multi: bool, + ty: &Ty, + ) { + if !allow_plus && impl_dyn_multi { + let sum_with_parens = format!("({})", pprust::ty_to_string(&ty)); + self.struct_span_err(ty.span, "ambiguous `+` in a type") + .span_suggestion( + ty.span, + "use parentheses to disambiguate", + sum_with_parens, + Applicability::MachineApplicable, + ) + .emit(); + } + } + + crate fn maybe_recover_from_bad_type_plus( + &mut self, + allow_plus: bool, + ty: &Ty, + ) -> PResult<'a, ()> { + // Do not add `+` to expected tokens. + if !allow_plus || !self.token.is_like_plus() { + return Ok(()); + } + + self.bump(); // `+` + let bounds = self.parse_generic_bounds(None)?; + let sum_span = ty.span.to(self.prev_span); + + let mut err = struct_span_err!( + self.sess.span_diagnostic, + sum_span, + E0178, + "expected a path on the left-hand side of `+`, not `{}`", + pprust::ty_to_string(ty) + ); + + match ty.node { + TyKind::Rptr(ref lifetime, ref mut_ty) => { + let sum_with_parens = pprust::to_string(|s| { + use crate::print::pprust::PrintState; + + s.s.word("&")?; + s.print_opt_lifetime(lifetime)?; + s.print_mutability(mut_ty.mutbl)?; + s.popen()?; + s.print_type(&mut_ty.ty)?; + s.print_type_bounds(" +", &bounds)?; + s.pclose() + }); + err.span_suggestion( + sum_span, + "try adding parentheses", + sum_with_parens, + Applicability::MachineApplicable, + ); + } + TyKind::Ptr(..) | TyKind::BareFn(..) => { + err.span_label(sum_span, "perhaps you forgot parentheses?"); + } + _ => { + err.span_label(sum_span, "expected a path"); + } + } + err.emit(); + Ok(()) + } + + /// Try to recover from associated item paths like `[T]::AssocItem`/`(T, U)::AssocItem`. + /// Attempt to convert the base expression/pattern/type into a type, parse the `::AssocItem` + /// tail, and combine them into a `::AssocItem` expression/pattern/type. + crate fn maybe_recover_from_bad_qpath( + &mut self, + base: P, + allow_recovery: bool, + ) -> PResult<'a, P> { + // Do not add `::` to expected tokens. + if allow_recovery && self.token == token::ModSep { + if let Some(ty) = base.to_ty() { + return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty); + } + } + Ok(base) + } + + /// Given an already parsed `Ty` parse the `::AssocItem` tail and + /// combine them into a `::AssocItem` expression/pattern/type. + crate fn maybe_recover_from_bad_qpath_stage_2( + &mut self, + ty_span: Span, + ty: P, + ) -> PResult<'a, P> { + self.expect(&token::ModSep)?; + + let mut path = ast::Path { + segments: Vec::new(), + span: syntax_pos::DUMMY_SP, + }; + self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?; + path.span = ty_span.to(self.prev_span); + + let ty_str = self + .sess + .source_map() + .span_to_snippet(ty_span) + .unwrap_or_else(|_| pprust::ty_to_string(&ty)); + self.diagnostic() + .struct_span_err(path.span, "missing angle brackets in associated item path") + .span_suggestion( + // this is a best-effort recovery + path.span, + "try", + format!("<{}>::{}", ty_str, path), + Applicability::MaybeIncorrect, + ) + .emit(); + + let path_span = ty_span.shrink_to_hi(); // use an empty path since `position` == 0 + Ok(P(T::recovered( + Some(QSelf { + ty, + path_span, + position: 0, + }), + path, + ))) + } + + crate fn maybe_consume_incorrect_semicolon(&mut self, items: &[P]) -> bool { + if self.eat(&token::Semi) { + let mut err = self.struct_span_err(self.prev_span, "expected item, found `;`"); + err.span_suggestion_short( + self.prev_span, + "remove this semicolon", + String::new(), + Applicability::MachineApplicable, + ); + if !items.is_empty() { + let previous_item = &items[items.len() - 1]; + let previous_item_kind_name = match previous_item.node { + // say "braced struct" because tuple-structs and + // braceless-empty-struct declarations do take a semicolon + ItemKind::Struct(..) => Some("braced struct"), + ItemKind::Enum(..) => Some("enum"), + ItemKind::Trait(..) => Some("trait"), + ItemKind::Union(..) => Some("union"), + _ => None, + }; + if let Some(name) = previous_item_kind_name { + err.help(&format!( + "{} declarations are not followed by a semicolon", + name + )); + } + } + err.emit(); + true + } else { + false + } + } +} diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 371e8fe5cf66f..608d00f5548f7 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -30,6 +30,7 @@ pub mod parser; pub mod lexer; pub mod token; pub mod attr; +pub mod diagnostics; pub mod classify; diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 8efe84cdf016f..9d863612657fa 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -189,41 +189,6 @@ enum PrevTokenKind { Other, } -trait RecoverQPath: Sized + 'static { - const PATH_STYLE: PathStyle = PathStyle::Expr; - fn to_ty(&self) -> Option>; - fn recovered(qself: Option, path: ast::Path) -> Self; -} - -impl RecoverQPath for Ty { - const PATH_STYLE: PathStyle = PathStyle::Type; - fn to_ty(&self) -> Option> { - Some(P(self.clone())) - } - fn recovered(qself: Option, path: ast::Path) -> Self { - Self { span: path.span, node: TyKind::Path(qself, path), id: ast::DUMMY_NODE_ID } - } -} - -impl RecoverQPath for Pat { - fn to_ty(&self) -> Option> { - self.to_ty() - } - fn recovered(qself: Option, path: ast::Path) -> Self { - Self { span: path.span, node: PatKind::Path(qself, path), id: ast::DUMMY_NODE_ID } - } -} - -impl RecoverQPath for Expr { - fn to_ty(&self) -> Option> { - self.to_ty() - } - fn recovered(qself: Option, path: ast::Path) -> Self { - Self { span: path.span, node: ExprKind::Path(qself, path), - attrs: ThinVec::new(), id: ast::DUMMY_NODE_ID } - } -} - /* ident is handled by common.rs */ #[derive(Clone)] @@ -1479,7 +1444,7 @@ impl<'a> Parser<'a> { fn span_err>(&self, sp: S, m: &str) { self.sess.span_diagnostic.span_err(sp, m) } - fn struct_span_err>(&self, sp: S, m: &str) -> DiagnosticBuilder<'a> { + crate fn struct_span_err>(&self, sp: S, m: &str) -> DiagnosticBuilder<'a> { self.sess.span_diagnostic.struct_span_err(sp, m) } fn struct_span_warn>(&self, sp: S, m: &str) -> DiagnosticBuilder<'a> { @@ -1882,99 +1847,6 @@ impl<'a> Parser<'a> { Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None)) } - fn maybe_report_ambiguous_plus(&mut self, allow_plus: bool, impl_dyn_multi: bool, ty: &Ty) { - if !allow_plus && impl_dyn_multi { - let sum_with_parens = format!("({})", pprust::ty_to_string(&ty)); - self.struct_span_err(ty.span, "ambiguous `+` in a type") - .span_suggestion( - ty.span, - "use parentheses to disambiguate", - sum_with_parens, - Applicability::MachineApplicable - ).emit(); - } - } - - fn maybe_recover_from_bad_type_plus(&mut self, allow_plus: bool, ty: &Ty) -> PResult<'a, ()> { - // Do not add `+` to expected tokens. - if !allow_plus || !self.token.is_like_plus() { - return Ok(()) - } - - self.bump(); // `+` - let bounds = self.parse_generic_bounds(None)?; - let sum_span = ty.span.to(self.prev_span); - - let mut err = struct_span_err!(self.sess.span_diagnostic, sum_span, E0178, - "expected a path on the left-hand side of `+`, not `{}`", pprust::ty_to_string(ty)); - - match ty.node { - TyKind::Rptr(ref lifetime, ref mut_ty) => { - let sum_with_parens = pprust::to_string(|s| { - use crate::print::pprust::PrintState; - - s.s.word("&")?; - s.print_opt_lifetime(lifetime)?; - s.print_mutability(mut_ty.mutbl)?; - s.popen()?; - s.print_type(&mut_ty.ty)?; - s.print_type_bounds(" +", &bounds)?; - s.pclose() - }); - err.span_suggestion( - sum_span, - "try adding parentheses", - sum_with_parens, - Applicability::MachineApplicable - ); - } - TyKind::Ptr(..) | TyKind::BareFn(..) => { - err.span_label(sum_span, "perhaps you forgot parentheses?"); - } - _ => { - err.span_label(sum_span, "expected a path"); - }, - } - err.emit(); - Ok(()) - } - - /// Try to recover from associated item paths like `[T]::AssocItem`/`(T, U)::AssocItem`. - /// Attempt to convert the base expression/pattern/type into a type, parse the `::AssocItem` - /// tail, and combine them into a `::AssocItem` expression/pattern/type. - fn maybe_recover_from_bad_qpath(&mut self, base: P, allow_recovery: bool) - -> PResult<'a, P> { - // Do not add `::` to expected tokens. - if allow_recovery && self.token == token::ModSep { - if let Some(ty) = base.to_ty() { - return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty); - } - } - Ok(base) - } - - /// Given an already parsed `Ty` parse the `::AssocItem` tail and - /// combine them into a `::AssocItem` expression/pattern/type. - fn maybe_recover_from_bad_qpath_stage_2(&mut self, ty_span: Span, ty: P) - -> PResult<'a, P> { - self.expect(&token::ModSep)?; - - let mut path = ast::Path { segments: Vec::new(), span: syntax_pos::DUMMY_SP }; - self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?; - path.span = ty_span.to(self.prev_span); - - let ty_str = self.sess.source_map().span_to_snippet(ty_span) - .unwrap_or_else(|_| pprust::ty_to_string(&ty)); - self.diagnostic() - .struct_span_err(path.span, "missing angle brackets in associated item path") - .span_suggestion( // this is a best-effort recovery - path.span, "try", format!("<{}>::{}", ty_str, path), Applicability::MaybeIncorrect - ).emit(); - - let path_span = ty_span.shrink_to_hi(); // use an empty path since `position` == 0 - Ok(P(T::recovered(Some(QSelf { ty, path_span, position: 0 }), path))) - } - fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> { let opt_lifetime = if self.check_lifetime() { Some(self.expect_lifetime()) } else { None }; let mutbl = self.parse_mutability(); @@ -2410,7 +2282,7 @@ impl<'a> Parser<'a> { self.parse_path(style) } - fn parse_path_segments(&mut self, + crate fn parse_path_segments(&mut self, segments: &mut Vec, style: PathStyle) -> PResult<'a, ()> { @@ -5815,7 +5687,7 @@ impl<'a> Parser<'a> { return Ok(bounds); } - fn parse_generic_bounds(&mut self, colon_span: Option) -> PResult<'a, GenericBounds> { + crate fn parse_generic_bounds(&mut self, colon_span: Option) -> PResult<'a, GenericBounds> { self.parse_generic_bounds_common(true, colon_span) } @@ -7352,37 +7224,6 @@ impl<'a> Parser<'a> { } } - fn maybe_consume_incorrect_semicolon(&mut self, items: &[P]) -> bool { - if self.eat(&token::Semi) { - let mut err = self.struct_span_err(self.prev_span, "expected item, found `;`"); - err.span_suggestion_short( - self.prev_span, - "remove this semicolon", - String::new(), - Applicability::MachineApplicable, - ); - if !items.is_empty() { - let previous_item = &items[items.len()-1]; - let previous_item_kind_name = match previous_item.node { - // say "braced struct" because tuple-structs and - // braceless-empty-struct declarations do take a semicolon - ItemKind::Struct(..) => Some("braced struct"), - ItemKind::Enum(..) => Some("enum"), - ItemKind::Trait(..) => Some("trait"), - ItemKind::Union(..) => Some("union"), - _ => None, - }; - if let Some(name) = previous_item_kind_name { - err.help(&format!("{} declarations are not followed by a semicolon", name)); - } - } - err.emit(); - true - } else { - false - } - } - /// Given a termination token, parses all of the items in a module. fn parse_mod_items(&mut self, term: &token::Token, inner_lo: Span) -> PResult<'a, Mod> { let mut items = vec![];