Skip to content

Commit

Permalink
Handle incorrect placement of parentheses in trait bounds more gracef…
Browse files Browse the repository at this point in the history
…ully

Fix #84772.
  • Loading branch information
estebank committed May 4, 2021
1 parent 8a9fa36 commit 6b64202
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 3 deletions.
39 changes: 36 additions & 3 deletions compiler/rustc_parse/src/parser/ty.rs
Expand Up @@ -470,7 +470,7 @@ impl<'a> Parser<'a> {
/// Is a `dyn B0 + ... + Bn` type allowed here?
fn is_explicit_dyn_type(&mut self) -> bool {
self.check_keyword(kw::Dyn)
&& (self.token.uninterpolated_span().rust_2018()
&& (!self.token.uninterpolated_span().rust_2015()
|| self.look_ahead(1, |t| {
t.can_begin_bound() && !can_continue_type_after_non_fn_ident(t)
}))
Expand Down Expand Up @@ -539,7 +539,21 @@ impl<'a> Parser<'a> {
) -> PResult<'a, GenericBounds> {
let mut bounds = Vec::new();
let mut negative_bounds = Vec::new();
while self.can_begin_bound() {

while self.can_begin_bound() || self.token.is_keyword(kw::Dyn) {
if self.token.is_keyword(kw::Dyn) {
// Account for `&dyn Trait + dyn Other`.
self.struct_span_err(self.token.span, "invalid `dyn` keyword")
.help("`dyn` is only needed at the start of a trait `+`-separated list")
.span_suggestion(
self.token.span,
"remove this keyword",
String::new(),
Applicability::MachineApplicable,
)
.emit();
self.bump();
}
match self.parse_generic_bound()? {
Ok(bound) => bounds.push(bound),
Err(neg_sp) => negative_bounds.push(neg_sp),
Expand Down Expand Up @@ -721,7 +735,26 @@ impl<'a> Parser<'a> {
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
let path = self.parse_path(PathStyle::Type)?;
if has_parens {
self.expect(&token::CloseDelim(token::Paren))?;
if self.token.is_like_plus() {
// Someone has written something like `&dyn (Trait + Other)`. The correct code
// would be `&(dyn Trait + Other)`, but we don't have access to the appropriate
// span to suggest that. When written as `&dyn Trait + Other`, an appropriate
// suggestion is given.
let bounds = vec![];
self.parse_remaining_bounds(bounds, true)?;
self.expect(&token::CloseDelim(token::Paren))?;
let sp = vec![lo, self.prev_token.span];
let sugg: Vec<_> = sp.iter().map(|sp| (*sp, String::new())).collect();
self.struct_span_err(sp, "incorrect braces around trait bounds")
.multipart_suggestion(
"remove the parentheses",
sugg,
Applicability::MachineApplicable,
)
.emit();
} else {
self.expect(&token::CloseDelim(token::Paren))?;
}
}

let modifier = modifiers.to_trait_bound_modifier();
Expand Down
17 changes: 17 additions & 0 deletions src/test/ui/parser/trait-object-delimiters.rs
@@ -0,0 +1,17 @@
// edition:2018

fn foo1(_: &dyn Drop + AsRef<str>) {} //~ ERROR ambiguous `+` in a type
//~^ ERROR only auto traits can be used as additional traits in a trait object

fn foo2(_: &dyn (Drop + AsRef<str>)) {} //~ ERROR incorrect braces around trait bounds

fn foo3(_: &dyn {Drop + AsRef<str>}) {} //~ ERROR expected parameter name, found `{`
//~^ ERROR expected one of `!`, `(`, `)`, `,`, `?`, `for`, lifetime, or path, found `{`
//~| ERROR at least one trait is required for an object type

fn foo4(_: &dyn <Drop + AsRef<str>>) {} //~ ERROR expected identifier, found `<`

fn foo5(_: &(dyn Drop + dyn AsRef<str>)) {} //~ ERROR invalid `dyn` keyword
//~^ ERROR only auto traits can be used as additional traits in a trait object

fn main() {}
77 changes: 77 additions & 0 deletions src/test/ui/parser/trait-object-delimiters.stderr
@@ -0,0 +1,77 @@
error: ambiguous `+` in a type
--> $DIR/trait-object-delimiters.rs:3:13
|
LL | fn foo1(_: &dyn Drop + AsRef<str>) {}
| ^^^^^^^^^^^^^^^^^^^^^ help: use parentheses to disambiguate: `(dyn Drop + AsRef<str>)`

error: incorrect braces around trait bounds
--> $DIR/trait-object-delimiters.rs:6:17
|
LL | fn foo2(_: &dyn (Drop + AsRef<str>)) {}
| ^ ^
|
help: remove the parentheses
|
LL | fn foo2(_: &dyn Drop + AsRef<str>) {}
| -- --

error: expected parameter name, found `{`
--> $DIR/trait-object-delimiters.rs:8:17
|
LL | fn foo3(_: &dyn {Drop + AsRef<str>}) {}
| ^ expected parameter name

error: expected one of `!`, `(`, `)`, `,`, `?`, `for`, lifetime, or path, found `{`
--> $DIR/trait-object-delimiters.rs:8:17
|
LL | fn foo3(_: &dyn {Drop + AsRef<str>}) {}
| -^ expected one of 8 possible tokens
| |
| help: missing `,`

error: expected identifier, found `<`
--> $DIR/trait-object-delimiters.rs:12:17
|
LL | fn foo4(_: &dyn <Drop + AsRef<str>>) {}
| ^ expected identifier

error: invalid `dyn` keyword
--> $DIR/trait-object-delimiters.rs:14:25
|
LL | fn foo5(_: &(dyn Drop + dyn AsRef<str>)) {}
| ^^^ help: remove this keyword
|
= help: `dyn` is only needed at the start of a trait `+`-separated list

error[E0225]: only auto traits can be used as additional traits in a trait object
--> $DIR/trait-object-delimiters.rs:3:24
|
LL | fn foo1(_: &dyn Drop + AsRef<str>) {}
| ---- ^^^^^^^^^^ additional non-auto trait
| |
| first non-auto trait
|
= help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Drop + AsRef<str> {}`
= note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>

error[E0224]: at least one trait is required for an object type
--> $DIR/trait-object-delimiters.rs:8:13
|
LL | fn foo3(_: &dyn {Drop + AsRef<str>}) {}
| ^^^

error[E0225]: only auto traits can be used as additional traits in a trait object
--> $DIR/trait-object-delimiters.rs:14:29
|
LL | fn foo5(_: &(dyn Drop + dyn AsRef<str>)) {}
| ---- ^^^^^^^^^^ additional non-auto trait
| |
| first non-auto trait
|
= help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Drop + AsRef<str> {}`
= note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>

error: aborting due to 9 previous errors

Some errors have detailed explanations: E0224, E0225.
For more information about an error, try `rustc --explain E0224`.

0 comments on commit 6b64202

Please sign in to comment.