Skip to content

Add suggestion for moving type declaration before associated type bindings in generic arguments. #57886

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 2 commits into from
Jan 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 107 additions & 27 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5530,22 +5530,31 @@ impl<'a> Parser<'a> {
fn parse_generic_args(&mut self) -> PResult<'a, (Vec<GenericArg>, Vec<TypeBinding>)> {
let mut args = Vec::new();
let mut bindings = Vec::new();

let mut seen_type = false;
let mut seen_binding = false;

let mut last_comma_span = None;
let mut first_type_or_binding_span: Option<Span> = None;
let mut first_binding_span: Option<Span> = None;

let mut bad_lifetime_pos = vec![];
let mut last_comma_span = None;
let mut suggestions = vec![];
let mut bad_type_pos = vec![];

let mut lifetime_suggestions = vec![];
let mut type_suggestions = vec![];
loop {
if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
// Parse lifetime argument.
args.push(GenericArg::Lifetime(self.expect_lifetime()));

if seen_type || seen_binding {
let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span);
bad_lifetime_pos.push(self.prev_span);

if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) {
suggestions.push((remove_sp, String::new()));
suggestions.push((
lifetime_suggestions.push((remove_sp, String::new()));
lifetime_suggestions.push((
first_type_or_binding_span.unwrap().shrink_to_lo(),
format!("{}, ", snippet)));
}
Expand All @@ -5563,24 +5572,29 @@ impl<'a> Parser<'a> {
ty,
span,
});

seen_binding = true;
if first_type_or_binding_span.is_none() {
first_type_or_binding_span = Some(span);
}
if first_binding_span.is_none() {
first_binding_span = Some(span);
}
} else if self.check_type() {
// Parse type argument.
let ty_param = self.parse_ty()?;
if seen_binding {
self.struct_span_err(
ty_param.span,
"type parameters must be declared prior to associated type bindings"
)
.span_label(
ty_param.span,
"must be declared prior to associated type bindings",
)
.emit();
let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span);
bad_type_pos.push(self.prev_span);

if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) {
type_suggestions.push((remove_sp, String::new()));
type_suggestions.push((
first_binding_span.unwrap().shrink_to_lo(),
format!("{}, ", snippet)));
}
}

