From 91194f795c3ce323b50b5caa30c089ec026200f0 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sun, 22 Mar 2020 04:40:05 +0100 Subject: [PATCH 1/7] parse: move constraint/arg restriction to ast_validation. --- src/librustc_ast/ast.rs | 29 +++-- src/librustc_ast/mut_visit.rs | 8 +- src/librustc_ast/visit.rs | 8 +- src/librustc_ast_lowering/lib.rs | 1 + src/librustc_ast_lowering/path.rs | 35 ++--- src/librustc_ast_passes/ast_validation.rs | 56 ++++++-- src/librustc_ast_passes/lib.rs | 4 +- src/librustc_ast_pretty/pprust.rs | 41 +++--- src/librustc_expand/build.rs | 3 +- src/librustc_interface/util.rs | 16 ++- src/librustc_parse/parser/path.rs | 76 ++++------- src/librustc_save_analysis/dump_visitor.rs | 4 +- ...ints-before-generic-args-syntactic-pass.rs | 9 ++ src/test/ui/parser/issue-32214.rs | 2 +- src/test/ui/parser/issue-32214.stderr | 11 +- src/test/ui/suggestions/suggest-move-types.rs | 22 ++-- .../ui/suggestions/suggest-move-types.stderr | 122 ++++++++++-------- 17 files changed, 247 insertions(+), 200 deletions(-) create mode 100644 src/test/ui/parser/constraints-before-generic-args-syntactic-pass.rs diff --git a/src/librustc_ast/ast.rs b/src/librustc_ast/ast.rs index 646294a7cca78..6586280d21454 100644 --- a/src/librustc_ast/ast.rs +++ b/src/librustc_ast/ast.rs @@ -214,11 +214,18 @@ impl GenericArg { pub struct AngleBracketedArgs { /// The overall span. pub span: Span, - /// The arguments for this path segment. - pub args: Vec, - /// Constraints on associated types, if any. - /// E.g., `Foo`. - pub constraints: Vec, + /// The comma separated parts in the `<...>`. + pub args: Vec, +} + +/// Either an argument for a parameter e.g., `'a`, `Vec`, `0`, +/// or a constraint on an associated item, e.g., `Item = String` or `Item: Bound`. +#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +pub enum AngleBracketedArg { + /// Argument for a generic parameter. + Arg(GenericArg), + /// Constraint for an associated item. + Constraint(AssocTyConstraint), } impl Into>> for AngleBracketedArgs { @@ -248,11 +255,13 @@ pub struct ParenthesizedArgs { impl ParenthesizedArgs { pub fn as_angle_bracketed_args(&self) -> AngleBracketedArgs { - AngleBracketedArgs { - span: self.span, - args: self.inputs.iter().cloned().map(GenericArg::Type).collect(), - constraints: vec![], - } + let args = self + .inputs + .iter() + .cloned() + .map(|input| AngleBracketedArg::Arg(GenericArg::Type(input))) + .collect(); + AngleBracketedArgs { span: self.span, args } } } diff --git a/src/librustc_ast/mut_visit.rs b/src/librustc_ast/mut_visit.rs index 67f7764d5bb27..a72a60c30b28a 100644 --- a/src/librustc_ast/mut_visit.rs +++ b/src/librustc_ast/mut_visit.rs @@ -546,9 +546,11 @@ pub fn noop_visit_angle_bracketed_parameter_data( data: &mut AngleBracketedArgs, vis: &mut T, ) { - let AngleBracketedArgs { args, constraints, span } = data; - visit_vec(args, |arg| vis.visit_generic_arg(arg)); - visit_vec(constraints, |constraint| vis.visit_ty_constraint(constraint)); + let AngleBracketedArgs { args, span } = data; + visit_vec(args, |arg| match arg { + AngleBracketedArg::Arg(arg) => vis.visit_generic_arg(arg), + AngleBracketedArg::Constraint(constraint) => vis.visit_ty_constraint(constraint), + }); vis.visit_span(span); } diff --git a/src/librustc_ast/visit.rs b/src/librustc_ast/visit.rs index cc2b1c48b51ac..51863350a8cf3 100644 --- a/src/librustc_ast/visit.rs +++ b/src/librustc_ast/visit.rs @@ -464,8 +464,12 @@ where { match *generic_args { GenericArgs::AngleBracketed(ref data) => { - walk_list!(visitor, visit_generic_arg, &data.args); - walk_list!(visitor, visit_assoc_ty_constraint, &data.constraints); + for arg in &data.args { + match arg { + AngleBracketedArg::Arg(a) => visitor.visit_generic_arg(a), + AngleBracketedArg::Constraint(c) => visitor.visit_assoc_ty_constraint(c), + } + } } GenericArgs::Parenthesized(ref data) => { walk_list!(visitor, visit_ty, &data.inputs); diff --git a/src/librustc_ast_lowering/lib.rs b/src/librustc_ast_lowering/lib.rs index 68cb1f8585fc7..45ee7265c15fc 100644 --- a/src/librustc_ast_lowering/lib.rs +++ b/src/librustc_ast_lowering/lib.rs @@ -34,6 +34,7 @@ #![feature(crate_visibility_modifier)] #![feature(marker_trait_attr)] #![feature(specialization)] +#![feature(or_patterns)] #![recursion_limit = "256"] use rustc_ast::ast; diff --git a/src/librustc_ast_lowering/path.rs b/src/librustc_ast_lowering/path.rs index e996bdac7cb37..f15a4555e5f13 100644 --- a/src/librustc_ast_lowering/path.rs +++ b/src/librustc_ast_lowering/path.rs @@ -366,22 +366,27 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { param_mode: ParamMode, mut itctx: ImplTraitContext<'_, 'hir>, ) -> (GenericArgsCtor<'hir>, bool) { - let &AngleBracketedArgs { ref args, ref constraints, .. } = data; - let has_non_lt_args = args.iter().any(|arg| match arg { - ast::GenericArg::Lifetime(_) => false, - ast::GenericArg::Type(_) => true, - ast::GenericArg::Const(_) => true, + let has_non_lt_args = data.args.iter().any(|arg| match arg { + AngleBracketedArg::Arg(ast::GenericArg::Lifetime(_)) => false, + AngleBracketedArg::Arg(ast::GenericArg::Type(_) | ast::GenericArg::Const(_)) + | AngleBracketedArg::Constraint(_) => true, }); - ( - GenericArgsCtor { - args: args.iter().map(|a| self.lower_generic_arg(a, itctx.reborrow())).collect(), - bindings: self.arena.alloc_from_iter( - constraints.iter().map(|b| self.lower_assoc_ty_constraint(b, itctx.reborrow())), - ), - parenthesized: false, - }, - !has_non_lt_args && param_mode == ParamMode::Optional, - ) + let args = data + .args + .iter() + .filter_map(|arg| match arg { + AngleBracketedArg::Arg(arg) => Some(self.lower_generic_arg(arg, itctx.reborrow())), + AngleBracketedArg::Constraint(_) => None, + }) + .collect(); + let bindings = self.arena.alloc_from_iter(data.args.iter().filter_map(|arg| match arg { + AngleBracketedArg::Constraint(c) => { + Some(self.lower_assoc_ty_constraint(c, itctx.reborrow())) + } + AngleBracketedArg::Arg(_) => None, + })); + let ctor = GenericArgsCtor { args, bindings, parenthesized: false }; + (ctor, !has_non_lt_args && param_mode == ParamMode::Optional) } fn lower_parenthesized_parameter_data( diff --git a/src/librustc_ast_passes/ast_validation.rs b/src/librustc_ast_passes/ast_validation.rs index 885f2f939a653..e6f09a3a6cba5 100644 --- a/src/librustc_ast_passes/ast_validation.rs +++ b/src/librustc_ast_passes/ast_validation.rs @@ -639,6 +639,37 @@ impl<'a> AstValidator<'a> { .emit(); } } + + /// Enforce generic args coming before constraints in `<...>` of a path segment. + fn check_generic_args_before_constraints(&self, data: &AngleBracketedArgs) { + // Early exit in case it's partitioned as it should be. + if data.args.iter().is_partitioned(|arg| matches!(arg, AngleBracketedArg::Arg(_))) { + return; + } + // Find all generic argument coming after the first constraint... + let mut misplaced_args = Vec::new(); + let mut first = None; + for arg in &data.args { + match (arg, first) { + (AngleBracketedArg::Arg(a), Some(_)) => misplaced_args.push(a.span()), + (AngleBracketedArg::Constraint(c), None) => first = Some(c.span), + (AngleBracketedArg::Arg(_), None) | (AngleBracketedArg::Constraint(_), Some(_)) => { + } + } + } + // ...and then error: + self.err_handler() + .struct_span_err( + data.span, + "constraints in a path segment must come after generic arguments", + ) + .span_labels( + misplaced_args, + "this generic argument must come before the first constraint", + ) + .span_label(first.unwrap(), "the first constraint is provided here") + .emit(); + } } /// Checks that generic parameters are in the correct order, @@ -1008,17 +1039,20 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_generic_args(&mut self, _: Span, generic_args: &'a GenericArgs) { match *generic_args { GenericArgs::AngleBracketed(ref data) => { - walk_list!(self, visit_generic_arg, &data.args); - - // Type bindings such as `Item = impl Debug` in `Iterator` - // are allowed to contain nested `impl Trait`. - self.with_impl_trait(None, |this| { - walk_list!( - this, - visit_assoc_ty_constraint_from_generic_args, - &data.constraints - ); - }); + self.check_generic_args_before_constraints(data); + + for arg in &data.args { + match arg { + AngleBracketedArg::Arg(arg) => self.visit_generic_arg(arg), + // Type bindings such as `Item = impl Debug` in `Iterator` + // are allowed to contain nested `impl Trait`. + AngleBracketedArg::Constraint(constraint) => { + self.with_impl_trait(None, |this| { + this.visit_assoc_ty_constraint_from_generic_args(constraint); + }); + } + } + } } GenericArgs::Parenthesized(ref data) => { walk_list!(self, visit_ty, &data.inputs); diff --git a/src/librustc_ast_passes/lib.rs b/src/librustc_ast_passes/lib.rs index 10081d36754ba..bfe304419801d 100644 --- a/src/librustc_ast_passes/lib.rs +++ b/src/librustc_ast_passes/lib.rs @@ -1,10 +1,12 @@ -#![feature(bindings_after_at)] //! The `rustc_ast_passes` crate contains passes which validate the AST in `syntax` //! parsed by `rustc_parse` and then lowered, after the passes in this crate, //! by `rustc_ast_lowering`. //! //! The crate also contains other misc AST visitors, e.g. `node_count` and `show_span`. +#![feature(bindings_after_at)] +#![feature(iter_is_partitioned)] + pub mod ast_validation; pub mod feature_gate; pub mod node_count; diff --git a/src/librustc_ast_pretty/pprust.rs b/src/librustc_ast_pretty/pprust.rs index 2d6932ffbeedf..4aabbe7efbef4 100644 --- a/src/librustc_ast_pretty/pprust.rs +++ b/src/librustc_ast_pretty/pprust.rs @@ -796,31 +796,10 @@ impl<'a> PrintState<'a> for State<'a> { match *args { ast::GenericArgs::AngleBracketed(ref data) => { self.s.word("<"); - - self.commasep(Inconsistent, &data.args, |s, generic_arg| { - s.print_generic_arg(generic_arg) + self.commasep(Inconsistent, &data.args, |s, arg| match arg { + ast::AngleBracketedArg::Arg(a) => s.print_generic_arg(a), + ast::AngleBracketedArg::Constraint(c) => s.print_assoc_constraint(c), }); - - let mut comma = !data.args.is_empty(); - - for constraint in data.constraints.iter() { - if comma { - self.word_space(",") - } - self.print_ident(constraint.ident); - self.s.space(); - match constraint.kind { - ast::AssocTyConstraintKind::Equality { ref ty } => { - self.word_space("="); - self.print_type(ty); - } - ast::AssocTyConstraintKind::Bound { ref bounds } => { - self.print_type_bounds(":", &*bounds); - } - } - comma = true; - } - self.s.word(">") } @@ -891,6 +870,20 @@ impl<'a> State<'a> { } } + fn print_assoc_constraint(&mut self, constraint: &ast::AssocTyConstraint) { + self.print_ident(constraint.ident); + self.s.space(); + match &constraint.kind { + ast::AssocTyConstraintKind::Equality { ty } => { + self.word_space("="); + self.print_type(ty); + } + ast::AssocTyConstraintKind::Bound { bounds } => { + self.print_type_bounds(":", &*bounds); + } + } + } + crate fn print_generic_arg(&mut self, generic_arg: &GenericArg) { match generic_arg { GenericArg::Lifetime(lt) => self.print_lifetime(*lt), diff --git a/src/librustc_expand/build.rs b/src/librustc_expand/build.rs index 48ceaf5fccd8e..4d89bf79e7c67 100644 --- a/src/librustc_expand/build.rs +++ b/src/librustc_expand/build.rs @@ -36,7 +36,8 @@ impl<'a> ExtCtxt<'a> { idents.into_iter().map(|ident| ast::PathSegment::from_ident(ident.with_span_pos(span))), ); let args = if !args.is_empty() { - ast::AngleBracketedArgs { args, constraints: Vec::new(), span }.into() + let args = args.into_iter().map(ast::AngleBracketedArg::Arg).collect(); + ast::AngleBracketedArgs { args, span }.into() } else { None }; diff --git a/src/librustc_interface/util.rs b/src/librustc_interface/util.rs index c6f2d1b82fcf4..eff7dacef7953 100644 --- a/src/librustc_interface/util.rs +++ b/src/librustc_interface/util.rs @@ -634,17 +634,19 @@ impl<'a, 'b> ReplaceBodyWithLoop<'a, 'b> { match seg.args.as_ref().map(|generic_arg| &**generic_arg) { None => false, Some(&ast::GenericArgs::AngleBracketed(ref data)) => { - let types = data.args.iter().filter_map(|arg| match arg { - ast::GenericArg::Type(ty) => Some(ty), - _ => None, - }); - any_involves_impl_trait(types) - || data.constraints.iter().any(|c| match c.kind { + data.args.iter().any(|arg| match arg { + ast::AngleBracketedArg::Arg(arg) => match arg { + ast::GenericArg::Type(ty) => involves_impl_trait(ty), + ast::GenericArg::Lifetime(_) + | ast::GenericArg::Const(_) => false, + }, + ast::AngleBracketedArg::Constraint(c) => match c.kind { ast::AssocTyConstraintKind::Bound { .. } => true, ast::AssocTyConstraintKind::Equality { ref ty } => { involves_impl_trait(ty) } - }) + }, + }) } Some(&ast::GenericArgs::Parenthesized(ref data)) => { any_involves_impl_trait(data.inputs.iter()) diff --git a/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs index f88b4fe6ff0a8..2320dcba33c97 100644 --- a/src/librustc_parse/parser/path.rs +++ b/src/librustc_parse/parser/path.rs @@ -1,12 +1,9 @@ use super::ty::{AllowPlus, RecoverQPath}; use super::{Parser, TokenType}; use crate::maybe_whole; -use rustc_ast::ast::{ - self, AngleBracketedArgs, Ident, ParenthesizedArgs, Path, PathSegment, QSelf, -}; -use rustc_ast::ast::{ - AnonConst, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode, GenericArg, -}; +use rustc_ast::ast::{self, AngleBracketedArg, AngleBracketedArgs, GenericArg, ParenthesizedArgs}; +use rustc_ast::ast::{AnonConst, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode}; +use rustc_ast::ast::{Ident, Path, PathSegment, QSelf}; use rustc_ast::token::{self, Token}; use rustc_errors::{pluralize, Applicability, PResult}; use rustc_span::source_map::{BytePos, Span}; @@ -218,11 +215,11 @@ impl<'a> Parser<'a> { let lo = self.token.span; let args = if self.eat_lt() { // `<'a, T, A = U>` - let (args, constraints) = - self.parse_generic_args_with_leading_angle_bracket_recovery(style, lo)?; + let args = + self.parse_angle_args_with_leading_angle_bracket_recovery(style, lo)?; self.expect_gt()?; let span = lo.to(self.prev_token.span); - AngleBracketedArgs { args, constraints, span }.into() + AngleBracketedArgs { args, span }.into() } else { // `(T, U) -> R` let (inputs, _) = self.parse_paren_comma_seq(|p| p.parse_ty())?; @@ -251,18 +248,18 @@ impl<'a> Parser<'a> { /// Parses generic args (within a path segment) with recovery for extra leading angle brackets. /// For the purposes of understanding the parsing logic of generic arguments, this function - /// can be thought of being the same as just calling `self.parse_generic_args()` if the source + /// can be thought of being the same as just calling `self.parse_angle_args()` if the source /// had the correct amount of leading angle brackets. /// /// ```ignore (diagnostics) /// bar::<<<::Output>(); /// ^^ help: remove extra angle brackets /// ``` - fn parse_generic_args_with_leading_angle_bracket_recovery( + fn parse_angle_args_with_leading_angle_bracket_recovery( &mut self, style: PathStyle, lo: Span, - ) -> PResult<'a, (Vec, Vec)> { + ) -> PResult<'a, Vec> { // We need to detect whether there are extra leading left angle brackets and produce an // appropriate error and suggestion. This cannot be implemented by looking ahead at // upcoming tokens for a matching `>` character - if there are unmatched `<` tokens @@ -337,8 +334,8 @@ impl<'a> Parser<'a> { let snapshot = if is_first_invocation { Some(self.clone()) } else { None }; debug!("parse_generic_args_with_leading_angle_bracket_recovery: (snapshotting)"); - match self.parse_generic_args() { - Ok(value) => Ok(value), + match self.parse_angle_args() { + Ok(args) => Ok(args), Err(ref mut e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => { // Cancel error from being unable to find `>`. We know the error // must have been this due to a non-zero unmatched angle bracket @@ -381,29 +378,22 @@ impl<'a> Parser<'a> { .emit(); // Try again without unmatched angle bracket characters. - self.parse_generic_args() + self.parse_angle_args() } Err(e) => Err(e), } } - /// Parses (possibly empty) list of lifetime and type arguments and associated type bindings, + /// Parses (possibly empty) list of generic arguments / associated item constraints, /// possibly including trailing comma. - fn parse_generic_args(&mut self) -> PResult<'a, (Vec, Vec)> { + fn parse_angle_args(&mut self) -> PResult<'a, Vec> { let mut args = Vec::new(); - let mut constraints = Vec::new(); - let mut misplaced_assoc_ty_constraints: Vec = Vec::new(); - let mut assoc_ty_constraints: Vec = Vec::new(); - - let args_lo = self.token.span; - loop { if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { // Parse lifetime argument. - args.push(GenericArg::Lifetime(self.expect_lifetime())); - misplaced_assoc_ty_constraints.append(&mut assoc_ty_constraints); + args.push(AngleBracketedArg::Arg(GenericArg::Lifetime(self.expect_lifetime()))); } else if self.check_ident() - && self.look_ahead(1, |t| t == &token::Eq || t == &token::Colon) + && self.look_ahead(1, |t| matches!(t.kind, token::Eq | token::Colon)) { // Parse associated type constraint. let lo = self.token.span; @@ -411,9 +401,8 @@ impl<'a> Parser<'a> { let kind = if self.eat(&token::Eq) { AssocTyConstraintKind::Equality { ty: self.parse_ty()? } } else if self.eat(&token::Colon) { - AssocTyConstraintKind::Bound { - bounds: self.parse_generic_bounds(Some(self.prev_token.span))?, - } + let bounds = self.parse_generic_bounds(Some(self.prev_token.span))?; + AssocTyConstraintKind::Bound { bounds } } else { unreachable!(); }; @@ -425,8 +414,8 @@ impl<'a> Parser<'a> { self.sess.gated_spans.gate(sym::associated_type_bounds, span); } - constraints.push(AssocTyConstraint { id: ast::DUMMY_NODE_ID, ident, kind, span }); - assoc_ty_constraints.push(span); + let constraint = AssocTyConstraint { id: ast::DUMMY_NODE_ID, ident, kind, span }; + args.push(AngleBracketedArg::Constraint(constraint)); } else if self.check_const_arg() { // Parse const argument. let expr = if let token::OpenDelim(token::Brace) = self.token.kind { @@ -453,12 +442,10 @@ impl<'a> Parser<'a> { self.parse_literal_maybe_minus()? }; let value = AnonConst { id: ast::DUMMY_NODE_ID, value: expr }; - args.push(GenericArg::Const(value)); - misplaced_assoc_ty_constraints.append(&mut assoc_ty_constraints); + args.push(AngleBracketedArg::Arg(GenericArg::Const(value))); } else if self.check_type() { // Parse type argument. - args.push(GenericArg::Type(self.parse_ty()?)); - misplaced_assoc_ty_constraints.append(&mut assoc_ty_constraints); + args.push(AngleBracketedArg::Arg(GenericArg::Type(self.parse_ty()?))); } else { break; } @@ -468,23 +455,6 @@ impl<'a> Parser<'a> { } } - // FIXME: we would like to report this in ast_validation instead, but we currently do not - // preserve ordering of generic parameters with respect to associated type binding, so we - // lose that information after parsing. - if !misplaced_assoc_ty_constraints.is_empty() { - let mut err = self.struct_span_err( - args_lo.to(self.prev_token.span), - "associated type bindings must be declared after generic parameters", - ); - for span in misplaced_assoc_ty_constraints { - err.span_label( - span, - "this associated type binding should be moved after the generic parameters", - ); - } - err.emit(); - } - - Ok((args, constraints)) + Ok(args) } } diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index a80c3b72044ef..0829a82b932c3 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -783,7 +783,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { match **generic_args { ast::GenericArgs::AngleBracketed(ref data) => { for arg in &data.args { - if let ast::GenericArg::Type(ty) = arg { + if let ast::AngleBracketedArg::Arg(ast::GenericArg::Type(ty)) = arg { self.visit_ty(ty); } } @@ -849,7 +849,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { if let ast::GenericArgs::AngleBracketed(ref data) = **generic_args { for arg in &data.args { match arg { - ast::GenericArg::Type(ty) => self.visit_ty(ty), + ast::AngleBracketedArg::Arg(ast::GenericArg::Type(ty)) => self.visit_ty(ty), _ => {} } } diff --git a/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.rs b/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.rs new file mode 100644 index 0000000000000..afbd13e6fd9a8 --- /dev/null +++ b/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.rs @@ -0,0 +1,9 @@ +// check-pass + +#[cfg(FALSE)] +fn syntax() { + foo::(); + foo::(); +} + +fn main() {} diff --git a/src/test/ui/parser/issue-32214.rs b/src/test/ui/parser/issue-32214.rs index 82f7ce62b9457..ca30f5f1329d8 100644 --- a/src/test/ui/parser/issue-32214.rs +++ b/src/test/ui/parser/issue-32214.rs @@ -1,6 +1,6 @@ trait Trait { type Item; } pub fn test >() {} -//~^ ERROR associated type bindings must be declared after generic parameters +//~^ ERROR constraints in a path segment must come after generic arguments fn main() { } diff --git a/src/test/ui/parser/issue-32214.stderr b/src/test/ui/parser/issue-32214.stderr index 08b230a14f50e..ee99fe70811de 100644 --- a/src/test/ui/parser/issue-32214.stderr +++ b/src/test/ui/parser/issue-32214.stderr @@ -1,10 +1,11 @@ -error: associated type bindings must be declared after generic parameters - --> $DIR/issue-32214.rs:3:25 +error: constraints in a path segment must come after generic arguments + --> $DIR/issue-32214.rs:3:24 | LL | pub fn test >() {} - | -------^^^ - | | - | this associated type binding should be moved after the generic parameters + | ^-------^^-^ + | | | + | | this generic argument must come before the first constraint + | the first constraint is provided here error: aborting due to previous error diff --git a/src/test/ui/suggestions/suggest-move-types.rs b/src/test/ui/suggestions/suggest-move-types.rs index 6505a97de6e4b..d9d5351b5c2d4 100644 --- a/src/test/ui/suggestions/suggest-move-types.rs +++ b/src/test/ui/suggestions/suggest-move-types.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #![allow(warnings)] // This test verifies that the suggestion to move types before associated type bindings @@ -25,20 +23,22 @@ trait ThreeWithLifetime<'a, 'b, 'c, T, U, V> { type C; } -struct A> { //~ ERROR associated type bindings must be declared after generic parameters +struct A> { +//~^ ERROR constraints in a path segment must come after generic arguments m: M, t: T, } struct Al<'a, T, M: OneWithLifetime> { -//~^ ERROR associated type bindings must be declared after generic parameters +//~^ ERROR constraints in a path segment must come after generic arguments //~^^ ERROR type provided when a lifetime was expected m: M, t: &'a T, } -struct B> { //~ ERROR associated type bindings must be declared after generic parameters +struct B> { +//~^ ERROR constraints in a path segment must come after generic arguments m: M, t: T, u: U, @@ -46,7 +46,7 @@ struct B> { //~ ERROR associated ty } struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { -//~^ ERROR associated type bindings must be declared after generic parameters +//~^ ERROR constraints in a path segment must come after generic arguments //~^^ ERROR type provided when a lifetime was expected m: M, t: &'a T, @@ -54,7 +54,8 @@ struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { //~ ERROR associated type bindings must be declared after generic parameters +struct C> { +//~^ ERROR constraints in a path segment must come after generic arguments m: M, t: T, u: U, @@ -62,7 +63,7 @@ struct C> { //~ ERROR associated ty } struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { -//~^ ERROR associated type bindings must be declared after generic parameters +//~^ ERROR constraints in a path segment must come after generic arguments //~^^ ERROR lifetime provided when a type was expected m: M, t: &'a T, @@ -70,7 +71,8 @@ struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { //~ ERROR associated type bindings must be declared after generic parameters +struct D> { +//~^ ERROR constraints in a path segment must come after generic arguments m: M, t: T, u: U, @@ -78,7 +80,7 @@ struct D> { //~ ERROR associated ty } struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { -//~^ ERROR associated type bindings must be declared after generic parameters +//~^ ERROR constraints in a path segment must come after generic arguments //~^^ ERROR lifetime provided when a type was expected m: M, t: &'a T, diff --git a/src/test/ui/suggestions/suggest-move-types.stderr b/src/test/ui/suggestions/suggest-move-types.stderr index ac91813f92839..0225546b9e37b 100644 --- a/src/test/ui/suggestions/suggest-move-types.stderr +++ b/src/test/ui/suggestions/suggest-move-types.stderr @@ -1,81 +1,93 @@ -error: associated type bindings must be declared after generic parameters - --> $DIR/suggest-move-types.rs:28:20 +error: constraints in a path segment must come after generic arguments + --> $DIR/suggest-move-types.rs:26:19 | LL | struct A> { - | ----^^^ - | | - | this associated type binding should be moved after the generic parameters + | ^----^^-^ + | | | + | | this generic argument must come before the first constraint + | the first constraint is provided here -error: associated type bindings must be declared after generic parameters - --> $DIR/suggest-move-types.rs:34:37 +error: constraints in a path segment must come after generic arguments + --> $DIR/suggest-move-types.rs:33:36 | LL | struct Al<'a, T, M: OneWithLifetime> { - | ----^^^^^^^ - | | - | this associated type binding should be moved after the generic parameters + | ^----^^-^^--^ + | | | | + | | | this generic argument must come before the first constraint + | | this generic argument must come before the first constraint + | the first constraint is provided here -error: associated type bindings must be declared after generic parameters - --> $DIR/suggest-move-types.rs:41:28 +error: constraints in a path segment must come after generic arguments + --> $DIR/suggest-move-types.rs:40:27 | LL | struct B> { - | ----^^----^^----^^^^^^^^^ - | | | | - | | | this associated type binding should be moved after the generic parameters - | | this associated type binding should be moved after the generic parameters - | this associated type binding should be moved after the generic parameters + | ^----^^^^^^^^^^^^^^-^^-^^-^ + | | | | | + | | | | this generic argument must come before the first constraint + | | | this generic argument must come before the first constraint + | | this generic argument must come before the first constraint + | the first constraint is provided here -error: associated type bindings must be declared after generic parameters - --> $DIR/suggest-move-types.rs:48:53 +error: constraints in a path segment must come after generic arguments + --> $DIR/suggest-move-types.rs:48:52 | LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ----^^----^^----^^^^^^^^^^^^^^^^^^^^^ - | | | | - | | | this associated type binding should be moved after the generic parameters - | | this associated type binding should be moved after the generic parameters - | this associated type binding should be moved after the generic parameters + | ^----^^^^^^^^^^^^^^-^^-^^-^^--^^--^^--^ + | | | | | | | | + | | | | | | | this generic argument must come before the first constraint + | | | | | | this generic argument must come before the first constraint + | | | | | this generic argument must come before the first constraint + | | | | this generic argument must come before the first constraint + | | | this generic argument must come before the first constraint + | | this generic argument must come before the first constraint + | the first constraint is provided here -error: associated type bindings must be declared after generic parameters - --> $DIR/suggest-move-types.rs:57:28 +error: constraints in a path segment must come after generic arguments + --> $DIR/suggest-move-types.rs:57:27 | LL | struct C> { - | ^^^----^^----^^----^^^^^^ - | | | | - | | | this associated type binding should be moved after the generic parameters - | | this associated type binding should be moved after the generic parameters - | this associated type binding should be moved after the generic parameters + | ^^^^----^^^^^^^^^^^^^^-^^-^ + | | | | + | | | this generic argument must come before the first constraint + | | this generic argument must come before the first constraint + | the first constraint is provided here -error: associated type bindings must be declared after generic parameters - --> $DIR/suggest-move-types.rs:64:53 +error: constraints in a path segment must come after generic arguments + --> $DIR/suggest-move-types.rs:65:52 | LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ^^^^^^^----^^----^^----^^^^^^^^^^^^^^ - | | | | - | | | this associated type binding should be moved after the generic parameters - | | this associated type binding should be moved after the generic parameters - | this associated type binding should be moved after the generic parameters + | ^^^^^^^^----^^^^^^^^^^^^^^-^^--^^-^^--^ + | | | | | | + | | | | | this generic argument must come before the first constraint + | | | | this generic argument must come before the first constraint + | | | this generic argument must come before the first constraint + | | this generic argument must come before the first constraint + | the first constraint is provided here -error: associated type bindings must be declared after generic parameters - --> $DIR/suggest-move-types.rs:73:28 +error: constraints in a path segment must come after generic arguments + --> $DIR/suggest-move-types.rs:74:27 | LL | struct D> { - | ^^^----^^----^^^^^----^^^ - | | | | - | | | this associated type binding should be moved after the generic parameters - | | this associated type binding should be moved after the generic parameters - | this associated type binding should be moved after the generic parameters + | ^^^^----^^^^^^^^-^^^^^^^^-^ + | | | | + | | | this generic argument must come before the first constraint + | | this generic argument must come before the first constraint + | the first constraint is provided here -error: associated type bindings must be declared after generic parameters - --> $DIR/suggest-move-types.rs:80:53 +error: constraints in a path segment must come after generic arguments + --> $DIR/suggest-move-types.rs:82:52 | LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ^^^^^^^----^^----^^^^^^^^^----^^^^^^^ - | | | | - | | | this associated type binding should be moved after the generic parameters - | | this associated type binding should be moved after the generic parameters - | this associated type binding should be moved after the generic parameters + | ^^^^^^^^----^^^^^^^^-^^--^^^^^^^^-^^--^ + | | | | | | + | | | | | this generic argument must come before the first constraint + | | | | this generic argument must come before the first constraint + | | | this generic argument must come before the first constraint + | | this generic argument must come before the first constraint + | the first constraint is provided here error[E0747]: type provided when a lifetime was expected - --> $DIR/suggest-move-types.rs:34:43 + --> $DIR/suggest-move-types.rs:33:43 | LL | struct Al<'a, T, M: OneWithLifetime> { | ^ @@ -91,7 +103,7 @@ LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime $DIR/suggest-move-types.rs:64:56 + --> $DIR/suggest-move-types.rs:65:56 | LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { | ^^ @@ -99,7 +111,7 @@ LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime $DIR/suggest-move-types.rs:80:56 + --> $DIR/suggest-move-types.rs:82:56 | LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { | ^^ From abce4881e0553aed7683ed2ade6827b9de9aedaf Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sun, 22 Mar 2020 04:54:46 +0100 Subject: [PATCH 2/7] split parse_angle_args into loop / single step --- src/librustc_parse/parser/path.rs | 130 +++++++++++++++--------------- 1 file changed, 67 insertions(+), 63 deletions(-) diff --git a/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs index 2320dcba33c97..3e0fc3a5478d3 100644 --- a/src/librustc_parse/parser/path.rs +++ b/src/librustc_parse/parser/path.rs @@ -388,73 +388,77 @@ impl<'a> Parser<'a> { /// possibly including trailing comma. fn parse_angle_args(&mut self) -> PResult<'a, Vec> { let mut args = Vec::new(); - loop { - if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { - // Parse lifetime argument. - args.push(AngleBracketedArg::Arg(GenericArg::Lifetime(self.expect_lifetime()))); - } else if self.check_ident() - && self.look_ahead(1, |t| matches!(t.kind, token::Eq | token::Colon)) - { - // Parse associated type constraint. - let lo = self.token.span; - let ident = self.parse_ident()?; - let kind = if self.eat(&token::Eq) { - AssocTyConstraintKind::Equality { ty: self.parse_ty()? } - } else if self.eat(&token::Colon) { - let bounds = self.parse_generic_bounds(Some(self.prev_token.span))?; - AssocTyConstraintKind::Bound { bounds } - } else { - unreachable!(); - }; - - let span = lo.to(self.prev_token.span); - - // Gate associated type bounds, e.g., `Iterator`. - if let AssocTyConstraintKind::Bound { .. } = kind { - self.sess.gated_spans.gate(sym::associated_type_bounds, span); - } - - let constraint = AssocTyConstraint { id: ast::DUMMY_NODE_ID, ident, kind, span }; - args.push(AngleBracketedArg::Constraint(constraint)); - } else if self.check_const_arg() { - // Parse const argument. - let expr = if let token::OpenDelim(token::Brace) = self.token.kind { - self.parse_block_expr( - None, - self.token.span, - BlockCheckMode::Default, - ast::AttrVec::new(), - )? - } else if self.token.is_ident() { - // FIXME(const_generics): to distinguish between idents for types and consts, - // we should introduce a GenericArg::Ident in the AST and distinguish when - // lowering to the HIR. For now, idents for const args are not permitted. - if self.token.is_bool_lit() { - self.parse_literal_maybe_minus()? - } else { - let span = self.token.span; - let msg = "identifiers may currently not be used for const generics"; - self.struct_span_err(span, msg).emit(); - let block = self.mk_block_err(span); - self.mk_expr(span, ast::ExprKind::Block(block, None), ast::AttrVec::new()) - } - } else { - self.parse_literal_maybe_minus()? - }; - let value = AnonConst { id: ast::DUMMY_NODE_ID, value: expr }; - args.push(AngleBracketedArg::Arg(GenericArg::Const(value))); - } else if self.check_type() { - // Parse type argument. - args.push(AngleBracketedArg::Arg(GenericArg::Type(self.parse_ty()?))); - } else { - break; - } - + while let Some(arg) = self.parse_angle_arg()? { + args.push(arg); if !self.eat(&token::Comma) { break; } } - Ok(args) } + + /// Parses a single argument in the angle arguments `<...>` of a path segment. + fn parse_angle_arg(&mut self) -> PResult<'a, Option> { + let arg = if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { + // Parse lifetime argument. + AngleBracketedArg::Arg(GenericArg::Lifetime(self.expect_lifetime())) + } else if self.check_ident() + && self.look_ahead(1, |t| matches!(t.kind, token::Eq | token::Colon)) + { + // Parse associated type constraint. + let lo = self.token.span; + let ident = self.parse_ident()?; + let kind = if self.eat(&token::Eq) { + AssocTyConstraintKind::Equality { ty: self.parse_ty()? } + } else if self.eat(&token::Colon) { + let bounds = self.parse_generic_bounds(Some(self.prev_token.span))?; + AssocTyConstraintKind::Bound { bounds } + } else { + unreachable!(); + }; + + let span = lo.to(self.prev_token.span); + + // Gate associated type bounds, e.g., `Iterator`. + if let AssocTyConstraintKind::Bound { .. } = kind { + self.sess.gated_spans.gate(sym::associated_type_bounds, span); + } + + let constraint = AssocTyConstraint { id: ast::DUMMY_NODE_ID, ident, kind, span }; + AngleBracketedArg::Constraint(constraint) + } else if self.check_const_arg() { + // Parse const argument. + let expr = if let token::OpenDelim(token::Brace) = self.token.kind { + self.parse_block_expr( + None, + self.token.span, + BlockCheckMode::Default, + ast::AttrVec::new(), + )? + } else if self.token.is_ident() { + // FIXME(const_generics): to distinguish between idents for types and consts, + // we should introduce a GenericArg::Ident in the AST and distinguish when + // lowering to the HIR. For now, idents for const args are not permitted. + if self.token.is_bool_lit() { + self.parse_literal_maybe_minus()? + } else { + let span = self.token.span; + let msg = "identifiers may currently not be used for const generics"; + self.struct_span_err(span, msg).emit(); + let block = self.mk_block_err(span); + self.mk_expr(span, ast::ExprKind::Block(block, None), ast::AttrVec::new()) + } + } else { + self.parse_literal_maybe_minus()? + }; + let value = AnonConst { id: ast::DUMMY_NODE_ID, value: expr }; + AngleBracketedArg::Arg(GenericArg::Const(value)) + } else if self.check_type() { + // Parse type argument. + AngleBracketedArg::Arg(GenericArg::Type(self.parse_ty()?)) + } else { + return Ok(None); + }; + Ok(Some(arg)) + } } From aa4999ec6963162c38b74a4d50857d1cf2bfbc26 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sun, 22 Mar 2020 05:01:38 +0100 Subject: [PATCH 3/7] parse_angle_arg: parse constraints first --- src/librustc_parse/parser/path.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs index 3e0fc3a5478d3..9018564252464 100644 --- a/src/librustc_parse/parser/path.rs +++ b/src/librustc_parse/parser/path.rs @@ -399,10 +399,7 @@ impl<'a> Parser<'a> { /// Parses a single argument in the angle arguments `<...>` of a path segment. fn parse_angle_arg(&mut self) -> PResult<'a, Option> { - let arg = if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { - // Parse lifetime argument. - AngleBracketedArg::Arg(GenericArg::Lifetime(self.expect_lifetime())) - } else if self.check_ident() + let arg = if self.check_ident() && self.look_ahead(1, |t| matches!(t.kind, token::Eq | token::Colon)) { // Parse associated type constraint. @@ -426,6 +423,9 @@ impl<'a> Parser<'a> { let constraint = AssocTyConstraint { id: ast::DUMMY_NODE_ID, ident, kind, span }; AngleBracketedArg::Constraint(constraint) + } else if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { + // Parse lifetime argument. + AngleBracketedArg::Arg(GenericArg::Lifetime(self.expect_lifetime())) } else if self.check_const_arg() { // Parse const argument. let expr = if let token::OpenDelim(token::Brace) = self.token.kind { From f91de44d073e40d6d4682ad41d61cadcc0fcedeb Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sun, 22 Mar 2020 05:12:51 +0100 Subject: [PATCH 4/7] extract parse_generic_arg --- src/librustc_parse/parser/path.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs index 9018564252464..f6d0f27eb18bf 100644 --- a/src/librustc_parse/parser/path.rs +++ b/src/librustc_parse/parser/path.rs @@ -399,8 +399,7 @@ impl<'a> Parser<'a> { /// Parses a single argument in the angle arguments `<...>` of a path segment. fn parse_angle_arg(&mut self) -> PResult<'a, Option> { - let arg = if self.check_ident() - && self.look_ahead(1, |t| matches!(t.kind, token::Eq | token::Colon)) + if self.check_ident() && self.look_ahead(1, |t| matches!(t.kind, token::Eq | token::Colon)) { // Parse associated type constraint. let lo = self.token.span; @@ -422,10 +421,18 @@ impl<'a> Parser<'a> { } let constraint = AssocTyConstraint { id: ast::DUMMY_NODE_ID, ident, kind, span }; - AngleBracketedArg::Constraint(constraint) - } else if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { + Ok(Some(AngleBracketedArg::Constraint(constraint))) + } else { + Ok(self.parse_generic_arg()?.map(AngleBracketedArg::Arg)) + } + } + + /// Parse a generic argument in a path segment. + /// This does not include constraints, e.g., `Item = u8`, which is handled in `parse_angle_arg`. + fn parse_generic_arg(&mut self) -> PResult<'a, Option> { + let arg = if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { // Parse lifetime argument. - AngleBracketedArg::Arg(GenericArg::Lifetime(self.expect_lifetime())) + GenericArg::Lifetime(self.expect_lifetime()) } else if self.check_const_arg() { // Parse const argument. let expr = if let token::OpenDelim(token::Brace) = self.token.kind { @@ -451,11 +458,10 @@ impl<'a> Parser<'a> { } else { self.parse_literal_maybe_minus()? }; - let value = AnonConst { id: ast::DUMMY_NODE_ID, value: expr }; - AngleBracketedArg::Arg(GenericArg::Const(value)) + GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value: expr }) } else if self.check_type() { // Parse type argument. - AngleBracketedArg::Arg(GenericArg::Type(self.parse_ty()?)) + GenericArg::Type(self.parse_ty()?) } else { return Ok(None); }; From 2972bb37b8dc6e64ec31cf9b79245974e30f417b Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sun, 22 Mar 2020 06:09:24 +0100 Subject: [PATCH 5/7] parse: improve recovery for assoc eq constraints. --- src/librustc_parse/parser/path.rs | 44 ++++++++++++++++++- .../parser/recover-assoc-const-constraint.rs | 7 +++ .../recover-assoc-const-constraint.stderr | 20 +++++++++ .../parser/recover-assoc-eq-missing-term.rs | 6 +++ .../recover-assoc-eq-missing-term.stderr | 17 +++++++ .../recover-assoc-lifetime-constraint.rs | 6 +++ .../recover-assoc-lifetime-constraint.stderr | 12 +++++ 7 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/parser/recover-assoc-const-constraint.rs create mode 100644 src/test/ui/parser/recover-assoc-const-constraint.stderr create mode 100644 src/test/ui/parser/recover-assoc-eq-missing-term.rs create mode 100644 src/test/ui/parser/recover-assoc-eq-missing-term.stderr create mode 100644 src/test/ui/parser/recover-assoc-lifetime-constraint.rs create mode 100644 src/test/ui/parser/recover-assoc-lifetime-constraint.stderr diff --git a/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs index f6d0f27eb18bf..d23adf4ffe395 100644 --- a/src/librustc_parse/parser/path.rs +++ b/src/librustc_parse/parser/path.rs @@ -4,6 +4,7 @@ use crate::maybe_whole; use rustc_ast::ast::{self, AngleBracketedArg, AngleBracketedArgs, GenericArg, ParenthesizedArgs}; use rustc_ast::ast::{AnonConst, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode}; use rustc_ast::ast::{Ident, Path, PathSegment, QSelf}; +use rustc_ast::ptr::P; use rustc_ast::token::{self, Token}; use rustc_errors::{pluralize, Applicability, PResult}; use rustc_span::source_map::{BytePos, Span}; @@ -405,7 +406,8 @@ impl<'a> Parser<'a> { let lo = self.token.span; let ident = self.parse_ident()?; let kind = if self.eat(&token::Eq) { - AssocTyConstraintKind::Equality { ty: self.parse_ty()? } + let ty = self.parse_assoc_equality_term(ident, self.prev_token.span)?; + AssocTyConstraintKind::Equality { ty } } else if self.eat(&token::Colon) { let bounds = self.parse_generic_bounds(Some(self.prev_token.span))?; AssocTyConstraintKind::Bound { bounds } @@ -427,6 +429,46 @@ impl<'a> Parser<'a> { } } + /// Parse the term to the right of an associated item equality constraint. + /// That is, parse `` in `Item = `. + /// Right now, this only admits types in ``. + fn parse_assoc_equality_term(&mut self, ident: Ident, eq: Span) -> PResult<'a, P> { + let arg = self.parse_generic_arg()?; + let span = ident.span.to(self.prev_token.span); + match arg { + Some(GenericArg::Type(ty)) => return Ok(ty), + Some(GenericArg::Const(expr)) => { + self.struct_span_err(span, "cannot constrain an associated constant to a value") + .span_label(ident.span, "the value constrains this associated constant") + .span_label(expr.value.span, "the value is given in this expression") + .emit(); + } + Some(GenericArg::Lifetime(lt)) => { + self.struct_span_err(span, "associated lifetimes are not supported") + .span_label(lt.ident.span, "the lifetime is given here") + .help("if you meant to specify a trait object, write `dyn Trait + 'lifetime`") + .emit(); + } + None => { + self.struct_span_err(span, "missing type to the right of `=`") + .span_suggestion( + span, + "to constrain the associated type, add a type after `=`", + format!("{} = TheType", ident), + Applicability::HasPlaceholders, + ) + .span_suggestion( + eq, + &format!("remove the `=` if `{}` is a type", ident), + String::new(), + Applicability::MaybeIncorrect, + ) + .emit(); + } + } + Ok(self.mk_ty(span, ast::TyKind::Err)) + } + /// Parse a generic argument in a path segment. /// This does not include constraints, e.g., `Item = u8`, which is handled in `parse_angle_arg`. fn parse_generic_arg(&mut self) -> PResult<'a, Option> { diff --git a/src/test/ui/parser/recover-assoc-const-constraint.rs b/src/test/ui/parser/recover-assoc-const-constraint.rs new file mode 100644 index 0000000000000..06be3cdcc1a95 --- /dev/null +++ b/src/test/ui/parser/recover-assoc-const-constraint.rs @@ -0,0 +1,7 @@ +#[cfg(FALSE)] +fn syntax() { + bar::(); //~ ERROR cannot constrain an associated constant to a value + bar::(); //~ ERROR cannot constrain an associated constant to a value +} + +fn main() {} diff --git a/src/test/ui/parser/recover-assoc-const-constraint.stderr b/src/test/ui/parser/recover-assoc-const-constraint.stderr new file mode 100644 index 0000000000000..bf61720793672 --- /dev/null +++ b/src/test/ui/parser/recover-assoc-const-constraint.stderr @@ -0,0 +1,20 @@ +error: cannot constrain an associated constant to a value + --> $DIR/recover-assoc-const-constraint.rs:3:11 + | +LL | bar::(); + | ----^^^-- + | | | + | | the value is given in this expression + | the value constrains this associated constant + +error: cannot constrain an associated constant to a value + --> $DIR/recover-assoc-const-constraint.rs:4:11 + | +LL | bar::(); + | ----^^^------ + | | | + | | the value is given in this expression + | the value constrains this associated constant + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/parser/recover-assoc-eq-missing-term.rs b/src/test/ui/parser/recover-assoc-eq-missing-term.rs new file mode 100644 index 0000000000000..d800df8236b05 --- /dev/null +++ b/src/test/ui/parser/recover-assoc-eq-missing-term.rs @@ -0,0 +1,6 @@ +#[cfg(FALSE)] +fn syntax() { + bar::(); //~ ERROR missing type to the right of `=` +} + +fn main() {} diff --git a/src/test/ui/parser/recover-assoc-eq-missing-term.stderr b/src/test/ui/parser/recover-assoc-eq-missing-term.stderr new file mode 100644 index 0000000000000..5eb5d6879e907 --- /dev/null +++ b/src/test/ui/parser/recover-assoc-eq-missing-term.stderr @@ -0,0 +1,17 @@ +error: missing type to the right of `=` + --> $DIR/recover-assoc-eq-missing-term.rs:3:11 + | +LL | bar::(); + | ^^^^^^ + | +help: to constrain the associated type, add a type after `=` + | +LL | bar::(); + | ^^^^^^^^^^^^^^ +help: remove the `=` if `Item` is a type + | +LL | bar::(); + | -- + +error: aborting due to previous error + diff --git a/src/test/ui/parser/recover-assoc-lifetime-constraint.rs b/src/test/ui/parser/recover-assoc-lifetime-constraint.rs new file mode 100644 index 0000000000000..558fcdfe1776f --- /dev/null +++ b/src/test/ui/parser/recover-assoc-lifetime-constraint.rs @@ -0,0 +1,6 @@ +#[cfg(FALSE)] +fn syntax() { + bar::(); //~ ERROR associated lifetimes are not supported +} + +fn main() {} diff --git a/src/test/ui/parser/recover-assoc-lifetime-constraint.stderr b/src/test/ui/parser/recover-assoc-lifetime-constraint.stderr new file mode 100644 index 0000000000000..79437533d7c0b --- /dev/null +++ b/src/test/ui/parser/recover-assoc-lifetime-constraint.stderr @@ -0,0 +1,12 @@ +error: associated lifetimes are not supported + --> $DIR/recover-assoc-lifetime-constraint.rs:3:11 + | +LL | bar::(); + | ^^^^^^^-- + | | + | the lifetime is given here + | + = help: if you meant to specify a trait object, write `dyn Trait + 'lifetime` + +error: aborting due to previous error + From 2ddc35997cd7147f007943e50ef5f942d20ad7c7 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Fri, 27 Mar 2020 07:39:10 +0100 Subject: [PATCH 6/7] address some review comments --- src/librustc_ast_lowering/path.rs | 6 +- src/librustc_ast_passes/ast_validation.rs | 6 +- src/librustc_parse/parser/path.rs | 14 +-- src/test/ui/parser/issue-32214.rs | 2 +- src/test/ui/parser/issue-32214.stderr | 9 +- .../recover-assoc-const-constraint.stderr | 8 +- .../parser/recover-assoc-eq-missing-term.rs | 2 +- .../recover-assoc-eq-missing-term.stderr | 12 +-- src/test/ui/suggestions/suggest-move-types.rs | 16 ++-- .../ui/suggestions/suggest-move-types.stderr | 88 +++++++------------ 10 files changed, 68 insertions(+), 95 deletions(-) diff --git a/src/librustc_ast_lowering/path.rs b/src/librustc_ast_lowering/path.rs index f15a4555e5f13..dde734756517c 100644 --- a/src/librustc_ast_lowering/path.rs +++ b/src/librustc_ast_lowering/path.rs @@ -367,9 +367,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { mut itctx: ImplTraitContext<'_, 'hir>, ) -> (GenericArgsCtor<'hir>, bool) { let has_non_lt_args = data.args.iter().any(|arg| match arg { - AngleBracketedArg::Arg(ast::GenericArg::Lifetime(_)) => false, - AngleBracketedArg::Arg(ast::GenericArg::Type(_) | ast::GenericArg::Const(_)) - | AngleBracketedArg::Constraint(_) => true, + AngleBracketedArg::Arg(ast::GenericArg::Lifetime(_)) + | AngleBracketedArg::Constraint(_) => false, + AngleBracketedArg::Arg(ast::GenericArg::Type(_) | ast::GenericArg::Const(_)) => true, }); let args = data .args diff --git a/src/librustc_ast_passes/ast_validation.rs b/src/librustc_ast_passes/ast_validation.rs index e6f09a3a6cba5..592d3bf2e052f 100644 --- a/src/librustc_ast_passes/ast_validation.rs +++ b/src/librustc_ast_passes/ast_validation.rs @@ -660,12 +660,8 @@ impl<'a> AstValidator<'a> { // ...and then error: self.err_handler() .struct_span_err( - data.span, - "constraints in a path segment must come after generic arguments", - ) - .span_labels( misplaced_args, - "this generic argument must come before the first constraint", + "generic arguments must come before the first constraint", ) .span_label(first.unwrap(), "the first constraint is provided here") .emit(); diff --git a/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs index d23adf4ffe395..9fa7bc027b878 100644 --- a/src/librustc_parse/parser/path.rs +++ b/src/librustc_parse/parser/path.rs @@ -439,8 +439,8 @@ impl<'a> Parser<'a> { Some(GenericArg::Type(ty)) => return Ok(ty), Some(GenericArg::Const(expr)) => { self.struct_span_err(span, "cannot constrain an associated constant to a value") - .span_label(ident.span, "the value constrains this associated constant") - .span_label(expr.value.span, "the value is given in this expression") + .span_label(ident.span, "this associated constant...") + .span_label(expr.value.span, "...cannot be constrained to this value") .emit(); } Some(GenericArg::Lifetime(lt)) => { @@ -450,15 +450,17 @@ impl<'a> Parser<'a> { .emit(); } None => { - self.struct_span_err(span, "missing type to the right of `=`") + let after_eq = eq.shrink_to_hi(); + let before_next = self.token.span.shrink_to_lo(); + self.struct_span_err(after_eq.to(before_next), "missing type to the right of `=`") .span_suggestion( - span, + self.sess.source_map().next_point(eq).to(before_next), "to constrain the associated type, add a type after `=`", - format!("{} = TheType", ident), + " TheType".to_string(), Applicability::HasPlaceholders, ) .span_suggestion( - eq, + eq.to(before_next), &format!("remove the `=` if `{}` is a type", ident), String::new(), Applicability::MaybeIncorrect, diff --git a/src/test/ui/parser/issue-32214.rs b/src/test/ui/parser/issue-32214.rs index ca30f5f1329d8..1379eeb58e6e8 100644 --- a/src/test/ui/parser/issue-32214.rs +++ b/src/test/ui/parser/issue-32214.rs @@ -1,6 +1,6 @@ trait Trait { type Item; } pub fn test >() {} -//~^ ERROR constraints in a path segment must come after generic arguments +//~^ ERROR generic arguments must come before the first constraint fn main() { } diff --git a/src/test/ui/parser/issue-32214.stderr b/src/test/ui/parser/issue-32214.stderr index ee99fe70811de..d25d3a0963304 100644 --- a/src/test/ui/parser/issue-32214.stderr +++ b/src/test/ui/parser/issue-32214.stderr @@ -1,10 +1,9 @@ -error: constraints in a path segment must come after generic arguments - --> $DIR/issue-32214.rs:3:24 +error: generic arguments must come before the first constraint + --> $DIR/issue-32214.rs:3:34 | LL | pub fn test >() {} - | ^-------^^-^ - | | | - | | this generic argument must come before the first constraint + | ------- ^ + | | | the first constraint is provided here error: aborting due to previous error diff --git a/src/test/ui/parser/recover-assoc-const-constraint.stderr b/src/test/ui/parser/recover-assoc-const-constraint.stderr index bf61720793672..c6733b33faa58 100644 --- a/src/test/ui/parser/recover-assoc-const-constraint.stderr +++ b/src/test/ui/parser/recover-assoc-const-constraint.stderr @@ -4,8 +4,8 @@ error: cannot constrain an associated constant to a value LL | bar::(); | ----^^^-- | | | - | | the value is given in this expression - | the value constrains this associated constant + | | ...cannot be constrained to this value + | this associated constant... error: cannot constrain an associated constant to a value --> $DIR/recover-assoc-const-constraint.rs:4:11 @@ -13,8 +13,8 @@ error: cannot constrain an associated constant to a value LL | bar::(); | ----^^^------ | | | - | | the value is given in this expression - | the value constrains this associated constant + | | ...cannot be constrained to this value + | this associated constant... error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/recover-assoc-eq-missing-term.rs b/src/test/ui/parser/recover-assoc-eq-missing-term.rs index d800df8236b05..4b42c44dc64e5 100644 --- a/src/test/ui/parser/recover-assoc-eq-missing-term.rs +++ b/src/test/ui/parser/recover-assoc-eq-missing-term.rs @@ -1,6 +1,6 @@ #[cfg(FALSE)] fn syntax() { - bar::(); //~ ERROR missing type to the right of `=` + bar::(); //~ ERROR missing type to the right of `=` } fn main() {} diff --git a/src/test/ui/parser/recover-assoc-eq-missing-term.stderr b/src/test/ui/parser/recover-assoc-eq-missing-term.stderr index 5eb5d6879e907..6e41e139220ae 100644 --- a/src/test/ui/parser/recover-assoc-eq-missing-term.stderr +++ b/src/test/ui/parser/recover-assoc-eq-missing-term.stderr @@ -1,16 +1,16 @@ error: missing type to the right of `=` - --> $DIR/recover-assoc-eq-missing-term.rs:3:11 + --> $DIR/recover-assoc-eq-missing-term.rs:3:17 | -LL | bar::(); - | ^^^^^^ +LL | bar::(); + | ^^^ | help: to constrain the associated type, add a type after `=` | -LL | bar::(); - | ^^^^^^^^^^^^^^ +LL | bar::(); + | ^^^^^^^ help: remove the `=` if `Item` is a type | -LL | bar::(); +LL | bar::(); | -- error: aborting due to previous error diff --git a/src/test/ui/suggestions/suggest-move-types.rs b/src/test/ui/suggestions/suggest-move-types.rs index d9d5351b5c2d4..27930626a6209 100644 --- a/src/test/ui/suggestions/suggest-move-types.rs +++ b/src/test/ui/suggestions/suggest-move-types.rs @@ -24,21 +24,21 @@ trait ThreeWithLifetime<'a, 'b, 'c, T, U, V> { } struct A> { -//~^ ERROR constraints in a path segment must come after generic arguments +//~^ ERROR generic arguments must come before the first constraint m: M, t: T, } struct Al<'a, T, M: OneWithLifetime> { -//~^ ERROR constraints in a path segment must come after generic arguments +//~^ ERROR generic arguments must come before the first constraint //~^^ ERROR type provided when a lifetime was expected m: M, t: &'a T, } struct B> { -//~^ ERROR constraints in a path segment must come after generic arguments +//~^ ERROR generic arguments must come before the first constraint m: M, t: T, u: U, @@ -46,7 +46,7 @@ struct B> { } struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { -//~^ ERROR constraints in a path segment must come after generic arguments +//~^ ERROR generic arguments must come before the first constraint //~^^ ERROR type provided when a lifetime was expected m: M, t: &'a T, @@ -55,7 +55,7 @@ struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { -//~^ ERROR constraints in a path segment must come after generic arguments +//~^ ERROR generic arguments must come before the first constraint m: M, t: T, u: U, @@ -63,7 +63,7 @@ struct C> { } struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { -//~^ ERROR constraints in a path segment must come after generic arguments +//~^ ERROR generic arguments must come before the first constraint //~^^ ERROR lifetime provided when a type was expected m: M, t: &'a T, @@ -72,7 +72,7 @@ struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { -//~^ ERROR constraints in a path segment must come after generic arguments +//~^ ERROR generic arguments must come before the first constraint m: M, t: T, u: U, @@ -80,7 +80,7 @@ struct D> { } struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { -//~^ ERROR constraints in a path segment must come after generic arguments +//~^ ERROR generic arguments must come before the first constraint //~^^ ERROR lifetime provided when a type was expected m: M, t: &'a T, diff --git a/src/test/ui/suggestions/suggest-move-types.stderr b/src/test/ui/suggestions/suggest-move-types.stderr index 0225546b9e37b..a0a8c3fc3ba58 100644 --- a/src/test/ui/suggestions/suggest-move-types.stderr +++ b/src/test/ui/suggestions/suggest-move-types.stderr @@ -1,89 +1,65 @@ -error: constraints in a path segment must come after generic arguments - --> $DIR/suggest-move-types.rs:26:19 +error: generic arguments must come before the first constraint + --> $DIR/suggest-move-types.rs:26:26 | LL | struct A> { - | ^----^^-^ - | | | - | | this generic argument must come before the first constraint + | ---- ^ + | | | the first constraint is provided here -error: constraints in a path segment must come after generic arguments - --> $DIR/suggest-move-types.rs:33:36 +error: generic arguments must come before the first constraint + --> $DIR/suggest-move-types.rs:33:43 | LL | struct Al<'a, T, M: OneWithLifetime> { - | ^----^^-^^--^ - | | | | - | | | this generic argument must come before the first constraint - | | this generic argument must come before the first constraint + | ---- ^ ^^ + | | | the first constraint is provided here -error: constraints in a path segment must come after generic arguments - --> $DIR/suggest-move-types.rs:40:27 +error: generic arguments must come before the first constraint + --> $DIR/suggest-move-types.rs:40:46 | LL | struct B> { - | ^----^^^^^^^^^^^^^^-^^-^^-^ - | | | | | - | | | | this generic argument must come before the first constraint - | | | this generic argument must come before the first constraint - | | this generic argument must come before the first constraint + | ---- ^ ^ ^ + | | | the first constraint is provided here -error: constraints in a path segment must come after generic arguments - --> $DIR/suggest-move-types.rs:48:52 +error: generic arguments must come before the first constraint + --> $DIR/suggest-move-types.rs:48:71 | LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ^----^^^^^^^^^^^^^^-^^-^^-^^--^^--^^--^ - | | | | | | | | - | | | | | | | this generic argument must come before the first constraint - | | | | | | this generic argument must come before the first constraint - | | | | | this generic argument must come before the first constraint - | | | | this generic argument must come before the first constraint - | | | this generic argument must come before the first constraint - | | this generic argument must come before the first constraint + | ---- ^ ^ ^ ^^ ^^ ^^ + | | | the first constraint is provided here -error: constraints in a path segment must come after generic arguments - --> $DIR/suggest-move-types.rs:57:27 +error: generic arguments must come before the first constraint + --> $DIR/suggest-move-types.rs:57:49 | LL | struct C> { - | ^^^^----^^^^^^^^^^^^^^-^^-^ - | | | | - | | | this generic argument must come before the first constraint - | | this generic argument must come before the first constraint + | ---- ^ ^ + | | | the first constraint is provided here -error: constraints in a path segment must come after generic arguments - --> $DIR/suggest-move-types.rs:65:52 +error: generic arguments must come before the first constraint + --> $DIR/suggest-move-types.rs:65:78 | LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ^^^^^^^^----^^^^^^^^^^^^^^-^^--^^-^^--^ - | | | | | | - | | | | | this generic argument must come before the first constraint - | | | | this generic argument must come before the first constraint - | | | this generic argument must come before the first constraint - | | this generic argument must come before the first constraint + | ---- ^ ^^ ^ ^^ + | | | the first constraint is provided here -error: constraints in a path segment must come after generic arguments - --> $DIR/suggest-move-types.rs:74:27 +error: generic arguments must come before the first constraint + --> $DIR/suggest-move-types.rs:74:43 | LL | struct D> { - | ^^^^----^^^^^^^^-^^^^^^^^-^ - | | | | - | | | this generic argument must come before the first constraint - | | this generic argument must come before the first constraint + | ---- ^ ^ + | | | the first constraint is provided here -error: constraints in a path segment must come after generic arguments - --> $DIR/suggest-move-types.rs:82:52 +error: generic arguments must come before the first constraint + --> $DIR/suggest-move-types.rs:82:72 | LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ^^^^^^^^----^^^^^^^^-^^--^^^^^^^^-^^--^ - | | | | | | - | | | | | this generic argument must come before the first constraint - | | | | this generic argument must come before the first constraint - | | | this generic argument must come before the first constraint - | | this generic argument must come before the first constraint + | ---- ^ ^^ ^ ^^ + | | | the first constraint is provided here error[E0747]: type provided when a lifetime was expected From 42c5cfdfda24529f424a6175fa1a48e5542cb824 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Fri, 27 Mar 2020 21:52:09 +0100 Subject: [PATCH 7/7] add the label back but make it shorter --- src/librustc_ast_passes/ast_validation.rs | 3 +- src/test/ui/parser/issue-32214.stderr | 2 +- .../ui/suggestions/suggest-move-types.stderr | 46 +++++++++++++------ 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/librustc_ast_passes/ast_validation.rs b/src/librustc_ast_passes/ast_validation.rs index 592d3bf2e052f..de7ae10723f4d 100644 --- a/src/librustc_ast_passes/ast_validation.rs +++ b/src/librustc_ast_passes/ast_validation.rs @@ -660,10 +660,11 @@ impl<'a> AstValidator<'a> { // ...and then error: self.err_handler() .struct_span_err( - misplaced_args, + misplaced_args.clone(), "generic arguments must come before the first constraint", ) .span_label(first.unwrap(), "the first constraint is provided here") + .span_labels(misplaced_args, "generic argument") .emit(); } } diff --git a/src/test/ui/parser/issue-32214.stderr b/src/test/ui/parser/issue-32214.stderr index d25d3a0963304..742f4fdc38bbd 100644 --- a/src/test/ui/parser/issue-32214.stderr +++ b/src/test/ui/parser/issue-32214.stderr @@ -2,7 +2,7 @@ error: generic arguments must come before the first constraint --> $DIR/issue-32214.rs:3:34 | LL | pub fn test >() {} - | ------- ^ + | ------- ^ generic argument | | | the first constraint is provided here diff --git a/src/test/ui/suggestions/suggest-move-types.stderr b/src/test/ui/suggestions/suggest-move-types.stderr index a0a8c3fc3ba58..4dd0613757a95 100644 --- a/src/test/ui/suggestions/suggest-move-types.stderr +++ b/src/test/ui/suggestions/suggest-move-types.stderr @@ -2,7 +2,7 @@ error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:26:26 | LL | struct A> { - | ---- ^ + | ---- ^ generic argument | | | the first constraint is provided here @@ -10,56 +10,72 @@ error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:33:43 | LL | struct Al<'a, T, M: OneWithLifetime> { - | ---- ^ ^^ - | | + | ---- ^ ^^ generic argument + | | | + | | generic argument | the first constraint is provided here error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:40:46 | LL | struct B> { - | ---- ^ ^ ^ - | | + | ---- ^ ^ ^ generic argument + | | | | + | | | generic argument + | | generic argument | the first constraint is provided here error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:48:71 | LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ---- ^ ^ ^ ^^ ^^ ^^ - | | + | ---- ^ ^ ^ ^^ ^^ ^^ generic argument + | | | | | | | + | | | | | | generic argument + | | | | | generic argument + | | | | generic argument + | | | generic argument + | | generic argument | the first constraint is provided here error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:57:49 | LL | struct C> { - | ---- ^ ^ - | | + | ---- ^ ^ generic argument + | | | + | | generic argument | the first constraint is provided here error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:65:78 | LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ---- ^ ^^ ^ ^^ - | | + | ---- ^ ^^ ^ ^^ generic argument + | | | | | + | | | | generic argument + | | | generic argument + | | generic argument | the first constraint is provided here error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:74:43 | LL | struct D> { - | ---- ^ ^ - | | + | ---- ^ ^ generic argument + | | | + | | generic argument | the first constraint is provided here error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:82:72 | LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ---- ^ ^^ ^ ^^ - | | + | ---- ^ ^^ ^ ^^ generic argument + | | | | | + | | | | generic argument + | | | generic argument + | | generic argument | the first constraint is provided here error[E0747]: type provided when a lifetime was expected