From c2e0e71a0943b16e6e6f8aad88aaea7b6f60dea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 29 Mar 2020 11:31:58 -0700 Subject: [PATCH] Suggest correct order for arguments when encountering early constraints When encountering constraints before type arguments or lifetimes, suggest the correct order. --- src/librustc_ast/ast.rs | 4 +- src/librustc_ast_passes/ast_validation.rs | 56 ++++++++++++------- src/librustc_ast_pretty/pprust.rs | 4 +- src/librustc_errors/diagnostic_builder.rs | 14 +++++ src/test/ui/parser/issue-32214.stderr | 6 +- .../ui/suggestions/suggest-move-types.stderr | 36 ++++++------ 6 files changed, 74 insertions(+), 46 deletions(-) diff --git a/src/librustc_ast/ast.rs b/src/librustc_ast/ast.rs index 6586280d214..f91cbe51d85 100644 --- a/src/librustc_ast/ast.rs +++ b/src/librustc_ast/ast.rs @@ -300,8 +300,8 @@ pub enum GenericBound { impl GenericBound { pub fn span(&self) -> Span { match self { - &GenericBound::Trait(ref t, ..) => t.span, - &GenericBound::Outlives(ref l) => l.ident.span, + GenericBound::Trait(ref t, ..) => t.span, + GenericBound::Outlives(ref l) => l.ident.span, } } } diff --git a/src/librustc_ast_passes/ast_validation.rs b/src/librustc_ast_passes/ast_validation.rs index 4c7edfc83fa..4f076c963c1 100644 --- a/src/librustc_ast_passes/ast_validation.rs +++ b/src/librustc_ast_passes/ast_validation.rs @@ -640,6 +640,32 @@ fn deny_items(&self, trait_items: &[P], ident_span: Span) { } } + fn suggest_correct_generic_order(&self, data: &AngleBracketedArgs) -> String { + // Lifetimes always come first. + let lt_sugg = data.args.iter().filter_map(|arg| match arg { + AngleBracketedArg::Arg(lt @ GenericArg::Lifetime(_)) => { + Some(pprust::to_string(|s| s.print_generic_arg(lt))) + } + _ => None, + }); + let args_sugg = data.args.iter().filter_map(|a| match a { + AngleBracketedArg::Arg(GenericArg::Lifetime(_)) => None, + AngleBracketedArg::Arg(arg) => Some(pprust::to_string(|s| s.print_generic_arg(arg))), + AngleBracketedArg::Constraint(_) => None, + }); + // Cosntraints always come last. + let constraint_sugg = data.args.iter().filter_map(|a| match a { + AngleBracketedArg::Arg(_) => None, + AngleBracketedArg::Constraint(c) => { + Some(pprust::to_string(|s| s.print_assoc_constraint(c))) + } + }); + format!( + "<{}>", + lt_sugg.chain(args_sugg).chain(constraint_sugg).collect::>().join(", ") + ) + } + /// 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. @@ -663,20 +689,7 @@ fn check_generic_args_before_constraints(&self, data: &AngleBracketedArgs) { _ => None, }) .collect::>(); - let snippet_span = match &constraint_spans[..] { - [single] => *single, - [first, .., last] => first.to(*last), - [] => unreachable!(), - }; - let removal_span = match &arg_spans[..] { - [first, ..] => snippet_span.until(*first), - [] => unreachable!(), - }; - let sugg_span = match &arg_spans[..] { - [.., last] => last.shrink_to_hi(), - [] => unreachable!(), - }; - let snippet = self.session.source_map().span_to_snippet(snippet_span).unwrap(); + let args_len = arg_spans.len(); let constraint_len = constraint_spans.len(); // ...and then error: self.err_handler() @@ -693,13 +706,14 @@ fn check_generic_args_before_constraints(&self, data: &AngleBracketedArgs) { ), ) .span_labels(arg_spans, "generic argument") - .multipart_suggestion( - "move the constraints after the generic arguments", - vec![ - (removal_span, String::new()), - (sugg_span.shrink_to_lo(), ", ".to_string()), - (sugg_span, snippet), - ], + .span_suggestion_verbose( + data.span, + &format!( + "move the constraint{} after the generic argument{}", + pluralize!(constraint_len), + pluralize!(args_len) + ), + self.suggest_correct_generic_order(&data), Applicability::MachineApplicable, ) .emit(); diff --git a/src/librustc_ast_pretty/pprust.rs b/src/librustc_ast_pretty/pprust.rs index 4aabbe7efbe..5ac96c5c8cb 100644 --- a/src/librustc_ast_pretty/pprust.rs +++ b/src/librustc_ast_pretty/pprust.rs @@ -870,7 +870,7 @@ pub fn print_opt_lifetime(&mut self, lifetime: &Option) { } } - fn print_assoc_constraint(&mut self, constraint: &ast::AssocTyConstraint) { + pub fn print_assoc_constraint(&mut self, constraint: &ast::AssocTyConstraint) { self.print_ident(constraint.ident); self.s.space(); match &constraint.kind { @@ -884,7 +884,7 @@ fn print_assoc_constraint(&mut self, constraint: &ast::AssocTyConstraint) { } } - crate fn print_generic_arg(&mut self, generic_arg: &GenericArg) { + pub fn print_generic_arg(&mut self, generic_arg: &GenericArg) { match generic_arg { GenericArg::Lifetime(lt) => self.print_lifetime(*lt), GenericArg::Type(ty) => self.print_type(ty), diff --git a/src/librustc_errors/diagnostic_builder.rs b/src/librustc_errors/diagnostic_builder.rs index fffae0bfd24..2dbd9f4e52f 100644 --- a/src/librustc_errors/diagnostic_builder.rs +++ b/src/librustc_errors/diagnostic_builder.rs @@ -315,6 +315,20 @@ pub fn span_suggestion_short( self } + pub fn span_suggestion_verbose( + &mut self, + sp: Span, + msg: &str, + suggestion: String, + applicability: Applicability, + ) -> &mut Self { + if !self.0.allow_suggestions { + return self; + } + self.0.diagnostic.span_suggestion_verbose(sp, msg, suggestion, applicability); + self + } + pub fn span_suggestion_hidden( &mut self, sp: Span, diff --git a/src/test/ui/parser/issue-32214.stderr b/src/test/ui/parser/issue-32214.stderr index 959a1ab121b..8daf76993bc 100644 --- a/src/test/ui/parser/issue-32214.stderr +++ b/src/test/ui/parser/issue-32214.stderr @@ -6,10 +6,10 @@ LL | pub fn test >() {} | | | the constraint is provided here | -help: move the constraints after the generic arguments +help: move the constraint after the generic argument | -LL | pub fn test >() {} - | --^^^^^^^ +LL | pub fn test >() {} + | ^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/suggestions/suggest-move-types.stderr b/src/test/ui/suggestions/suggest-move-types.stderr index 3d2b35fcf45..e8cf54e2767 100644 --- a/src/test/ui/suggestions/suggest-move-types.stderr +++ b/src/test/ui/suggestions/suggest-move-types.stderr @@ -6,10 +6,10 @@ LL | struct A> { | | | the constraint is provided here | -help: move the constraints after the generic arguments +help: move the constraint after the generic argument | -LL | struct A> { - | --^^^^ +LL | struct A> { + | ^^^^^^^^^^^ error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:33:43 @@ -20,10 +20,10 @@ LL | struct Al<'a, T, M: OneWithLifetime> { | | generic argument | the constraint is provided here | -help: move the constraints after the generic arguments +help: move the constraint after the generic arguments | -LL | struct Al<'a, T, M: OneWithLifetime> { - | -- ^^^^ +LL | struct Al<'a, T, M: OneWithLifetime<'a, T, A = ()>> { + | ^^^^^^^^^^^^^^^ error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:40:46 @@ -39,8 +39,8 @@ LL | struct B> { | help: move the constraints after the generic arguments | -LL | struct B> { - | -- ^^^^^^^^^^^^^^^^ +LL | struct B> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:48:71 @@ -59,8 +59,8 @@ LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | -- ^^^^^^^^^^^^^^^^ +LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A = (), B = (), C = ()>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:57:28 @@ -76,8 +76,8 @@ LL | struct C> { | help: move the constraints after the generic arguments | -LL | struct C> { - | -- ^^^^^^^^^^^^^^^^ +LL | struct C> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:65:53 @@ -96,8 +96,8 @@ LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | -- ^^^^^^^^^^^^^^^^ +LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A = (), B = (), C = ()>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:74:28 @@ -113,8 +113,8 @@ LL | struct D> { | help: move the constraints after the generic arguments | -LL | struct D> { - | -- ^^^^^^^^^^^^^^^^^^^ +LL | struct D> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:82:53 @@ -133,8 +133,8 @@ LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | -- ^^^^^^^^^^^^^^^^^^^^^^^ +LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A = (), B = (), C = ()>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0747]: type provided when a lifetime was expected --> $DIR/suggest-move-types.rs:33:43