if first_type_or_binding_span.is_none() {
first_type_or_binding_span = Some(ty_param.span);
}
Expand All @@ -5596,27 +5610,93 @@ impl<'a> Parser<'a> {
last_comma_span = Some(self.prev_span);
}
}
if !bad_lifetime_pos.is_empty() {
let mut err = self.struct_span_err(

self.maybe_report_incorrect_generic_argument_order(
bad_lifetime_pos, bad_type_pos, lifetime_suggestions, type_suggestions
);

Ok((args, bindings))
}

/// Maybe report an error about incorrect generic argument order - "lifetime parameters
/// must be declared before type parameters", "type parameters must be declared before
/// associated type bindings" or both.
fn maybe_report_incorrect_generic_argument_order(
&self,
bad_lifetime_pos: Vec<Span>,
bad_type_pos: Vec<Span>,
lifetime_suggestions: Vec<(Span, String)>,
type_suggestions: Vec<(Span, String)>,
) {
let mut err = if !bad_lifetime_pos.is_empty() && !bad_type_pos.is_empty() {
let mut positions = bad_lifetime_pos.clone();
positions.extend_from_slice(&bad_type_pos);

self.struct_span_err(
positions,
"generic arguments must declare lifetimes, types and associated type bindings in \
that order",
)
} else if !bad_lifetime_pos.is_empty() {
self.struct_span_err(
bad_lifetime_pos.clone(),
"lifetime parameters must be declared prior to type parameters"
);
)
} else if !bad_type_pos.is_empty() {
self.struct_span_err(
bad_type_pos.clone(),
"type parameters must be declared prior to associated type bindings"
)
} else {
return;
};

if !bad_lifetime_pos.is_empty() {
for sp in &bad_lifetime_pos {
err.span_label(*sp, "must be declared prior to type parameters");
}
if !suggestions.is_empty() {
err.multipart_suggestion_with_applicability(
&format!(
"move the lifetime parameter{} prior to the first type parameter",
if bad_lifetime_pos.len() > 1 { "s" } else { "" },
),
suggestions,
Applicability::MachineApplicable,
);
}

if !bad_type_pos.is_empty() {
for sp in &bad_type_pos {
err.span_label(*sp, "must be declared prior to associated type bindings");
}
err.emit();
}
Ok((args, bindings))

if !lifetime_suggestions.is_empty() && !type_suggestions.is_empty() {
let mut suggestions = lifetime_suggestions;
suggestions.extend_from_slice(&type_suggestions);

let plural = bad_lifetime_pos.len() + bad_type_pos.len() > 1;
err.multipart_suggestion_with_applicability(
&format!(
"move the parameter{}",
if plural { "s" } else { "" },
),
suggestions,
Applicability::MachineApplicable,
);
} else if !lifetime_suggestions.is_empty() {
err.multipart_suggestion_with_applicability(
&format!(
"move the lifetime parameter{} prior to the first type parameter",
if bad_lifetime_pos.len() > 1 { "s" } else { "" },
),
lifetime_suggestions,
Applicability::MachineApplicable,
);
} else if !type_suggestions.is_empty() {
err.multipart_suggestion_with_applicability(
&format!(
"move the type parameter{} prior to the first associated type binding",
if bad_type_pos.len() > 1 { "s" } else { "" },
),
type_suggestions,
Applicability::MachineApplicable,
);
}

err.emit();
}

/// Parses an optional `where` clause and places it in `generics`.
Expand Down
4 changes: 4 additions & 0 deletions src/test/ui/parser/issue-32214.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ error: type parameters must be declared prior to associated type bindings
|
LL | pub fn test<W, I: Trait<Item=(), W> >() {}
| ^ must be declared prior to associated type bindings
help: move the type parameter prior to the first associated type binding
|
LL | pub fn test<W, I: Trait<W, Item=()> >() {}
| ^^ --

error: aborting due to previous error

85 changes: 85 additions & 0 deletions src/test/ui/suggestions/suggest-move-types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// ignore-tidy-linelength

#![allow(warnings)]

// This test verifies that the suggestion to move types before associated type bindings
// is correct.

trait One<T> {
type A;
}

trait OneWithLifetime<'a, T> {
type A;
}

trait Three<T, U, V> {
type A;
type B;
type C;
}

trait ThreeWithLifetime<'a, 'b, 'c, T, U, V> {
type A;
type B;
type C;
}

struct A<T, M: One<A=(), T>> { //~ ERROR type parameters must be declared
m: M,
t: T,
}


struct Al<'a, T, M: OneWithLifetime<A=(), T, 'a>> {
//~^ ERROR generic arguments must declare lifetimes, types and associated type bindings in that order
m: M,
t: &'a T,
}

struct B<T, U, V, M: Three<A=(), B=(), C=(), T, U, V>> { //~ ERROR type parameters must be declared
m: M,
t: T,
u: U,
v: V,
}

struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<A=(), B=(), C=(), T, U, V, 'a, 'b, 'c>> {
//~^ ERROR generic arguments must declare lifetimes, types and associated type bindings in that order
m: M,
t: &'a T,
u: &'b U,
v: &'c V,
}

struct C<T, U, V, M: Three<T, A=(), B=(), C=(), U, V>> { //~ ERROR type parameters must be declared
m: M,
t: T,
u: U,
v: V,
}

struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<T, 'a, A=(), B=(), C=(), U, 'b, V, 'c>> {
//~^ ERROR generic arguments must declare lifetimes, types and associated type bindings in that order
m: M,
t: &'a T,
u: &'b U,
v: &'c V,
}

struct D<T, U, V, M: Three<T, A=(), B=(), U, C=(), V>> { //~ ERROR type parameters must be declared
m: M,
t: T,
u: U,
v: V,
}

struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<T, 'a, A=(), B=(), U, 'b, C=(), V, 'c>> {
//~^ ERROR generic arguments must declare lifetimes, types and associated type bindings in that order
m: M,
t: &'a T,
u: &'b U,
v: &'c V,
}

