1 //! Diagnostics related methods for `TyS`.
2
3 use crate::ty::TyKind::*;
4 use crate::ty::{InferTy, TyCtxt, TyS};
5 use rustc_errors::{Applicability, DiagnosticBuilder};
6 use rustc_hir as hir;
7 use rustc_hir::def_id::DefId;
8 use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
9
10 impl<'tcx> TyS<'tcx> {
11 /// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive.
is_primitive_ty(&self) -> bool12 pub fn is_primitive_ty(&self) -> bool {
13 matches!(
14 self.kind(),
15 Bool | Char
16 | Str
17 | Int(_)
18 | Uint(_)
19 | Float(_)
20 | Infer(
21 InferTy::IntVar(_)
22 | InferTy::FloatVar(_)
23 | InferTy::FreshIntTy(_)
24 | InferTy::FreshFloatTy(_)
25 )
26 )
27 }
28
29 /// Whether the type is succinctly representable as a type instead of just referred to with a
30 /// description in error messages. This is used in the main error message.
is_simple_ty(&self) -> bool31 pub fn is_simple_ty(&self) -> bool {
32 match self.kind() {
33 Bool
34 | Char
35 | Str
36 | Int(_)
37 | Uint(_)
38 | Float(_)
39 | Infer(
40 InferTy::IntVar(_)
41 | InferTy::FloatVar(_)
42 | InferTy::FreshIntTy(_)
43 | InferTy::FreshFloatTy(_),
44 ) => true,
45 Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
46 Tuple(tys) if tys.is_empty() => true,
47 _ => false,
48 }
49 }
50
51 /// Whether the type is succinctly representable as a type instead of just referred to with a
52 /// description in error messages. This is used in the primary span label. Beyond what
53 /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
54 /// ADTs with no type arguments.
is_simple_text(&self) -> bool55 pub fn is_simple_text(&self) -> bool {
56 match self.kind() {
57 Adt(_, substs) => substs.non_erasable_generics().next().is_none(),
58 Ref(_, ty, _) => ty.is_simple_text(),
59 _ => self.is_simple_ty(),
60 }
61 }
62
63 /// Whether the type can be safely suggested during error recovery.
is_suggestable(&self) -> bool64 pub fn is_suggestable(&self) -> bool {
65 !matches!(
66 self.kind(),
67 Opaque(..)
68 | FnDef(..)
69 | FnPtr(..)
70 | Dynamic(..)
71 | Closure(..)
72 | Infer(..)
73 | Projection(..)
74 )
75 }
76 }
77
suggest_arbitrary_trait_bound( generics: &hir::Generics<'_>, err: &mut DiagnosticBuilder<'_>, param_name: &str, constraint: &str, ) -> bool78 pub fn suggest_arbitrary_trait_bound(
79 generics: &hir::Generics<'_>,
80 err: &mut DiagnosticBuilder<'_>,
81 param_name: &str,
82 constraint: &str,
83 ) -> bool {
84 let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
85 match (param, param_name) {
86 (Some(_), "Self") => return false,
87 _ => {}
88 }
89 // Suggest a where clause bound for a non-type paremeter.
90 let (action, prefix) = if generics.where_clause.predicates.is_empty() {
91 ("introducing a", " where ")
92 } else {
93 ("extending the", ", ")
94 };
95 err.span_suggestion_verbose(
96 generics.where_clause.tail_span_for_suggestion(),
97 &format!(
98 "consider {} `where` bound, but there might be an alternative better way to express \
99 this requirement",
100 action,
101 ),
102 format!("{}{}: {}", prefix, param_name, constraint),
103 Applicability::MaybeIncorrect,
104 );
105 true
106 }
107
suggest_removing_unsized_bound( generics: &hir::Generics<'_>, err: &mut DiagnosticBuilder<'_>, param_name: &str, param: &hir::GenericParam<'_>, def_id: Option<DefId>, )108 fn suggest_removing_unsized_bound(
109 generics: &hir::Generics<'_>,
110 err: &mut DiagnosticBuilder<'_>,
111 param_name: &str,
112 param: &hir::GenericParam<'_>,
113 def_id: Option<DefId>,
114 ) {
115 // See if there's a `?Sized` bound that can be removed to suggest that.
116 // First look at the `where` clause because we can have `where T: ?Sized`,
117 // then look at params.
118 for (where_pos, predicate) in generics.where_clause.predicates.iter().enumerate() {
119 match predicate {
120 WherePredicate::BoundPredicate(WhereBoundPredicate {
121 bounded_ty:
122 hir::Ty {
123 kind:
124 hir::TyKind::Path(hir::QPath::Resolved(
125 None,
126 hir::Path {
127 segments: [segment],
128 res: hir::def::Res::Def(hir::def::DefKind::TyParam, _),
129 ..
130 },
131 )),
132 ..
133 },
134 bounds,
135 span,
136 ..
137 }) if segment.ident.as_str() == param_name => {
138 for (pos, bound) in bounds.iter().enumerate() {
139 match bound {
140 hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
141 if poly.trait_ref.trait_def_id() == def_id => {}
142 _ => continue,
143 }
144 let sp = match (
145 bounds.len(),
146 pos,
147 generics.where_clause.predicates.len(),
148 where_pos,
149 ) {
150 // where T: ?Sized
151 // ^^^^^^^^^^^^^^^
152 (1, _, 1, _) => generics.where_clause.span,
153 // where Foo: Bar, T: ?Sized,
154 // ^^^^^^^^^^^
155 (1, _, len, pos) if pos == len - 1 => generics.where_clause.predicates
156 [pos - 1]
157 .span()
158 .shrink_to_hi()
159 .to(*span),
160 // where T: ?Sized, Foo: Bar,
161 // ^^^^^^^^^^^
162 (1, _, _, pos) => {
163 span.until(generics.where_clause.predicates[pos + 1].span())
164 }
165 // where T: ?Sized + Bar, Foo: Bar,
166 // ^^^^^^^^^
167 (_, 0, _, _) => bound.span().to(bounds[1].span().shrink_to_lo()),
168 // where T: Bar + ?Sized, Foo: Bar,
169 // ^^^^^^^^^
170 (_, pos, _, _) => bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
171 };
172 err.span_suggestion_verbose(
173 sp,
174 "consider removing the `?Sized` bound to make the \
175 type parameter `Sized`",
176 String::new(),
177 Applicability::MaybeIncorrect,
178 );
179 }
180 }
181 _ => {}
182 }
183 }
184 for (pos, bound) in param.bounds.iter().enumerate() {
185 match bound {
186 hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
187 if poly.trait_ref.trait_def_id() == def_id =>
188 {
189 let sp = match (param.bounds.len(), pos) {
190 // T: ?Sized,
191 // ^^^^^^^^
192 (1, _) => param.span.shrink_to_hi().to(bound.span()),
193 // T: ?Sized + Bar,
194 // ^^^^^^^^^
195 (_, 0) => bound.span().to(param.bounds[1].span().shrink_to_lo()),
196 // T: Bar + ?Sized,
197 // ^^^^^^^^^
198 (_, pos) => param.bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
199 };
200 err.span_suggestion_verbose(
201 sp,
202 "consider removing the `?Sized` bound to make the type parameter \
203 `Sized`",
204 String::new(),
205 Applicability::MaybeIncorrect,
206 );
207 }
208 _ => {}
209 }
210 }
211 }
212
213 /// Suggest restricting a type param with a new bound.
suggest_constraining_type_param( tcx: TyCtxt<'_>, generics: &hir::Generics<'_>, err: &mut DiagnosticBuilder<'_>, param_name: &str, constraint: &str, def_id: Option<DefId>, ) -> bool214 pub fn suggest_constraining_type_param(
215 tcx: TyCtxt<'_>,
216 generics: &hir::Generics<'_>,
217 err: &mut DiagnosticBuilder<'_>,
218 param_name: &str,
219 constraint: &str,
220 def_id: Option<DefId>,
221 ) -> bool {
222 let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
223
224 let Some(param) = param else {
225 return false;
226 };
227
228 const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound";
229 let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name);
230 let msg_restrict_type_further =
231 format!("consider further restricting type parameter `{}`", param_name);
232
233 if def_id == tcx.lang_items().sized_trait() {
234 // Type parameters are already `Sized` by default.
235 err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint));
236 suggest_removing_unsized_bound(generics, err, param_name, param, def_id);
237 return true;
238 }
239 let mut suggest_restrict = |span| {
240 err.span_suggestion_verbose(
241 span,
242 MSG_RESTRICT_BOUND_FURTHER,
243 format!(" + {}", constraint),
244 Applicability::MachineApplicable,
245 );
246 };
247
248 if param_name.starts_with("impl ") {
249 // If there's an `impl Trait` used in argument position, suggest
250 // restricting it:
251 //
252 // fn foo(t: impl Foo) { ... }
253 // --------
254 // |
255 // help: consider further restricting this bound with `+ Bar`
256 //
257 // Suggestion for tools in this case is:
258 //
259 // fn foo(t: impl Foo) { ... }
260 // --------
261 // |
262 // replace with: `impl Foo + Bar`
263
264 suggest_restrict(param.span.shrink_to_hi());
265 return true;
266 }
267
268 if generics.where_clause.predicates.is_empty()
269 // Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
270 // `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
271 && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
272 {
273 if let Some(bounds_span) = param.bounds_span() {
274 // If user has provided some bounds, suggest restricting them:
275 //
276 // fn foo<T: Foo>(t: T) { ... }
277 // ---
278 // |
279 // help: consider further restricting this bound with `+ Bar`
280 //
281 // Suggestion for tools in this case is:
282 //
283 // fn foo<T: Foo>(t: T) { ... }
284 // --
285 // |
286 // replace with: `T: Bar +`
287 suggest_restrict(bounds_span.shrink_to_hi());
288 } else {
289 // If user hasn't provided any bounds, suggest adding a new one:
290 //
291 // fn foo<T>(t: T) { ... }
292 // - help: consider restricting this type parameter with `T: Foo`
293 err.span_suggestion_verbose(
294 param.span.shrink_to_hi(),
295 &msg_restrict_type,
296 format!(": {}", constraint),
297 Applicability::MachineApplicable,
298 );
299 }
300
301 true
302 } else {
303 // This part is a bit tricky, because using the `where` clause user can
304 // provide zero, one or many bounds for the same type parameter, so we
305 // have following cases to consider:
306 //
307 // 1) When the type parameter has been provided zero bounds
308 //
309 // Message:
310 // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
311 // - help: consider restricting this type parameter with `where X: Bar`
312 //
313 // Suggestion:
314 // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
315 // - insert: `, X: Bar`
316 //
317 //
318 // 2) When the type parameter has been provided one bound
319 //
320 // Message:
321 // fn foo<T>(t: T) where T: Foo { ... }
322 // ^^^^^^
323 // |
324 // help: consider further restricting this bound with `+ Bar`
325 //
326 // Suggestion:
327 // fn foo<T>(t: T) where T: Foo { ... }
328 // ^^
329 // |
330 // replace with: `T: Bar +`
331 //
332 //
333 // 3) When the type parameter has been provided many bounds
334 //
335 // Message:
336 // fn foo<T>(t: T) where T: Foo, T: Bar {... }
337 // - help: consider further restricting this type parameter with `where T: Zar`
338 //
339 // Suggestion:
340 // fn foo<T>(t: T) where T: Foo, T: Bar {... }
341 // - insert: `, T: Zar`
342 //
343 // Additionally, there may be no `where` clause whatsoever in the case that this was
344 // reached because the generic parameter has a default:
345 //
346 // Message:
347 // trait Foo<T=()> {... }
348 // - help: consider further restricting this type parameter with `where T: Zar`
349 //
350 // Suggestion:
351 // trait Foo<T=()> where T: Zar {... }
352 // - insert: `where T: Zar`
353
354 if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
355 && generics.where_clause.predicates.len() == 0
356 {
357 // Suggest a bound, but there is no existing `where` clause *and* the type param has a
358 // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
359 err.span_suggestion_verbose(
360 generics.where_clause.tail_span_for_suggestion(),
361 &msg_restrict_type_further,
362 format!(" where {}: {}", param_name, constraint),
363 Applicability::MachineApplicable,
364 );
365 } else {
366 let mut param_spans = Vec::new();
367
368 for predicate in generics.where_clause.predicates {
369 if let WherePredicate::BoundPredicate(WhereBoundPredicate {
370 span,
371 bounded_ty,
372 ..
373 }) = predicate
374 {
375 if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
376 if let Some(segment) = path.segments.first() {
377 if segment.ident.to_string() == param_name {
378 param_spans.push(span);
379 }
380 }
381 }
382 }
383 }
384
385 match param_spans[..] {
386 [¶m_span] => suggest_restrict(param_span.shrink_to_hi()),
387 _ => {
388 err.span_suggestion_verbose(
389 generics.where_clause.tail_span_for_suggestion(),
390 &msg_restrict_type_further,
391 format!(", {}: {}", param_name, constraint),
392 Applicability::MachineApplicable,
393 );
394 }
395 }
396 }
397
398 true
399 }
400 }
401
402 /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
403 pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>);
404
405 impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
406 type Map = rustc_hir::intravisit::ErasedMap<'v>;
407
nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map>408 fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
409 hir::intravisit::NestedVisitorMap::None
410 }
411
visit_ty(&mut self, ty: &'v hir::Ty<'v>)412 fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
413 match ty.kind {
414 hir::TyKind::TraitObject(
415 _,
416 hir::Lifetime {
417 name:
418 hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
419 ..
420 },
421 _,
422 ) => {
423 self.0.push(ty);
424 }
425 hir::TyKind::OpaqueDef(item_id, _) => {
426 self.0.push(ty);
427 let item = self.1.item(item_id);
428 hir::intravisit::walk_item(self, item);
429 }
430 _ => {}
431 }
432 hir::intravisit::walk_ty(self, ty);
433 }
434 }
435