diff --git a/src/librustc_parse/parser/generics.rs b/src/librustc_parse/parser/generics.rs index 1b816e2b90da9..075583711f5d3 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 918e826fc26bf..12dcf0391da1e 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 4bacf9349379e..2b27138a4d854 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 35c380612d2fb..43a2bcd564f4c 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, 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);` 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 5b0ca2f347ea8..a53042d6e9538 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 9fe7af4c822c1..e788bf37790cf 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 | @@ -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); + | ^ ^ + +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 BadTrait for BadStruct {} + | ^^^ ^ ^ + +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 | @@ -405,7 +447,7 @@ help: use type parameters instead LL | fn clone_from(&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`.