Skip to content

Commit

Permalink
Extend support of _ in type parameters
Browse files Browse the repository at this point in the history
 - 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.
  • Loading branch information
estebank committed Jan 9, 2020
1 parent ed6468d commit c751961
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 12 deletions.
2 changes: 1 addition & 1 deletion src/librustc_parse/parser/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 5 additions & 1 deletion src/librustc_parse/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
36 changes: 28 additions & 8 deletions src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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(),
Expand All @@ -172,16 +179,20 @@ 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,
};

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> {
Expand Down Expand Up @@ -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,
}
}
Expand Down
13 changes: 13 additions & 0 deletions src/test/ui/typeck/typeck_type_placeholder_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!()
}
44 changes: 43 additions & 1 deletion src/test/ui/typeck/typeck_type_placeholder_item.stderr
Original file line number Diff line number Diff line change
@@ -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
|
Expand Down Expand Up @@ -255,6 +267,36 @@ LL | fn fn_test13(x: _) -> (i32, _) { (x, x) }
| | not allowed in type signatures
| 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
|
Expand Down Expand Up @@ -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`.

0 comments on commit c751961

Please sign in to comment.