about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2020-01-09 13:46:37 -0800
committerEsteban Küber <esteban@kuber.com.ar>2020-01-09 14:20:53 -0800
commitc751961d290e4da3caae3d5f1d01435accea6c6c (patch)
tree70b707e8c48c174568b25ceb59e6496e512552ff
parented6468da160bd67a2ce0573427f09a98daff8c07 (diff)
downloadrust-c751961d290e4da3caae3d5f1d01435accea6c6c.tar.gz
rust-c751961d290e4da3caae3d5f1d01435accea6c6c.zip
Extend support of `_` in type parameters
 - Account for `impl Trait<_>`.
 - Provide a reasonable `Span` for empty `Generics` in `impl`s.
 - Account for `fn foo<_>(_: _) {}` to suggest `fn foo<T>(_: T) {}`.
 - Fix #67995.
-rw-r--r--src/librustc_parse/parser/generics.rs2
-rw-r--r--src/librustc_parse/parser/item.rs6
-rw-r--r--src/librustc_typeck/astconv.rs2
-rw-r--r--src/librustc_typeck/collect.rs36
-rw-r--r--src/test/ui/typeck/typeck_type_placeholder_item.rs13
-rw-r--r--src/test/ui/typeck/typeck_type_placeholder_item.stderr44
6 files changed, 91 insertions, 12 deletions
diff --git a/src/librustc_parse/parser/generics.rs b/src/librustc_parse/parser/generics.rs
index 1b816e2b90d..075583711f5 100644
--- a/src/librustc_parse/parser/generics.rs
+++ b/src/librustc_parse/parser/generics.rs
@@ -156,7 +156,7 @@ impl<'a> Parser<'a> {
             self.expect_gt()?;
             (params, span_lo.to(self.prev_span))
         } else {
-            (vec![], self.prev_span.between(self.token.span))
+            (vec![], self.prev_span.shrink_to_hi())
         };
         Ok(ast::Generics {
             params,
diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs
index 918e826fc26..12dcf0391da 100644
--- a/src/librustc_parse/parser/item.rs
+++ b/src/librustc_parse/parser/item.rs
@@ -555,7 +555,11 @@ impl<'a> Parser<'a> {
         let mut generics = if self.choose_generics_over_qpath() {
             self.parse_generics()?
         } else {
-            Generics::default()
+            let mut generics = Generics::default();
+            // impl A for B {}
+            //    /\ this is where `generics.span` should point when there are no type params.
+            generics.span = self.prev_span.shrink_to_hi();
+            generics
         };
 
         // Disambiguate `impl !Trait for Type { ... }` and `impl ! { ... }` for the never type.
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 4bacf934937..2b27138a4d8 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -2803,7 +2803,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             // allowed. `allow_ty_infer` gates this behavior.
             crate::collect::placeholder_type_error(
                 tcx,
-                ident_span.unwrap_or(DUMMY_SP),
+                ident_span.map(|sp| sp.shrink_to_hi()).unwrap_or(DUMMY_SP),
                 generic_params,
                 visitor.0,
                 ident_span.is_some(),
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 35c380612d2..43a2bcd564f 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -124,7 +124,7 @@ struct CollectItemTypesVisitor<'tcx> {
 /// all already existing generic type parameters to avoid suggesting a name that is already in use.
 crate fn placeholder_type_error(
     tcx: TyCtxt<'tcx>,
-    ident_span: Span,
+    span: Span,
     generics: &[hir::GenericParam<'_>],
     placeholder_types: Vec<Span>,
     suggest: bool,
@@ -150,7 +150,14 @@ crate fn placeholder_type_error(
     let mut sugg: Vec<_> =
         placeholder_types.iter().map(|sp| (*sp, type_name.to_string())).collect();
     if generics.is_empty() {
-        sugg.push((ident_span.shrink_to_hi(), format!("<{}>", type_name)));
+        sugg.push((span, format!("<{}>", type_name)));
+    } else if let Some(arg) = generics.iter().find(|arg| match arg.name {
+        hir::ParamName::Plain(Ident { name: kw::Underscore, .. }) => true,
+        _ => false,
+    }) {
+        // Account for `_` already present in cases like `struct S<_>(_);` and suggest
+        // `struct S<T>(T);` instead of `struct S<_, T>(T);`.
+        sugg.push((arg.span, format!("{}", type_name)));
     } else {
         sugg.push((
             generics.iter().last().unwrap().span.shrink_to_hi(),
@@ -172,8 +179,12 @@ fn reject_placeholder_type_signatures_in_item(tcx: TyCtxt<'tcx>, item: &'tcx hir
     let (generics, suggest) = match &item.kind {
         hir::ItemKind::Union(_, generics)
         | hir::ItemKind::Enum(_, generics)
-        | hir::ItemKind::Struct(_, generics) => (&generics.params[..], true),
-        hir::ItemKind::TyAlias(_, generics) => (&generics.params[..], false),
+        | hir::ItemKind::TraitAlias(generics, _)
+        | hir::ItemKind::Trait(_, _, generics, ..)
+        | hir::ItemKind::Impl(_, _, _, generics, ..)
+        | hir::ItemKind::Struct(_, generics) => (generics, true),
+        hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. })
+        | hir::ItemKind::TyAlias(_, generics) => (generics, false),
         // `static`, `fn` and `const` are handled elsewhere to suggest appropriate type.
         _ => return,
     };
@@ -181,7 +192,7 @@ fn reject_placeholder_type_signatures_in_item(tcx: TyCtxt<'tcx>, item: &'tcx hir
     let mut visitor = PlaceholderHirTyCollector::default();
     visitor.visit_item(item);
 
-    placeholder_type_error(tcx, item.ident.span, generics, visitor.0, suggest);
+    placeholder_type_error(tcx, generics.span, &generics.params[..], visitor.0, suggest);
 }
 
 impl Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
@@ -1789,10 +1800,19 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
 /// Whether `ty` is a type with `_` placeholders that can be infered. Used in diagnostics only to
 /// use inference to provide suggestions for the appropriate type if possible.
 fn is_suggestable_infer_ty(ty: &hir::Ty<'_>) -> bool {
+    use hir::TyKind::*;
     match &ty.kind {
-        hir::TyKind::Infer => true,
-        hir::TyKind::Slice(ty) | hir::TyKind::Array(ty, _) => is_suggestable_infer_ty(ty),
-        hir::TyKind::Tup(tys) => tys.iter().any(|ty| is_suggestable_infer_ty(ty)),
+        Infer => true,
+        Slice(ty) | Array(ty, _) => is_suggestable_infer_ty(ty),
+        Tup(tys) => tys.iter().any(is_suggestable_infer_ty),
+        Ptr(mut_ty) | Rptr(_, mut_ty) => is_suggestable_infer_ty(mut_ty.ty),
+        Def(_, generic_args) => generic_args
+            .iter()
+            .filter_map(|arg| match arg {
+                hir::GenericArg::Type(ty) => Some(ty),
+                _ => None,
+            })
+            .any(is_suggestable_infer_ty),
         _ => false,
     }
 }
diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.rs b/src/test/ui/typeck/typeck_type_placeholder_item.rs
index 5b0ca2f347e..a53042d6e95 100644
--- a/src/test/ui/typeck/typeck_type_placeholder_item.rs
+++ b/src/test/ui/typeck/typeck_type_placeholder_item.rs
@@ -131,3 +131,16 @@ trait T {
     fn assoc_fn_test3() -> _;
     //~^ ERROR the type placeholder `_` is not allowed within types on item signatures
 }
+
+struct BadStruct<_>(_);
+//~^ ERROR expected identifier, found reserved identifier `_`
+//~| ERROR the type placeholder `_` is not allowed within types on item signatures
+trait BadTrait<_> {}
+//~^ ERROR expected identifier, found reserved identifier `_`
+impl BadTrait<_> for BadStruct<_> {}
+//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
+
+fn impl_trait() -> impl BadTrait<_> {
+//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
+    unimplemented!()
+}
diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.stderr b/src/test/ui/typeck/typeck_type_placeholder_item.stderr
index 9fe7af4c822..e788bf37790 100644
--- a/src/test/ui/typeck/typeck_type_placeholder_item.stderr
+++ b/src/test/ui/typeck/typeck_type_placeholder_item.stderr
@@ -1,3 +1,15 @@
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/typeck_type_placeholder_item.rs:135:18
+   |
+LL | struct BadStruct<_>(_);
+   |                  ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/typeck_type_placeholder_item.rs:138:16
+   |
+LL | trait BadTrait<_> {}
+   |                ^ expected identifier, found reserved identifier
+
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
   --> $DIR/typeck_type_placeholder_item.rs:4:14
    |
@@ -256,6 +268,36 @@ LL |     fn fn_test13(x: _) -> (i32, _) { (x, x) }
    |                           help: replace with the correct return type: `(i32, i32)`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+  --> $DIR/typeck_type_placeholder_item.rs:135:21
+   |
+LL | struct BadStruct<_>(_);
+   |                     ^ not allowed in type signatures
+   |
+help: use type parameters instead
+   |
+LL | struct BadStruct<T>(T);
+   |                  ^  ^
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+  --> $DIR/typeck_type_placeholder_item.rs:140:15
+   |
+LL | impl BadTrait<_> for BadStruct<_> {}
+   |               ^                ^ not allowed in type signatures
+   |               |
+   |               not allowed in type signatures
+   |
+help: use type parameters instead
+   |
+LL | impl<T> BadTrait<T> for BadStruct<T> {}
+   |     ^^^          ^                ^
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+  --> $DIR/typeck_type_placeholder_item.rs:143:34
+   |
+LL | fn impl_trait() -> impl BadTrait<_> {
+   |                                  ^ not allowed in type signatures
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
   --> $DIR/typeck_type_placeholder_item.rs:121:31
    |
 LL |     fn method_test1(&self, x: _);
@@ -405,7 +447,7 @@ help: use type parameters instead
 LL |         fn clone_from<T>(&mut self, other: T) { *self = FnTest9; }
    |                      ^^^                   ^
 
-error: aborting due to 40 previous errors
+error: aborting due to 45 previous errors
 
 Some errors have detailed explanations: E0121, E0282.
 For more information about an error, try `rustc --explain E0121`.