Skip to content

Commit 9b2d80a

Browse files
committed
Provide more context when denying invalid type params
1 parent f023b92 commit 9b2d80a

File tree

12 files changed

+564
-110
lines changed

12 files changed

+564
-110
lines changed

compiler/rustc_typeck/src/astconv/mod.rs

Lines changed: 144 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use rustc_middle::ty::{self, Const, DefIdTree, EarlyBinder, Ty, TyCtxt, TypeFold
3030
use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS};
3131
use rustc_span::edition::Edition;
3232
use rustc_span::lev_distance::find_best_match_for_name;
33-
use rustc_span::symbol::{Ident, Symbol};
33+
use rustc_span::symbol::{kw, Ident, Symbol};
3434
use rustc_span::{Span, DUMMY_SP};
3535
use rustc_target::spec::abi;
3636
use rustc_trait_selection::traits;
@@ -653,7 +653,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
653653
span, item_def_id, item_segment
654654
);
655655
if tcx.generics_of(item_def_id).params.is_empty() {
656-
self.prohibit_generics(slice::from_ref(item_segment).iter());
656+
self.prohibit_generics(slice::from_ref(item_segment).iter(), |_| {});
657657

658658
parent_substs
659659
} else {
@@ -681,7 +681,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
681681
trait_ref: &hir::TraitRef<'_>,
682682
self_ty: Ty<'tcx>,
683683
) -> ty::TraitRef<'tcx> {
684-
self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter());
684+
self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {});
685685

686686
self.ast_path_to_mono_trait_ref(
687687
trait_ref.path.span,
@@ -784,7 +784,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
784784
let args = trait_segment.args();
785785
let infer_args = trait_segment.infer_args;
786786

787-
self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter());
787+
self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {});
788788
self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false);
789789

790790
self.instantiate_poly_trait_ref_inner(
@@ -1776,12 +1776,17 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
17761776
hir_ref_id: hir::HirId,
17771777
span: Span,
17781778
qself_ty: Ty<'tcx>,
1779-
qself_res: Res,
1779+
qself: &hir::Ty<'_>,
17801780
assoc_segment: &hir::PathSegment<'_>,
17811781
permit_variants: bool,
17821782
) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> {
17831783
let tcx = self.tcx();
17841784
let assoc_ident = assoc_segment.ident;
1785+
let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, ref path)) = qself.kind {
1786+
path.res
1787+
} else {
1788+
Res::Err
1789+
};
17851790

17861791
debug!("associated_path_to_ty: {:?}::{}", qself_ty, assoc_ident);
17871792

@@ -1796,7 +1801,55 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
17961801
if let Some(variant_def) = variant_def {
17971802
if permit_variants {
17981803
tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None);
1799-
self.prohibit_generics(slice::from_ref(assoc_segment).iter());
1804+
self.prohibit_generics(slice::from_ref(assoc_segment).iter(), |err| {
1805+
err.note("enum variants can't have type parameters");
1806+
let type_name = tcx.opt_item_name(adt_def.did());
1807+
let the_enum = type_name.map(|n| format!("enum `{n}`")).unwrap_or_else(|| "the enum".to_string());
1808+
let msg = format!("you might have meant to specity type parameters on {the_enum}");
1809+
let Some(args) = assoc_segment.args else { return; };
1810+
let args_span = assoc_segment.ident.span.shrink_to_hi().to(args.span_ext);
1811+
let Ok(snippet) = tcx.sess.source_map().span_to_snippet(args_span) else {
1812+
err.note(&msg);
1813+
return;
1814+
};
1815+
let (qself_sugg_span, is_self) = if let hir::TyKind::Path(hir::QPath::Resolved(_, ref path)) = qself.kind {
1816+
// If the path segment already has type params, we want to overwrite
1817+
// them.
1818+
match &path.segments[..] {
1819+
[.., segment, _] => (
1820+
segment.ident.span.shrink_to_hi().to(segment.args.map_or(
1821+
segment.ident.span.shrink_to_hi(),
1822+
|a| a.span_ext)),
1823+
false,
1824+
),
1825+
[segment] => (
1826+
segment.ident.span.shrink_to_hi().to(segment.args.map_or(
1827+
segment.ident.span.shrink_to_hi(),
1828+
|a| a.span_ext)),
1829+
kw::SelfUpper == segment.ident.name,
1830+
),
1831+
_ => unreachable!(),
1832+
}
1833+
} else {
1834+
err.note(&msg);
1835+
return;
1836+
};
1837+
let Some(type_name) = type_name else {
1838+
err.note(&msg);
1839+
return;
1840+
};
1841+
let suggestion = vec![
1842+
if is_self {
1843+
// Account for people writing `Self::Variant::<Args>`, where
1844+
// `Self` is the enum.
1845+
(qself.span, format!("{type_name}{snippet}"))
1846+
} else {
1847+
(qself_sugg_span, snippet)
1848+
},
1849+
(args_span, String::new()),
1850+
];
1851+
err.multipart_suggestion_verbose(&msg, suggestion, Applicability::MaybeIncorrect);
1852+
});
18001853
return Ok((qself_ty, DefKind::Variant, variant_def.def_id));
18011854
} else {
18021855
variant_resolution = Some(variant_def.def_id);
@@ -2017,9 +2070,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
20172070
self.normalize_ty(span, tcx.mk_projection(item_def_id, item_substs))
20182071
}
20192072

