]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_middle/src/ty/diagnostics.rs
New upstream version 1.66.0+dfsg1
[rustc.git] / compiler / rustc_middle / src / ty / diagnostics.rs
CommitLineData
5099ac24 1//! Diagnostics related methods for `Ty`.
60c5eb7d 2
923072b8
FG
3use std::ops::ControlFlow;
4
a2a8927a 5use crate::ty::{
064997fb
FG
6 visit::TypeVisitable, Const, ConstKind, DefIdTree, ExistentialPredicate, InferConst, InferTy,
7 PolyTraitPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
a2a8927a
XL
8};
9
5e7ed085 10use rustc_data_structures::fx::FxHashMap;
04454e1e 11use rustc_errors::{Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg};
f9f354fc
XL
12use rustc_hir as hir;
13use rustc_hir::def_id::DefId;
04454e1e 14use rustc_hir::WherePredicate;
a2a8927a 15use rustc_span::Span;
923072b8 16use rustc_type_ir::sty::TyKind::*;
60c5eb7d 17
04454e1e
FG
18impl<'tcx> IntoDiagnosticArg for Ty<'tcx> {
19 fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
20 format!("{}", self).into_diagnostic_arg()
21 }
22}
23
5099ac24
FG
24impl<'tcx> Ty<'tcx> {
25 /// Similar to `Ty::is_primitive`, but also considers inferred numeric values to be primitive.
26 pub fn is_primitive_ty(self) -> bool {
29967ef6
XL
27 matches!(
28 self.kind(),
5869c6ff
XL
29 Bool | Char
30 | Str
31 | Int(_)
32 | Uint(_)
33 | Float(_)
34 | Infer(
35 InferTy::IntVar(_)
36 | InferTy::FloatVar(_)
37 | InferTy::FreshIntTy(_)
38 | InferTy::FreshFloatTy(_)
39 )
29967ef6 40 )
60c5eb7d
XL
41 }
42
43 /// Whether the type is succinctly representable as a type instead of just referred to with a
44 /// description in error messages. This is used in the main error message.
5099ac24 45 pub fn is_simple_ty(self) -> bool {
1b1a35ee 46 match self.kind() {
dfeec247
XL
47 Bool
48 | Char
49 | Str
50 | Int(_)
51 | Uint(_)
52 | Float(_)
ba9703b0
XL
53 | Infer(
54 InferTy::IntVar(_)
55 | InferTy::FloatVar(_)
56 | InferTy::FreshIntTy(_)
57 | InferTy::FreshFloatTy(_),
58 ) => true,
60c5eb7d
XL
59 Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
60 Tuple(tys) if tys.is_empty() => true,
61 _ => false,
62 }
63 }
64
65 /// Whether the type is succinctly representable as a type instead of just referred to with a
66 /// description in error messages. This is used in the primary span label. Beyond what
67 /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
68 /// ADTs with no type arguments.
5099ac24 69 pub fn is_simple_text(self) -> bool {
1b1a35ee 70 match self.kind() {
17df50a5 71 Adt(_, substs) => substs.non_erasable_generics().next().is_none(),
60c5eb7d
XL
72 Ref(_, ty, _) => ty.is_simple_text(),
73 _ => self.is_simple_ty(),
74 }
75 }
923072b8 76}
60c5eb7d 77
923072b8
FG
78pub trait IsSuggestable<'tcx> {
79 /// Whether this makes sense to suggest in a diagnostic.
80 ///
81 /// We filter out certain types and constants since they don't provide
82 /// meaningful rendered suggestions when pretty-printed. We leave some
83 /// nonsense, such as region vars, since those render as `'_` and are
84 /// usually okay to reinterpret as elided lifetimes.
064997fb
FG
85 ///
86 /// Only if `infer_suggestable` is true, we consider type and const
87 /// inference variables to be suggestable.
88 fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool;
923072b8 89}
a2a8927a 90
923072b8
FG
91impl<'tcx, T> IsSuggestable<'tcx> for T
92where
064997fb 93 T: TypeVisitable<'tcx>,
923072b8 94{
064997fb
FG
95 fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool {
96 self.visit_with(&mut IsSuggestableVisitor { tcx, infer_suggestable }).is_continue()
60c5eb7d
XL
97 }
98}
f9f354fc 99
923072b8
FG
100pub fn suggest_arbitrary_trait_bound<'tcx>(
101 tcx: TyCtxt<'tcx>,
6a06907d 102 generics: &hir::Generics<'_>,
5e7ed085 103 err: &mut Diagnostic,
923072b8 104 trait_pred: PolyTraitPredicate<'tcx>,
f2b60f7d 105 associated_ty: Option<(&'static str, Ty<'tcx>)>,
6a06907d 106) -> bool {
064997fb 107 if !trait_pred.is_suggestable(tcx, false) {
923072b8
FG
108 return false;
109 }
110
111 let param_name = trait_pred.skip_binder().self_ty().to_string();
f2b60f7d
FG
112 let mut constraint = trait_pred.print_modifiers_and_trait_path().to_string();
113
114 if let Some((name, term)) = associated_ty {
115 // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.
116 // That should be extracted into a helper function.
117 if constraint.ends_with('>') {
118 constraint = format!("{}, {} = {}>", &constraint[..constraint.len() - 1], name, term);
119 } else {
120 constraint.push_str(&format!("<{} = {}>", name, term));
121 }
122 }
123
6a06907d 124 let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
923072b8
FG
125
126 // Skip, there is a param named Self
127 if param.is_some() && param_name == "Self" {
128 return false;
6a06907d 129 }
923072b8 130
5e7ed085 131 // Suggest a where clause bound for a non-type parameter.
6a06907d 132 err.span_suggestion_verbose(
04454e1e 133 generics.tail_span_for_predicate_suggestion(),
6a06907d 134 &format!(
923072b8 135 "consider {} `where` clause, but there might be an alternative better way to express \
6a06907d 136 this requirement",
923072b8 137 if generics.where_clause_span.is_empty() { "introducing a" } else { "extending the" },
6a06907d 138 ),
923072b8 139 format!("{} {}: {}", generics.add_where_or_trailing_comma(), param_name, constraint),
6a06907d
XL
140 Applicability::MaybeIncorrect,
141 );
142 true
143}
144
5e7ed085
FG
145#[derive(Debug)]
146enum SuggestChangingConstraintsMessage<'a> {
147 RestrictBoundFurther,
148 RestrictType { ty: &'a str },
149 RestrictTypeFurther { ty: &'a str },
150 RemovingQSized,
151}
152
94222f64 153fn suggest_removing_unsized_bound(
04454e1e 154 tcx: TyCtxt<'_>,
94222f64 155 generics: &hir::Generics<'_>,
5e7ed085 156 suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>,
94222f64
XL
157 param: &hir::GenericParam<'_>,
158 def_id: Option<DefId>,
159) {
160 // See if there's a `?Sized` bound that can be removed to suggest that.
c295e0f8
XL
161 // First look at the `where` clause because we can have `where T: ?Sized`,
162 // then look at params.
04454e1e
FG
163 let param_def_id = tcx.hir().local_def_id(param.hir_id);
164 for (where_pos, predicate) in generics.predicates.iter().enumerate() {
165 let WherePredicate::BoundPredicate(predicate) = predicate else {
166 continue;
167 };
168 if !predicate.is_param_bound(param_def_id.to_def_id()) {
169 continue;
170 };
5e7ed085 171
04454e1e
FG
172 for (pos, bound) in predicate.bounds.iter().enumerate() {
173 let hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe) = bound else {
174 continue;
175 };
176 if poly.trait_ref.trait_def_id() != def_id {
177 continue;
94222f64 178 }
04454e1e
FG
179 let sp = generics.span_for_bound_removal(where_pos, pos);
180 suggestions.push((
181 sp,
182 String::new(),
183 SuggestChangingConstraintsMessage::RemovingQSized,
184 ));
94222f64
XL
185 }
186 }
187}
188
f9f354fc
XL
189/// Suggest restricting a type param with a new bound.
190pub fn suggest_constraining_type_param(
191 tcx: TyCtxt<'_>,
192 generics: &hir::Generics<'_>,
5e7ed085 193 err: &mut Diagnostic,
f9f354fc
XL
194 param_name: &str,
195 constraint: &str,
196 def_id: Option<DefId>,
197) -> bool {
5e7ed085
FG
198 suggest_constraining_type_params(
199 tcx,
200 generics,
201 err,
202 [(param_name, constraint, def_id)].into_iter(),
203 )
204}
f9f354fc 205
5e7ed085
FG
206/// Suggest restricting a type param with a new bound.
207pub fn suggest_constraining_type_params<'a>(
208 tcx: TyCtxt<'_>,
209 generics: &hir::Generics<'_>,
210 err: &mut Diagnostic,
211 param_names_and_constraints: impl Iterator<Item = (&'a str, &'a str, Option<DefId>)>,
212) -> bool {
213 let mut grouped = FxHashMap::default();
214 param_names_and_constraints.for_each(|(param_name, constraint, def_id)| {
215 grouped.entry(param_name).or_insert(Vec::new()).push((constraint, def_id))
216 });
f9f354fc 217
5e7ed085
FG
218 let mut applicability = Applicability::MachineApplicable;
219 let mut suggestions = Vec::new();
f9f354fc 220
5e7ed085
FG
221 for (param_name, mut constraints) in grouped {
222 let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
223 let Some(param) = param else { return false };
f9f354fc 224
5e7ed085
FG
225 {
226 let mut sized_constraints =
227 constraints.drain_filter(|(_, def_id)| *def_id == tcx.lang_items().sized_trait());
228 if let Some((constraint, def_id)) = sized_constraints.next() {
229 applicability = Applicability::MaybeIncorrect;
f9f354fc 230
5e7ed085
FG
231 err.span_label(
232 param.span,
233 &format!("this type parameter needs to be `{}`", constraint),
234 );
04454e1e 235 suggest_removing_unsized_bound(tcx, generics, &mut suggestions, param, def_id);
5e7ed085
FG
236 }
237 }
238
239 if constraints.is_empty() {
240 continue;
241 }
242
923072b8
FG
243 let mut constraint = constraints.iter().map(|&(c, _)| c).collect::<Vec<_>>();
244 constraint.sort();
245 constraint.dedup();
246 let constraint = constraint.join(" + ");
04454e1e 247 let mut suggest_restrict = |span, bound_list_non_empty| {
5e7ed085
FG
248 suggestions.push((
249 span,
04454e1e
FG
250 if bound_list_non_empty {
251 format!(" + {}", constraint)
252 } else {
253 format!(" {}", constraint)
254 },
5e7ed085
FG
255 SuggestChangingConstraintsMessage::RestrictBoundFurther,
256 ))
257 };
258
04454e1e
FG
259 // When the type parameter has been provided bounds
260 //
261 // Message:
262 // fn foo<T>(t: T) where T: Foo { ... }
263 // ^^^^^^
264 // |
265 // help: consider further restricting this bound with `+ Bar`
266 //
267 // Suggestion:
268 // fn foo<T>(t: T) where T: Foo { ... }
269 // ^
270 // |
271 // replace with: ` + Bar`
272 //
273 // Or, if user has provided some bounds, suggest restricting them:
274 //
275 // fn foo<T: Foo>(t: T) { ... }
276 // ---
277 // |
278 // help: consider further restricting this bound with `+ Bar`
279 //
280 // Suggestion for tools in this case is:
281 //
282 // fn foo<T: Foo>(t: T) { ... }
283 // --
284 // |
285 // replace with: `T: Bar +`
286 let param_def_id = tcx.hir().local_def_id(param.hir_id);
287 if let Some(span) = generics.bounds_span_for_suggestions(param_def_id) {
288 suggest_restrict(span, true);
5e7ed085 289 continue;
f9f354fc
XL
290 }
291
923072b8 292 if generics.has_where_clause_predicates {
5e7ed085
FG
293 // This part is a bit tricky, because using the `where` clause user can
294 // provide zero, one or many bounds for the same type parameter, so we
295 // have following cases to consider:
296 //
04454e1e 297 // When the type parameter has been provided zero bounds
5e7ed085
FG
298 //
299 // Message:
300 // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
301 // - help: consider restricting this type parameter with `where X: Bar`
302 //
303 // Suggestion:
304 // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
305 // - insert: `, X: Bar`
04454e1e
FG
306 suggestions.push((
307 generics.tail_span_for_predicate_suggestion(),
308 constraints
309 .iter()
310 .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint))
311 .collect::<String>(),
312 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
313 ));
314 continue;
315 }
5e7ed085 316
04454e1e
FG
317 // Additionally, there may be no `where` clause but the generic parameter has a default:
318 //
319 // Message:
320 // trait Foo<T=()> {... }
321 // - help: consider further restricting this type parameter with `where T: Zar`
322 //
323 // Suggestion:
324 // trait Foo<T=()> {... }
325 // - insert: `where T: Zar`
326 if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) {
327 // Suggest a bound, but there is no existing `where` clause *and* the type param has a
328 // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
329 suggestions.push((
330 generics.tail_span_for_predicate_suggestion(),
331 format!(" where {}: {}", param_name, constraint),
332 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
333 ));
334 continue;
335 }
f9f354fc 336
04454e1e
FG
337 // If user has provided a colon, don't suggest adding another:
338 //
339 // fn foo<T:>(t: T) { ... }
340 // - insert: consider restricting this type parameter with `T: Foo`
341 if let Some(colon_span) = param.colon_span {
342 suggestions.push((
343 colon_span.shrink_to_hi(),
344 format!(" {}", constraint),
345 SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
346 ));
347 continue;
f9f354fc 348 }
04454e1e
FG
349
350 // If user hasn't provided any bounds, suggest adding a new one:
351 //
352 // fn foo<T>(t: T) { ... }
353 // - help: consider restricting this type parameter with `T: Foo`
354 suggestions.push((
355 param.span.shrink_to_hi(),
356 format!(": {}", constraint),
357 SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
358 ));
5e7ed085
FG
359 }
360
361 if suggestions.len() == 1 {
362 let (span, suggestion, msg) = suggestions.pop().unwrap();
f9f354fc 363
5e7ed085
FG
364 let s;
365 let msg = match msg {
366 SuggestChangingConstraintsMessage::RestrictBoundFurther => {
367 "consider further restricting this bound"
368 }
369 SuggestChangingConstraintsMessage::RestrictType { ty } => {
370 s = format!("consider restricting type parameter `{}`", ty);
371 &s
372 }
373 SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => {
374 s = format!("consider further restricting type parameter `{}`", ty);
375 &s
376 }
377 SuggestChangingConstraintsMessage::RemovingQSized => {
378 "consider removing the `?Sized` bound to make the type parameter `Sized`"
379 }
380 };
381
382 err.span_suggestion_verbose(span, msg, suggestion, applicability);
383 } else if suggestions.len() > 1 {
384 err.multipart_suggestion_verbose(
385 "consider restricting type parameters",
386 suggestions.into_iter().map(|(span, suggestion, _)| (span, suggestion)).collect(),
387 applicability,
388 );
f9f354fc 389 }
5e7ed085
FG
390
391 true
f9f354fc
XL
392}
393
f035d41b
XL
394/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
395pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>);
396
397impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
f035d41b
XL
398 fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
399 match ty.kind {
400 hir::TyKind::TraitObject(
401 _,
402 hir::Lifetime {
403 name:
404 hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
405 ..
406 },
6a06907d 407 _,
f035d41b
XL
408 ) => {
409 self.0.push(ty);
410 }
f2b60f7d 411 hir::TyKind::OpaqueDef(item_id, _, _) => {
f035d41b 412 self.0.push(ty);
6a06907d 413 let item = self.1.item(item_id);
f035d41b
XL
414 hir::intravisit::walk_item(self, item);
415 }
416 _ => {}
f9f354fc 417 }
f035d41b 418 hir::intravisit::walk_ty(self, ty);
f9f354fc
XL
419 }
420}
a2a8927a
XL
421
422/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
423pub struct StaticLifetimeVisitor<'tcx>(pub Vec<Span>, pub crate::hir::map::Map<'tcx>);
424
425impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> {
a2a8927a
XL
426 fn visit_lifetime(&mut self, lt: &'v hir::Lifetime) {
427 if let hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static =
428 lt.name
429 {
430 self.0.push(lt.span);
431 }
432 }
433}
923072b8
FG
434
435pub struct IsSuggestableVisitor<'tcx> {
436 tcx: TyCtxt<'tcx>,
064997fb 437 infer_suggestable: bool,
923072b8
FG
438}
439
440impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> {
441 type BreakTy = ();
442
443 fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
444 match t.kind() {
064997fb
FG
445 Infer(InferTy::TyVar(_)) if self.infer_suggestable => {}
446
923072b8
FG
447 FnDef(..)
448 | Closure(..)
449 | Infer(..)
450 | Generator(..)
451 | GeneratorWitness(..)
452 | Bound(_, _)
453 | Placeholder(_)
454 | Error(_) => {
455 return ControlFlow::Break(());
456 }
457
458 Opaque(did, _) => {
459 let parent = self.tcx.parent(*did);
460 if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = self.tcx.def_kind(parent)
461 && let Opaque(parent_did, _) = self.tcx.type_of(parent).kind()
462 && parent_did == did
463 {
464 // Okay
465 } else {
466 return ControlFlow::Break(());
467 }
468 }
469
f2b60f7d 470 Dynamic(dty, _, _) => {
923072b8
FG
471 for pred in *dty {
472 match pred.skip_binder() {
473 ExistentialPredicate::Trait(_) | ExistentialPredicate::Projection(_) => {
474 // Okay
475 }
476 _ => return ControlFlow::Break(()),
477 }
478 }
479 }
480
481 Param(param) => {
482 // FIXME: It would be nice to make this not use string manipulation,
483 // but it's pretty hard to do this, since `ty::ParamTy` is missing
484 // sufficient info to determine if it is synthetic, and we don't
485 // always have a convenient way of getting `ty::Generics` at the call
486 // sites we invoke `IsSuggestable::is_suggestable`.
487 if param.name.as_str().starts_with("impl ") {
488 return ControlFlow::Break(());
489 }
490 }
491
492 _ => {}
493 }
494
495 t.super_visit_with(self)
496 }
497
498 fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
499 match c.kind() {
064997fb
FG
500 ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => {}
501
923072b8
FG
502 ConstKind::Infer(..)
503 | ConstKind::Bound(..)
504 | ConstKind::Placeholder(..)
505 | ConstKind::Error(..) => {
506 return ControlFlow::Break(());
507 }
508 _ => {}
509 }
510
511 c.super_visit_with(self)
512 }
513}
2b03887a
FG
514
515#[derive(Diagnostic)]
516#[diag(borrowck_const_not_used_in_type_alias)]
517pub(super) struct ConstNotUsedTraitAlias {
518 pub ct: String,
519 #[primary_span]
520 pub span: Span,
521}