From e590b934998c85175b85cb13842163a27a221dc7 Mon Sep 17 00:00:00 2001 From: Ezra Shaw Date: Sat, 14 Jan 2023 21:20:20 +1300 Subject: [PATCH 1/2] make error emitted on `impl &Trait` nicer --- compiler/rustc_ast/src/ast.rs | 3 +- compiler/rustc_parse/src/parser/ty.rs | 39 +++++++++-- tests/ui/generics/issue-106694.rs | 24 +++++++ tests/ui/generics/issue-106694.stderr | 93 +++++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 tests/ui/generics/issue-106694.rs create mode 100644 tests/ui/generics/issue-106694.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 7de594719ddc4..9317579f70dd5 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2032,7 +2032,8 @@ impl Clone for Ty { impl Ty { pub fn peel_refs(&self) -> &Self { let mut final_ty = self; - while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind { + while let TyKind::Ref(_, MutTy { ty, .. }) | TyKind::Ptr(MutTy { ty, .. }) = &final_ty.kind + { final_ty = ty; } final_ty diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index a6f702e542869..aedebd0fb7062 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -727,11 +727,13 @@ impl<'a> Parser<'a> { let mut bounds = Vec::new(); let mut negative_bounds = Vec::new(); + // In addition to looping while we find generic bounds: + // We continue even if we find a keyword. This is necessary for error recovery on, + // for example, `impl fn()`. The only keyword that can go after generic bounds is + // `where`, so stop if it's it. + // We also continue if we find types (not traits), again for error recovery. while self.can_begin_bound() - // Continue even if we find a keyword. - // This is necessary for error recover on, for example, `impl fn()`. - // - // The only keyword that can go after generic bounds is `where`, so stop if it's it. + || self.token.can_begin_type() || (self.token.is_reserved_ident() && !self.token.is_keyword(kw::Where)) { if self.token.is_keyword(kw::Dyn) { @@ -938,6 +940,35 @@ impl<'a> Parser<'a> { && self.look_ahead(1, |tok| tok.kind == TokenKind::OpenDelim(Delimiter::Parenthesis)) && let Some(path) = self.recover_path_from_fn() { + path + } else if !self.token.is_path_start() && self.token.can_begin_type() && let Ok(ty) = self.parse_ty_no_plus() { + // Instead of finding a path (a trait), we found a type. + let mut err = self.struct_span_err(ty.span, "expected a trait, found type"); + + // If we can recover, try to extract a path from the type. Note + // that we do not use the try operator when parsing the type because + // if it fails then we get a parser error which we don't want (we're trying + // to recover from errors, not make more). + let path = if self.may_recover() + && matches!(ty.kind, TyKind::Ptr(..) | TyKind::Ref(..)) + && let TyKind::Path(_, path) = &ty.peel_refs().kind { + // Just get the indirection part of the type. + let span = ty.span.until(path.span); + + err.span_suggestion_verbose( + span, + "consider removing the indirection", + "", + Applicability::MaybeIncorrect, + ); + + path.clone() + } else { + return Err(err); + }; + + err.emit(); + path } else { self.parse_path(PathStyle::Type)? diff --git a/tests/ui/generics/issue-106694.rs b/tests/ui/generics/issue-106694.rs new file mode 100644 index 0000000000000..c4b02ee81ec8f --- /dev/null +++ b/tests/ui/generics/issue-106694.rs @@ -0,0 +1,24 @@ +trait Trait {} + +fn foo(_: impl &Trait) {} +//~^ ERROR expected a trait, found type + +fn bar(_: T) {} +//~^ ERROR expected a trait, found type + +fn partially_correct_impl(_: impl &*const &Trait + Copy) {} +//~^ ERROR expected a trait, found type + +fn foo_bad(_: impl &BadTrait) {} +//~^ ERROR expected a trait, found type +//~^^ ERROR cannot find trait `BadTrait` in this scope + +fn bar_bad(_: T) {} +//~^ ERROR expected a trait, found type +//~^^ ERROR cannot find trait `BadTrait` in this scope + +fn partially_correct_impl_bad(_: impl &*const &BadTrait + Copy) {} +//~^ ERROR expected a trait, found type +//~^^ ERROR cannot find trait `BadTrait` in this scope + +fn main() {} diff --git a/tests/ui/generics/issue-106694.stderr b/tests/ui/generics/issue-106694.stderr new file mode 100644 index 0000000000000..235b8982a99c6 --- /dev/null +++ b/tests/ui/generics/issue-106694.stderr @@ -0,0 +1,93 @@ +error: expected a trait, found type + --> $DIR/issue-106694.rs:3:16 + | +LL | fn foo(_: impl &Trait) {} + | ^^^^^^ + | +help: consider removing the indirection + | +LL - fn foo(_: impl &Trait) {} +LL + fn foo(_: impl Trait) {} + | + +error: expected a trait, found type + --> $DIR/issue-106694.rs:6:11 + | +LL | fn bar(_: T) {} + | ^^^^^^ + | +help: consider removing the indirection + | +LL - fn bar(_: T) {} +LL + fn bar(_: T) {} + | + +error: expected a trait, found type + --> $DIR/issue-106694.rs:9:35 + | +LL | fn partially_correct_impl(_: impl &*const &Trait + Copy) {} + | ^^^^^^^^^^^^^^ + | +help: consider removing the indirection + | +LL - fn partially_correct_impl(_: impl &*const &Trait + Copy) {} +LL + fn partially_correct_impl(_: impl Trait + Copy) {} + | + +error: expected a trait, found type + --> $DIR/issue-106694.rs:12:20 + | +LL | fn foo_bad(_: impl &BadTrait) {} + | ^^^^^^^^^ + | +help: consider removing the indirection + | +LL - fn foo_bad(_: impl &BadTrait) {} +LL + fn foo_bad(_: impl BadTrait) {} + | + +error: expected a trait, found type + --> $DIR/issue-106694.rs:16:15 + | +LL | fn bar_bad(_: T) {} + | ^^^^^^^^^ + | +help: consider removing the indirection + | +LL - fn bar_bad(_: T) {} +LL + fn bar_bad(_: T) {} + | + +error: expected a trait, found type + --> $DIR/issue-106694.rs:20:39 + | +LL | fn partially_correct_impl_bad(_: impl &*const &BadTrait + Copy) {} + | ^^^^^^^^^^^^^^^^^ + | +help: consider removing the indirection + | +LL - fn partially_correct_impl_bad(_: impl &*const &BadTrait + Copy) {} +LL + fn partially_correct_impl_bad(_: impl BadTrait + Copy) {} + | + +error[E0405]: cannot find trait `BadTrait` in this scope + --> $DIR/issue-106694.rs:12:21 + | +LL | fn foo_bad(_: impl &BadTrait) {} + | ^^^^^^^^ not found in this scope + +error[E0405]: cannot find trait `BadTrait` in this scope + --> $DIR/issue-106694.rs:16:16 + | +LL | fn bar_bad(_: T) {} + | ^^^^^^^^ not found in this scope + +error[E0405]: cannot find trait `BadTrait` in this scope + --> $DIR/issue-106694.rs:20:48 + | +LL | fn partially_correct_impl_bad(_: impl &*const &BadTrait + Copy) {} + | ^^^^^^^^ not found in this scope + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0405`. From fcd5ed21b77dd1d72696e43dd70e60b1ad458f55 Mon Sep 17 00:00:00 2001 From: Ezra Shaw Date: Mon, 16 Jan 2023 16:18:56 +1300 Subject: [PATCH 2/2] fix dropping diagnostic without emit --- compiler/rustc_parse/src/parser/ty.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index aedebd0fb7062..1766b0293de52 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -941,7 +941,8 @@ impl<'a> Parser<'a> { && let Some(path) = self.recover_path_from_fn() { path - } else if !self.token.is_path_start() && self.token.can_begin_type() && let Ok(ty) = self.parse_ty_no_plus() { + } else if !self.token.is_path_start() && self.token.can_begin_type() { + let ty = self.parse_ty_no_plus()?; // Instead of finding a path (a trait), we found a type. let mut err = self.struct_span_err(ty.span, "expected a trait, found type");