2020-
pub fn prohibit_generics<'a, T: Iterator<Item = &'a hir::PathSegment<'a>> + Clone>(
2073+
pub fn prohibit_generics<'a>(
20212074
&self,
2022-
segments: T,
2075+
segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone,
2076+
extend: impl Fn(&mut DiagnosticBuilder<'tcx, ErrorGuaranteed>),
20232077
) -> bool {
20242078
let args = segments.clone().flat_map(|segment| segment.args().args);
20252079

@@ -2078,6 +2132,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
20782132
"{kind} arguments are not allowed for this type",
20792133
);
20802134
err.span_label(last_span, format!("{kind} argument{s} not allowed"));
2135+
extend(&mut err);
20812136
err.emit();
20822137
emitted = true;
20832138
}
@@ -2239,7 +2294,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
22392294
// Check for desugared `impl Trait`.
22402295
assert!(ty::is_impl_trait_defn(tcx, did).is_none());
22412296
let item_segment = path.segments.split_last().unwrap();
2242-
self.prohibit_generics(item_segment.1.iter());
2297+
self.prohibit_generics(item_segment.1.iter(), |err| {
2298+
err.note("`impl Trait` types can't have type parameters");
2299+
});
22432300
let substs = self.ast_path_substs_for_ty(span, did, item_segment.0);
22442301
self.normalize_ty(span, tcx.mk_opaque(did, substs))
22452302
}
@@ -2252,7 +2309,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
22522309
did,
22532310
) => {
22542311
assert_eq!(opt_self_ty, None);
2255-
self.prohibit_generics(path.segments.split_last().unwrap().1.iter());
2312+
self.prohibit_generics(path.segments.split_last().unwrap().1.iter(), |_| {});
22562313
self.ast_path_to_ty(span, did, path.segments.last().unwrap())
22572314
}
22582315
Res::Def(kind @ DefKind::Variant, def_id) if permit_variants => {
@@ -2264,18 +2321,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
22642321
self.def_ids_for_value_path_segments(path.segments, None, kind, def_id);
22652322
let generic_segs: FxHashSet<_> =
22662323
path_segs.iter().map(|PathSeg(_, index)| index).collect();
2267-
self.prohibit_generics(path.segments.iter().enumerate().filter_map(
2268-
|(index, seg)| {
2324+
self.prohibit_generics(
2325+
path.segments.iter().enumerate().filter_map(|(index, seg)| {
22692326
if !generic_segs.contains(&index) { Some(seg) } else { None }
2327+
}),
2328+
|err| {
2329+
err.note("enum variants can't have type parameters");
22702330
},
2271-
));
2331+
);
22722332

22732333
let PathSeg(def_id, index) = path_segs.last().unwrap();
22742334
self.ast_path_to_ty(span, *def_id, &path.segments[*index])
22752335
}
22762336
Res::Def(DefKind::TyParam, def_id) => {
22772337
assert_eq!(opt_self_ty, None);
2278-
self.prohibit_generics(path.segments.iter());
2338+
self.prohibit_generics(path.segments.iter(), |err| {
2339+
if let Some(span) = tcx.def_ident_span(def_id) {
2340+
let name = tcx.item_name(def_id);
2341+
err.span_note(span, &format!("type parameter `{name}` defined here"));
2342+
}
2343+
});
22792344

22802345
let def_id = def_id.expect_local();
22812346
let item_def_id = tcx.hir().ty_param_owner(def_id);
@@ -2286,15 +2351,63 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
22862351
Res::SelfTy { trait_: Some(_), alias_to: None } => {
22872352
// `Self` in trait or type alias.
22882353
assert_eq!(opt_self_ty, None);
2289-
self.prohibit_generics(path.segments.iter());
2354+
self.prohibit_generics(path.segments.iter(), |err| {
2355+
if let [hir::PathSegment { args: Some(args), ident, .. }] = &path.segments[..] {
2356+
err.span_suggestion_verbose(
2357+
ident.span.shrink_to_hi().to(args.span_ext),
2358+
"the `Self` type doesn't accept type parameters",
2359+
String::new(),
2360+
Applicability::MaybeIncorrect,
2361+
);
2362+
}
2363+
});
22902364
tcx.types.self_param
22912365
}
22922366
Res::SelfTy { trait_: _, alias_to: Some((def_id, forbid_generic)) } => {
22932367
// `Self` in impl (we know the concrete type).
22942368
assert_eq!(opt_self_ty, None);
2295-
self.prohibit_generics(path.segments.iter());
22962369
// Try to evaluate any array length constants.
22972370
let ty = tcx.at(span).type_of(def_id);
2371+
let span_of_impl = tcx.span_of_impl(def_id);
2372+
// TODO: confirm that `def_id`'s type accepts type params at all before suggesting
2373+
// using that instead.
2374+
self.prohibit_generics(path.segments.iter(), |err| {
2375+
let def_id = match *ty.kind() {
2376+
ty::Adt(self_def, _) => self_def.did(),
2377+
_ => return,
2378+
};
2379+
2380+
let type_name = tcx.item_name(def_id);
2381+
let span_of_ty = tcx.def_ident_span(def_id);
2382+
2383+
let msg = format!("the `Self` type is `{ty}`");
2384+
if let (Ok(i_sp), Some(t_sp)) = (span_of_impl, span_of_ty) {
2385+
let i_sp = tcx.sess.source_map().guess_head_span(i_sp);
2386+
let mut span: MultiSpan = vec![t_sp].into();
2387+
span.push_span_label(
2388+
i_sp,
2389+
&format!("`Self` is `{type_name}` in this `impl`"),
2390+
);
2391+
span.push_span_label(t_sp, "`Self` corresponds to this type");
2392+
err.span_note(span, &msg);
2393+
} else {
2394+
err.note(&msg);
2395+
}
2396+
for segment in path.segments {
2397+
if let Some(_args) = segment.args && segment.ident.name == kw::SelfUpper {
2398+
err.span_suggestion_verbose(
2399+
segment.ident.span,
2400+
format!(
2401+
"the `Self` type doesn't accept type parameters, use the \
2402+
concrete type's name `{type_name}` instead if you want to \
2403+
specify its type parameters"
2404+
),
2405+
type_name.to_string(),
2406+
Applicability::MaybeIncorrect,
2407+
);
2408+
}
2409+
}
2410+
});
22982411
// HACK(min_const_generics): Forbid generic `Self` types
22992412
// here as we can't easily do that during nameres.
23002413
//
@@ -2334,7 +2447,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
23342447
}
23352448
Res::Def(DefKind::AssocTy, def_id) => {
23362449
debug_assert!(path.segments.len() >= 2);
2337-
self.prohibit_generics(path.segments[..path.segments.len() - 2].iter());
2450+
self.prohibit_generics(path.segments[..path.segments.len() - 2].iter(), |_| {});
23382451
self.qpath_to_ty(
23392452
span,
23402453
opt_self_ty,
@@ -2345,7 +2458,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
23452458
}
23462459
Res::PrimTy(prim_ty) => {
23472460
assert_eq!(opt_self_ty, None);
2348-
self.prohibit_generics(path.segments.iter());
2461+
self.prohibit_generics(path.segments.iter(), |err| {
2462+
let name = prim_ty.name_str();
2463+
for segment in path.segments {
2464+
if let Some(args) = segment.args {
2465+
err.span_suggestion_verbose(
2466+
segment.ident.span.shrink_to_hi().to(args.span_ext),
2467+
&format!("primitive type `{name}` doesn't have type parameters"),
2468+
String::new(),
2469+
Applicability::MaybeIncorrect,
2470+
);
2471+
}
2472+
}
2473+
});
23492474
match prim_ty {
23502475
hir::PrimTy::Bool => tcx.types.bool,
23512476
hir::PrimTy::Char => tcx.types.char,
@@ -2436,13 +2561,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
24362561
hir::TyKind::Path(hir::QPath::TypeRelative(ref qself, ref segment)) => {
24372562
debug!(?qself, ?segment);
24382563
let ty = self.ast_ty_to_ty_inner(qself, false, true);
2439-
2440-
let res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = qself.kind {
2441-
path.res
2442-
} else {
2443-
Res::Err
2444-
};
2445-
self.associated_path_to_ty(ast_ty.hir_id, ast_ty.span, ty, res, segment, false)
2564+
self.associated_path_to_ty(ast_ty.hir_id, ast_ty.span, ty, qself, segment, false)
24462565
.map(|(ty, _, _)| ty)
24472566
.unwrap_or_else(|_| tcx.ty_error())
24482567
}

compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,6 +1228,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12281228
None
12291229
}
12301230
}),
1231+
|_| {},
12311232
);
12321233

12331234
if let Res::Local(hid) = res {

compiler/rustc_typeck/src/check/fn_ctxt/checks.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,13 +1564,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15641564
QPath::TypeRelative(ref qself, ref segment) => {
15651565
let ty = self.to_ty(qself);
15661566

1567-
let res = if let hir::TyKind::Path(QPath::Resolved(_, ref path)) = qself.kind {
1568-
path.res
1569-
} else {
1570-
Res::Err
1571-
};
15721567
let result = <dyn AstConv<'_>>::associated_path_to_ty(
1573-
self, hir_id, path_span, ty, res, segment, true,
1568+
self, hir_id, path_span, ty, qself, segment, true,
15741569
);
15751570
let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error());
15761571
let result = result.map(|(_, kind, def_id)| (kind, def_id));

src/test/ui/error-codes/E0109.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ error[E0109]: type arguments are not allowed for this type
33
|
44
LL | type X = u32<i32>;
55
| ^^^ type argument not allowed
6+
|
7+
help: primitive type `u32` doesn't have type parameters
8+
|
9+
LL - type X = u32<i32>;
10+
LL + type X = u32;
11+
|
612

713
error: aborting due to previous error
814

src/test/ui/error-codes/E0110.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ error[E0109]: lifetime arguments are not allowed for this type
33
|
44
LL | type X = u32<'static>;
55
| ^^^^^^^ lifetime argument not allowed
6+
|
7+
help: primitive type `u32` doesn't have type parameters
8+
|
9+
LL - type X = u32<'static>;
10+
LL + type X = u32;
11+
|
612

713
error: aborting due to previous error
814

src/test/ui/structs/struct-path-self.stderr

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ error[E0109]: type arguments are not allowed for this type
99
|
1010
LL | let z = Self::<u8> {};
1111
| ^^ type argument not allowed
12+
|
13+
help: the `Self` type doesn't accept type parameters
14+
|
15+
LL - let z = Self::<u8> {};
16+
LL + let z = Self {};
17+
|
1218

1319
error[E0071]: expected struct, variant or union type, found type parameter `Self`
1420
--> $DIR/struct-path-self.rs:7:17
@@ -27,12 +33,38 @@ error[E0109]: type arguments are not allowed for this type
2733
|
2834
LL | let z = Self::<u8> {};
2935
| ^^ type argument not allowed
36+
|
37+
note: the `Self` type is `S`
38+
--> $DIR/struct-path-self.rs:1:8
39+
|
40+
LL | struct S;
41+
| ^ `Self` corresponds to this type
42+
...
43+
LL | impl Tr for S {
44+
| ------------- `Self` is `S` in this `impl`
45+
help: the `Self` type doesn't accept type parameters, use the concrete type's name `S` instead if you want to specify its type parameters
46+
|
47+
LL | let z = S::<u8> {};
48+
| ~
3049

3150
error[E0109]: type arguments are not allowed for this type
3251
--> $DIR/struct-path-self.rs:30:24
3352
|
3453
LL | let z = Self::<u8> {};
3554
| ^^ type argument not allowed
55+
|
56+
note: the `Self` type is `S`
57+
--> $DIR/struct-path-self.rs:1:8
58+
|
59+
LL | struct S;
60+
| ^ `Self` corresponds to this type
61+
...
62+
LL | impl S {
63+
| ------ `Self` is `S` in this `impl`
64+
help: the `Self` type doesn't accept type parameters, use the concrete type's name `S` instead if you want to specify its type parameters
65+
|
66+
LL | let z = S::<u8> {};
67+
| ~
3668

3769
error: aborting due to 6 previous errors
3870

0 commit comments

Comments
 (0)