about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2023-01-17 05:25:21 +0100
committerGitHub <noreply@github.com>2023-01-17 05:25:21 +0100
commit9cda9e0ab62467199e09644258618343bd40d52e (patch)
tree465aa506c27ca589296a3c6ae1f60bdad1d971ff
parentf74044259a4f9392203f3b28d9e9172be22f2fef (diff)
parentfcd5ed21b77dd1d72696e43dd70e60b1ad458f55 (diff)
downloadrust-9cda9e0ab62467199e09644258618343bd40d52e.tar.gz
rust-9cda9e0ab62467199e09644258618343bd40d52e.zip
Rollup merge of #106712 - Ezrashaw:impl-ref-trait, r=estebank
make error emitted on `impl &Trait` nicer

Fixes #106694

Turned out to be simpler than I thought, also added UI test.

Before: ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=9bda53271ef3a8886793cf427b8cea91))
```text
error: expected one of `:`, ``@`,` or `|`, found `)`
 --> src/main.rs:2:22
  |
2 | fn foo(_: impl &Trait) {}
  |                      ^ expected one of `:`, ``@`,` or `|`
  |
  = note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
help: if this is a parameter name, give it a type
  |
2 | fn foo(_: impl Trait: &TypeName) {}
  |                ~~~~~~~~~~~~~~~~
help: if this is a type, explicitly ignore the parameter name
  |
2 | fn foo(_: impl _: &Trait) {}
  |                ++

error: expected one of `!`, `(`, `)`, `,`, `?`, `for`, `~`, lifetime, or path, found `&`
 --> src/main.rs:2:16
  |
2 | fn foo(_: impl &Trait) {}
  |               -^ expected one of 9 possible tokens
  |               |
  |               help: missing `,`

error: expected one of `!`, `(`, `,`, `=`, `>`, `?`, `for`, `~`, lifetime, or path, found `&`
 --> src/main.rs:3:11
  |
3 | fn bar<T: &Trait>(_: T) {}
  |           ^ expected one of 10 possible tokens
```

After:
```text
error: expected a trait, found type
 --> <anon>:2:16
  |
2 | fn foo(_: impl &Trait) {}
  |                -^^^^^
  |                |
  |                help: consider removing the indirection

error: expected a trait, found type
 --> <anon>:3:11
  |
3 | fn bar<T: &Trait>(_: T) {}
  |           -^^^^^
  |           |
  |           help: consider removing the indirection
```
-rw-r--r--compiler/rustc_ast/src/ast.rs3
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs40
-rw-r--r--tests/ui/generics/issue-106694.rs24
-rw-r--r--tests/ui/generics/issue-106694.stderr93
4 files changed, 155 insertions, 5 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 7de594719dd..9317579f70d 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 a6f702e5428..1766b0293de 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) {
@@ -939,6 +941,36 @@ 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 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 00000000000..c4b02ee81ec
--- /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: &Trait>(_: 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: &BadTrait>(_: 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 00000000000..235b8982a99
--- /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: &Trait>(_: T) {}
+   |           ^^^^^^
+   |
+help: consider removing the indirection
+   |
+LL - fn bar<T: &Trait>(_: T) {}
+LL + fn bar<T: Trait>(_: 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: &BadTrait>(_: T) {}
+   |               ^^^^^^^^^
+   |
+help: consider removing the indirection
+   |
+LL - fn bar_bad<T: &BadTrait>(_: T) {}
+LL + fn bar_bad<T: BadTrait>(_: 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: &BadTrait>(_: 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`.