fn main() {}
107 changes: 107 additions & 0 deletions src/test/ui/suggestions/suggest-move-types.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
error: type parameters must be declared prior to associated type bindings
--> $DIR/suggest-move-types.rs:28:26
|
LL | struct A<T, M: One<A=(), T>> { //~ ERROR type parameters must be declared
| ^ must be declared prior to associated type bindings
help: move the type parameter prior to the first associated type binding
|
LL | struct A<T, M: One<T, A=()>> { //~ ERROR type parameters must be declared
| ^^ --

error: generic arguments must declare lifetimes, types and associated type bindings in that order
--> $DIR/suggest-move-types.rs:34:46
|
LL | struct Al<'a, T, M: OneWithLifetime<A=(), T, 'a>> {
| ^ ^^ must be declared prior to type parameters
| |
| must be declared prior to associated type bindings
help: move the parameters
|
LL | struct Al<'a, T, M: OneWithLifetime<'a, T, A=()>> {
| ^^^ ^^ --

error: type parameters must be declared prior to associated type bindings
--> $DIR/suggest-move-types.rs:40:46
|
LL | struct B<T, U, V, M: Three<A=(), B=(), C=(), T, U, V>> { //~ ERROR type parameters must be declared
| ^ ^ ^ must be declared prior to associated type bindings
| | |
| | must be declared prior to associated type bindings
| must be declared prior to associated type bindings
help: move the type parameters prior to the first associated type binding
|
LL | struct B<T, U, V, M: Three<T, U, V, A=(), B=(), C=()>> { //~ ERROR type parameters must be declared
| ^^ ^^ ^^ --

error: generic arguments must declare lifetimes, types and associated type bindings in that order
--> $DIR/suggest-move-types.rs:47:80
|
LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<A=(), B=(), C=(), T, U, V, 'a, 'b, 'c>> {
| ^ ^ ^ ^^ ^^ ^^ must be declared prior to type parameters
| | | | | |
| | | | | must be declared prior to type parameters
| | | | must be declared prior to type parameters
| | | must be declared prior to associated type bindings
| | must be declared prior to associated type bindings
| must be declared prior to associated type bindings
help: move the parameters
|
LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A=(), B=(), C=()>> {
| ^^^ ^^^ ^^^ ^^ ^^ ^^ --

error: type parameters must be declared prior to associated type bindings
--> $DIR/suggest-move-types.rs:55:49
|
LL | struct C<T, U, V, M: Three<T, A=(), B=(), C=(), U, V>> { //~ ERROR type parameters must be declared
| ^ ^ must be declared prior to associated type bindings
| |
| must be declared prior to associated type bindings
help: move the type parameters prior to the first associated type binding
|
LL | struct C<T, U, V, M: Three<T, U, V, A=(), B=(), C=()>> { //~ ERROR type parameters must be declared
| ^^ ^^ --

error: generic arguments must declare lifetimes, types and associated type bindings in that order
--> $DIR/suggest-move-types.rs:62:56
|
LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<T, 'a, A=(), B=(), C=(), U, 'b, V, 'c>> {
| ^^ ^ ^^ ^ ^^ must be declared prior to type parameters
| | | | |
| | | | must be declared prior to associated type bindings
| | | must be declared prior to type parameters
| | must be declared prior to associated type bindings
| must be declared prior to type parameters
help: move the parameters
|
LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A=(), B=(), C=()>> {
| ^^^ ^^^ ^^^ -- ^^ ^^ --

error: type parameters must be declared prior to associated type bindings
--> $DIR/suggest-move-types.rs:70:43
|
LL | struct D<T, U, V, M: Three<T, A=(), B=(), U, C=(), V>> { //~ ERROR type parameters must be declared
| ^ ^ must be declared prior to associated type bindings
| |
| must be declared prior to associated type bindings
help: move the type parameters prior to the first associated type binding
|
LL | struct D<T, U, V, M: Three<T, U, V, A=(), B=(), C=()>> { //~ ERROR type parameters must be declared
| ^^ ^^ -- --

error: generic arguments must declare lifetimes, types and associated type bindings in that order
--> $DIR/suggest-move-types.rs:77:56
|
LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<T, 'a, A=(), B=(), U, 'b, C=(), V, 'c>> {
| ^^ ^ ^^ ^ ^^ must be declared prior to type parameters
| | | | |
| | | | must be declared prior to associated type bindings
| | | must be declared prior to type parameters
| | must be declared prior to associated type bindings
| must be declared prior to type parameters
help: move the parameters
|
LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A=(), B=(), C=()>> {
| ^^^ ^^^ ^^^ -- ^^ ^^ -- --

error: aborting due to 8 previous errors