]>
Commit | Line | Data |
---|---|---|
f9f354fc XL |
1 | use crate::traits::{ObligationCause, ObligationCauseCode}; |
2 | use crate::ty::diagnostics::suggest_constraining_type_param; | |
6a06907d | 3 | use crate::ty::print::{FmtPrinter, Printer}; |
fc512014 | 4 | use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt}; |
f9f354fc XL |
5 | use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; |
6 | use rustc_errors::{pluralize, DiagnosticBuilder}; | |
dfeec247 XL |
7 | use rustc_hir as hir; |
8 | use rustc_hir::def_id::DefId; | |
f9f354fc XL |
9 | use rustc_span::symbol::{sym, Symbol}; |
10 | use rustc_span::{BytePos, MultiSpan, Span}; | |
83c7162d | 11 | use rustc_target::spec::abi; |
e9174d1e | 12 | |
60c5eb7d XL |
13 | use std::borrow::Cow; |
14 | use std::fmt; | |
e9174d1e | 15 | |
60c5eb7d | 16 | #[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable)] |
e9174d1e SL |
17 | pub struct ExpectedFound<T> { |
18 | pub expected: T, | |
cc61c64b | 19 | pub found: T, |
e9174d1e SL |
20 | } |
21 | ||
74b04a01 XL |
22 | impl<T> ExpectedFound<T> { |
23 | pub fn new(a_is_expected: bool, a: T, b: T) -> Self { | |
24 | if a_is_expected { | |
25 | ExpectedFound { expected: a, found: b } | |
26 | } else { | |
27 | ExpectedFound { expected: b, found: a } | |
28 | } | |
29 | } | |
30 | } | |
31 | ||
e9174d1e | 32 | // Data structures used in type unification |
60c5eb7d | 33 | #[derive(Clone, Debug, TypeFoldable)] |
e9174d1e SL |
34 | pub enum TypeError<'tcx> { |
35 | Mismatch, | |
94222f64 | 36 | ConstnessMismatch(ExpectedFound<ty::BoundConstness>), |
3c0e092e | 37 | PolarityMismatch(ExpectedFound<ty::ImplPolarity>), |
e9174d1e SL |
38 | UnsafetyMismatch(ExpectedFound<hir::Unsafety>), |
39 | AbiMismatch(ExpectedFound<abi::Abi>), | |
40 | Mutability, | |
cdc7bbd5 | 41 | ArgumentMutability(usize), |
e9174d1e | 42 | TupleSize(ExpectedFound<usize>), |
ea8adc8c | 43 | FixedArraySize(ExpectedFound<u64>), |
e9174d1e | 44 | ArgCount, |
3c0e092e | 45 | FieldMisMatch(Symbol, Symbol), |
ea8adc8c | 46 | |
7cac9316 | 47 | RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>), |
fc512014 XL |
48 | RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>), |
49 | RegionsOverlyPolymorphic(BoundRegionKind, Region<'tcx>), | |
0731742a | 50 | RegionsPlaceholderMismatch, |
ea8adc8c | 51 | |
e9174d1e | 52 | Sorts(ExpectedFound<Ty<'tcx>>), |
cdc7bbd5 | 53 | ArgumentSorts(ExpectedFound<Ty<'tcx>>, usize), |
e9174d1e | 54 | IntMismatch(ExpectedFound<ty::IntVarValue>), |
5869c6ff | 55 | FloatMismatch(ExpectedFound<ty::FloatTy>), |
e9174d1e | 56 | Traits(ExpectedFound<DefId>), |
e9174d1e | 57 | VariadicMismatch(ExpectedFound<bool>), |
ff7c6d11 XL |
58 | |
59 | /// Instantiating a type variable with the given type would have | |
60 | /// created a cycle (because it appears somewhere within that | |
61 | /// type). | |
62 | CyclicTy(Ty<'tcx>), | |
1b1a35ee | 63 | CyclicConst(&'tcx ty::Const<'tcx>), |
041b39d2 | 64 | ProjectionMismatched(ExpectedFound<DefId>), |
cdc7bbd5 XL |
65 | ExistentialMismatch( |
66 | ExpectedFound<&'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>>, | |
67 | ), | |
e74abb32 | 68 | ObjectUnsafeCoercion(DefId), |
48663c56 | 69 | ConstMismatch(ExpectedFound<&'tcx ty::Const<'tcx>>), |
e1599b0c XL |
70 | |
71 | IntrinsicCast, | |
f9f354fc XL |
72 | /// Safe `#[target_feature]` functions are not assignable to safe function pointers. |
73 | TargetFeatureCast(DefId), | |
e9174d1e SL |
74 | } |
75 | ||
e9174d1e SL |
76 | /// Explains the source of a type err in a short, human readable way. This is meant to be placed |
77 | /// in parentheses after some larger message. You should also invoke `note_and_explain_type_err()` | |
78 | /// afterwards to present additional details, particularly when it comes to lifetime-related | |
79 | /// errors. | |
80 | impl<'tcx> fmt::Display for TypeError<'tcx> { | |
0bf4aa26 | 81 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
e9174d1e | 82 | use self::TypeError::*; |
60c5eb7d XL |
83 | fn report_maybe_different( |
84 | f: &mut fmt::Formatter<'_>, | |
85 | expected: &str, | |
86 | found: &str, | |
87 | ) -> fmt::Result { | |
e9174d1e SL |
88 | // A naive approach to making sure that we're not reporting silly errors such as: |
89 | // (expected closure, found closure). | |
90 | if expected == found { | |
91 | write!(f, "expected {}, found a different {}", expected, found) | |
92 | } else { | |
93 | write!(f, "expected {}, found {}", expected, found) | |
94 | } | |
95 | } | |
96 | ||
fc512014 | 97 | let br_string = |br: ty::BoundRegionKind| match br { |
dfeec247 XL |
98 | ty::BrNamed(_, name) => format!(" {}", name), |
99 | _ => String::new(), | |
532ac7d7 XL |
100 | }; |
101 | ||
e9174d1e | 102 | match *self { |
ff7c6d11 | 103 | CyclicTy(_) => write!(f, "cyclic type of infinite size"), |
1b1a35ee | 104 | CyclicConst(_) => write!(f, "encountered a self-referencing constant"), |
e9174d1e | 105 | Mismatch => write!(f, "types differ"), |
94222f64 XL |
106 | ConstnessMismatch(values) => { |
107 | write!(f, "expected {} bound, found {} bound", values.expected, values.found) | |
108 | } | |
3c0e092e XL |
109 | PolarityMismatch(values) => { |
110 | write!(f, "expected {} polarity, found {} polarity", values.expected, values.found) | |
111 | } | |
e9174d1e | 112 | UnsafetyMismatch(values) => { |
dfeec247 | 113 | write!(f, "expected {} fn, found {} fn", values.expected, values.found) |
e9174d1e SL |
114 | } |
115 | AbiMismatch(values) => { | |
dfeec247 | 116 | write!(f, "expected {} fn, found {} fn", values.expected, values.found) |
e9174d1e | 117 | } |
cdc7bbd5 | 118 | ArgumentMutability(_) | Mutability => write!(f, "types differ in mutability"), |
dfeec247 XL |
119 | TupleSize(values) => write!( |
120 | f, | |
121 | "expected a tuple with {} element{}, \ | |
dc9dc135 | 122 | found one with {} element{}", |
dfeec247 XL |
123 | values.expected, |
124 | pluralize!(values.expected), | |
125 | values.found, | |
126 | pluralize!(values.found) | |
127 | ), | |
128 | FixedArraySize(values) => write!( | |
129 | f, | |
130 | "expected an array with a fixed size of {} element{}, \ | |
dc9dc135 | 131 | found one with {} element{}", |
dfeec247 XL |
132 | values.expected, |
133 | pluralize!(values.expected), | |
134 | values.found, | |
135 | pluralize!(values.found) | |
136 | ), | |
137 | ArgCount => write!(f, "incorrect number of function parameters"), | |
3c0e092e | 138 | FieldMisMatch(adt, field) => write!(f, "field type mismatch: {}.{}", adt, field), |
dfeec247 XL |
139 | RegionsDoesNotOutlive(..) => write!(f, "lifetime mismatch"), |
140 | RegionsInsufficientlyPolymorphic(br, _) => write!( | |
141 | f, | |
142 | "expected bound lifetime parameter{}, found concrete lifetime", | |
143 | br_string(br) | |
144 | ), | |
145 | RegionsOverlyPolymorphic(br, _) => write!( | |
146 | f, | |
147 | "expected concrete lifetime, found bound lifetime parameter{}", | |
148 | br_string(br) | |
149 | ), | |
150 | RegionsPlaceholderMismatch => write!(f, "one type is more general than the other"), | |
cdc7bbd5 | 151 | ArgumentSorts(values, _) | Sorts(values) => ty::tls::with(|tcx| { |
dfeec247 XL |
152 | report_maybe_different( |
153 | f, | |
154 | &values.expected.sort_string(tcx), | |
155 | &values.found.sort_string(tcx), | |
156 | ) | |
e9174d1e SL |
157 | }), |
158 | Traits(values) => ty::tls::with(|tcx| { | |
dfeec247 XL |
159 | report_maybe_different( |
160 | f, | |
161 | &format!("trait `{}`", tcx.def_path_str(values.expected)), | |
162 | &format!("trait `{}`", tcx.def_path_str(values.found)), | |
163 | ) | |
e9174d1e | 164 | }), |
e9174d1e | 165 | IntMismatch(ref values) => { |
17df50a5 XL |
166 | let expected = match values.expected { |
167 | ty::IntVarValue::IntType(ty) => ty.name_str(), | |
168 | ty::IntVarValue::UintType(ty) => ty.name_str(), | |
169 | }; | |
170 | let found = match values.found { | |
171 | ty::IntVarValue::IntType(ty) => ty.name_str(), | |
172 | ty::IntVarValue::UintType(ty) => ty.name_str(), | |
173 | }; | |
174 | write!(f, "expected `{}`, found `{}`", expected, found) | |
e9174d1e SL |
175 | } |
176 | FloatMismatch(ref values) => { | |
17df50a5 XL |
177 | write!( |
178 | f, | |
179 | "expected `{}`, found `{}`", | |
180 | values.expected.name_str(), | |
181 | values.found.name_str() | |
182 | ) | |
e9174d1e | 183 | } |
dfeec247 XL |
184 | VariadicMismatch(ref values) => write!( |
185 | f, | |
186 | "expected {} fn, found {} function", | |
187 | if values.expected { "variadic" } else { "non-variadic" }, | |
188 | if values.found { "variadic" } else { "non-variadic" } | |
189 | ), | |
041b39d2 | 190 | ProjectionMismatched(ref values) => ty::tls::with(|tcx| { |
dfeec247 XL |
191 | write!( |
192 | f, | |
193 | "expected {}, found {}", | |
194 | tcx.def_path_str(values.expected), | |
195 | tcx.def_path_str(values.found) | |
196 | ) | |
041b39d2 | 197 | }), |
dfeec247 XL |
198 | ExistentialMismatch(ref values) => report_maybe_different( |
199 | f, | |
200 | &format!("trait `{}`", values.expected), | |
201 | &format!("trait `{}`", values.found), | |
202 | ), | |
48663c56 | 203 | ConstMismatch(ref values) => { |
dc9dc135 | 204 | write!(f, "expected `{}`, found `{}`", values.expected, values.found) |
48663c56 | 205 | } |
dfeec247 | 206 | IntrinsicCast => write!(f, "cannot coerce intrinsics to function pointers"), |
f9f354fc XL |
207 | TargetFeatureCast(_) => write!( |
208 | f, | |
209 | "cannot coerce functions with `#[target_feature]` to safe function pointers" | |
210 | ), | |
e74abb32 | 211 | ObjectUnsafeCoercion(_) => write!(f, "coercion to object-unsafe trait object"), |
e9174d1e SL |
212 | } |
213 | } | |
214 | } | |
215 | ||
60c5eb7d XL |
216 | impl<'tcx> TypeError<'tcx> { |
217 | pub fn must_include_note(&self) -> bool { | |
218 | use self::TypeError::*; | |
219 | match self { | |
94222f64 | 220 | CyclicTy(_) | CyclicConst(_) | UnsafetyMismatch(_) | ConstnessMismatch(_) |
3c0e092e XL |
221 | | PolarityMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_) |
222 | | ArgumentSorts(..) | Sorts(_) | IntMismatch(_) | FloatMismatch(_) | |
223 | | VariadicMismatch(_) | TargetFeatureCast(_) => false, | |
60c5eb7d | 224 | |
dfeec247 | 225 | Mutability |
cdc7bbd5 | 226 | | ArgumentMutability(_) |
dfeec247 XL |
227 | | TupleSize(_) |
228 | | ArgCount | |
3c0e092e | 229 | | FieldMisMatch(..) |
dfeec247 XL |
230 | | RegionsDoesNotOutlive(..) |
231 | | RegionsInsufficientlyPolymorphic(..) | |
232 | | RegionsOverlyPolymorphic(..) | |
233 | | RegionsPlaceholderMismatch | |
234 | | Traits(_) | |
235 | | ProjectionMismatched(_) | |
dfeec247 XL |
236 | | ExistentialMismatch(_) |
237 | | ConstMismatch(_) | |
238 | | IntrinsicCast | |
239 | | ObjectUnsafeCoercion(_) => true, | |
60c5eb7d XL |
240 | } |
241 | } | |
242 | } | |
243 | ||
dc9dc135 XL |
244 | impl<'tcx> ty::TyS<'tcx> { |
245 | pub fn sort_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> { | |
1b1a35ee | 246 | match *self.kind() { |
dfeec247 XL |
247 | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => { |
248 | format!("`{}`", self).into() | |
249 | } | |
60c5eb7d | 250 | ty::Tuple(ref tys) if tys.is_empty() => format!("`{}`", self).into(), |
e9174d1e | 251 | |
532ac7d7 XL |
252 | ty::Adt(def, _) => format!("{} `{}`", def.descr(), tcx.def_path_str(def.did)).into(), |
253 | ty::Foreign(def_id) => format!("extern type `{}`", tcx.def_path_str(def_id)).into(), | |
60c5eb7d | 254 | ty::Array(t, n) => { |
6a06907d XL |
255 | if t.is_simple_ty() { |
256 | return format!("array `{}`", self).into(); | |
257 | } | |
258 | ||
29967ef6 | 259 | let n = tcx.lift(n).unwrap(); |
6a06907d XL |
260 | if let ty::ConstKind::Value(v) = n.val { |
261 | if let Some(n) = v.try_to_machine_usize(tcx) { | |
262 | return format!("array of {} element{}", n, pluralize!(n)).into(); | |
263 | } | |
dc9dc135 | 264 | } |
6a06907d | 265 | "array".into() |
ea8adc8c | 266 | } |
60c5eb7d | 267 | ty::Slice(ty) if ty.is_simple_ty() => format!("slice `{}`", self).into(), |
0bf4aa26 XL |
268 | ty::Slice(_) => "slice".into(), |
269 | ty::RawPtr(_) => "*-ptr".into(), | |
60c5eb7d | 270 | ty::Ref(_, ty, mutbl) => { |
94b46f34 | 271 | let tymut = ty::TypeAndMut { ty, mutbl }; |
5bcae85e | 272 | let tymut_string = tymut.to_string(); |
dfeec247 XL |
273 | if tymut_string != "_" |
274 | && (ty.is_simple_text() || tymut_string.len() < "mutable reference".len()) | |
275 | { | |
60c5eb7d | 276 | format!("`&{}`", tymut_string).into() |
dfeec247 XL |
277 | } else { |
278 | // Unknown type name, it's long or has type arguments | |
60c5eb7d | 279 | match mutbl { |
dfeec247 | 280 | hir::Mutability::Mut => "mutable reference", |
60c5eb7d | 281 | _ => "reference", |
dfeec247 XL |
282 | } |
283 | .into() | |
5bcae85e SL |
284 | } |
285 | } | |
0bf4aa26 XL |
286 | ty::FnDef(..) => "fn item".into(), |
287 | ty::FnPtr(_) => "fn pointer".into(), | |
94222f64 XL |
288 | ty::Dynamic(ref inner, ..) if let Some(principal) = inner.principal() => { |
289 | format!("trait object `dyn {}`", tcx.def_path_str(principal.def_id())).into() | |
e9174d1e | 290 | } |
94222f64 | 291 | ty::Dynamic(..) => "trait object".into(), |
0bf4aa26 | 292 | ty::Closure(..) => "closure".into(), |
6a06907d | 293 | ty::Generator(def_id, ..) => tcx.generator_kind(def_id).unwrap().descr().into(), |
0bf4aa26 XL |
294 | ty::GeneratorWitness(..) => "generator witness".into(), |
295 | ty::Tuple(..) => "tuple".into(), | |
296 | ty::Infer(ty::TyVar(_)) => "inferred type".into(), | |
0731742a XL |
297 | ty::Infer(ty::IntVar(_)) => "integer".into(), |
298 | ty::Infer(ty::FloatVar(_)) => "floating-point number".into(), | |
a1dfa0c6 XL |
299 | ty::Placeholder(..) => "placeholder type".into(), |
300 | ty::Bound(..) => "bound type".into(), | |
0bf4aa26 XL |
301 | ty::Infer(ty::FreshTy(_)) => "fresh type".into(), |
302 | ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".into(), | |
303 | ty::Infer(ty::FreshFloatTy(_)) => "fresh floating-point type".into(), | |
304 | ty::Projection(_) => "associated type".into(), | |
60c5eb7d | 305 | ty::Param(p) => format!("type parameter `{}`", p).into(), |
0bf4aa26 | 306 | ty::Opaque(..) => "opaque type".into(), |
f035d41b | 307 | ty::Error(_) => "type error".into(), |
e9174d1e SL |
308 | } |
309 | } | |
60c5eb7d | 310 | |
6a06907d | 311 | pub fn prefix_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> { |
1b1a35ee | 312 | match *self.kind() { |
dfeec247 | 313 | ty::Infer(_) |
f035d41b | 314 | | ty::Error(_) |
dfeec247 XL |
315 | | ty::Bool |
316 | | ty::Char | |
317 | | ty::Int(_) | |
318 | | ty::Uint(_) | |
319 | | ty::Float(_) | |
320 | | ty::Str | |
321 | | ty::Never => "type".into(), | |
60c5eb7d XL |
322 | ty::Tuple(ref tys) if tys.is_empty() => "unit type".into(), |
323 | ty::Adt(def, _) => def.descr().into(), | |
324 | ty::Foreign(_) => "extern type".into(), | |
325 | ty::Array(..) => "array".into(), | |
326 | ty::Slice(_) => "slice".into(), | |
327 | ty::RawPtr(_) => "raw pointer".into(), | |
328 | ty::Ref(.., mutbl) => match mutbl { | |
dfeec247 XL |
329 | hir::Mutability::Mut => "mutable reference", |
330 | _ => "reference", | |
331 | } | |
332 | .into(), | |
60c5eb7d XL |
333 | ty::FnDef(..) => "fn item".into(), |
334 | ty::FnPtr(_) => "fn pointer".into(), | |
335 | ty::Dynamic(..) => "trait object".into(), | |
336 | ty::Closure(..) => "closure".into(), | |
6a06907d | 337 | ty::Generator(def_id, ..) => tcx.generator_kind(def_id).unwrap().descr().into(), |
60c5eb7d XL |
338 | ty::GeneratorWitness(..) => "generator witness".into(), |
339 | ty::Tuple(..) => "tuple".into(), | |
340 | ty::Placeholder(..) => "higher-ranked type".into(), | |
341 | ty::Bound(..) => "bound type variable".into(), | |
342 | ty::Projection(_) => "associated type".into(), | |
60c5eb7d XL |
343 | ty::Param(_) => "type parameter".into(), |
344 | ty::Opaque(..) => "opaque type".into(), | |
345 | } | |
346 | } | |
e9174d1e SL |
347 | } |
348 | ||
dc9dc135 | 349 | impl<'tcx> TyCtxt<'tcx> { |
e1599b0c XL |
350 | pub fn note_and_explain_type_err( |
351 | self, | |
352 | db: &mut DiagnosticBuilder<'_>, | |
353 | err: &TypeError<'tcx>, | |
f9f354fc | 354 | cause: &ObligationCause<'tcx>, |
e1599b0c | 355 | sp: Span, |
60c5eb7d | 356 | body_owner_def_id: DefId, |
e1599b0c | 357 | ) { |
e9174d1e | 358 | use self::TypeError::*; |
f9f354fc | 359 | debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause); |
e1599b0c | 360 | match err { |
cdc7bbd5 | 361 | ArgumentSorts(values, _) | Sorts(values) => { |
1b1a35ee | 362 | match (values.expected.kind(), values.found.kind()) { |
29967ef6 XL |
363 | (ty::Closure(..), ty::Closure(..)) => { |
364 | db.note("no two closures, even if identical, have the same type"); | |
365 | db.help("consider boxing your closure and/or using it as a trait object"); | |
366 | } | |
367 | (ty::Opaque(..), ty::Opaque(..)) => { | |
368 | // Issue #63167 | |
369 | db.note("distinct uses of `impl Trait` result in different opaque types"); | |
370 | } | |
94222f64 | 371 | (ty::Float(_), ty::Infer(ty::IntVar(_))) |
dfeec247 XL |
372 | if let Ok( |
373 | // Issue #53280 | |
374 | snippet, | |
94222f64 XL |
375 | ) = self.sess.source_map().span_to_snippet(sp) => |
376 | { | |
377 | if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') { | |
378 | db.span_suggestion( | |
379 | sp, | |
380 | "use a float literal", | |
381 | format!("{}.0", snippet), | |
382 | MachineApplicable, | |
383 | ); | |
b7449926 | 384 | } |
dfeec247 | 385 | } |
60c5eb7d XL |
386 | (ty::Param(expected), ty::Param(found)) => { |
387 | let generics = self.generics_of(body_owner_def_id); | |
388 | let e_span = self.def_span(generics.type_param(expected, self).def_id); | |
389 | if !sp.contains(e_span) { | |
390 | db.span_label(e_span, "expected type parameter"); | |
391 | } | |
392 | let f_span = self.def_span(generics.type_param(found, self).def_id); | |
393 | if !sp.contains(f_span) { | |
394 | db.span_label(f_span, "found type parameter"); | |
395 | } | |
dfeec247 XL |
396 | db.note( |
397 | "a type parameter was expected, but a different one was found; \ | |
29967ef6 | 398 | you might be missing a type parameter or trait bound", |
dfeec247 XL |
399 | ); |
400 | db.note( | |
401 | "for more information, visit \ | |
29967ef6 XL |
402 | https://doc.rust-lang.org/book/ch10-02-traits.html\ |
403 | #traits-as-parameters", | |
dfeec247 | 404 | ); |
e1599b0c XL |
405 | } |
406 | (ty::Projection(_), ty::Projection(_)) => { | |
407 | db.note("an associated type was expected, but a different one was found"); | |
408 | } | |
f9f354fc XL |
409 | (ty::Param(p), ty::Projection(proj)) | (ty::Projection(proj), ty::Param(p)) => { |
410 | let generics = self.generics_of(body_owner_def_id); | |
411 | let p_span = self.def_span(generics.type_param(p, self).def_id); | |
412 | if !sp.contains(p_span) { | |
413 | db.span_label(p_span, "this type parameter"); | |
414 | } | |
415 | let hir = self.hir(); | |
416 | let mut note = true; | |
417 | if let Some(generics) = generics | |
418 | .type_param(p, self) | |
419 | .def_id | |
420 | .as_local() | |
3dfed10e | 421 | .map(|id| hir.local_def_id_to_hir_id(id)) |
f9f354fc XL |
422 | .and_then(|id| self.hir().find(self.hir().get_parent_node(id))) |
423 | .as_ref() | |
424 | .and_then(|node| node.generics()) | |
425 | { | |
426 | // Synthesize the associated type restriction `Add<Output = Expected>`. | |
427 | // FIXME: extract this logic for use in other diagnostics. | |
6a06907d | 428 | let (trait_ref, assoc_substs) = proj.trait_ref_and_own_substs(self); |
f9f354fc XL |
429 | let path = |
430 | self.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs); | |
431 | let item_name = self.item_name(proj.item_def_id); | |
6a06907d XL |
432 | let item_args = self.format_generic_args(assoc_substs); |
433 | ||
f9f354fc | 434 | let path = if path.ends_with('>') { |
6a06907d XL |
435 | format!( |
436 | "{}, {}{} = {}>", | |
437 | &path[..path.len() - 1], | |
438 | item_name, | |
439 | item_args, | |
440 | p | |
441 | ) | |
f9f354fc | 442 | } else { |
6a06907d | 443 | format!("{}<{}{} = {}>", path, item_name, item_args, p) |
f9f354fc XL |
444 | }; |
445 | note = !suggest_constraining_type_param( | |
446 | self, | |
447 | generics, | |
448 | db, | |
449 | &format!("{}", proj.self_ty()), | |
450 | &path, | |
451 | None, | |
452 | ); | |
453 | } | |
454 | if note { | |
455 | db.note("you might be missing a type parameter or trait bound"); | |
456 | } | |
e1599b0c | 457 | } |
f9f354fc XL |
458 | (ty::Param(p), ty::Dynamic(..) | ty::Opaque(..)) |
459 | | (ty::Dynamic(..) | ty::Opaque(..), ty::Param(p)) => { | |
60c5eb7d XL |
460 | let generics = self.generics_of(body_owner_def_id); |
461 | let p_span = self.def_span(generics.type_param(p, self).def_id); | |
462 | if !sp.contains(p_span) { | |
463 | db.span_label(p_span, "this type parameter"); | |
464 | } | |
e1599b0c XL |
465 | db.help("type parameters must be constrained to match other types"); |
466 | if self.sess.teach(&db.get_code().unwrap()) { | |
dfeec247 XL |
467 | db.help( |
468 | "given a type parameter `T` and a method `foo`: | |
e1599b0c XL |
469 | ``` |
470 | trait Trait<T> { fn foo(&self) -> T; } | |
471 | ``` | |
472 | the only ways to implement method `foo` are: | |
473 | - constrain `T` with an explicit type: | |
474 | ``` | |
475 | impl Trait<String> for X { | |
476 | fn foo(&self) -> String { String::new() } | |
477 | } | |
478 | ``` | |
479 | - add a trait bound to `T` and call a method on that trait that returns `Self`: | |
480 | ``` | |
481 | impl<T: std::default::Default> Trait<T> for X { | |
482 | fn foo(&self) -> T { <T as std::default::Default>::default() } | |
483 | } | |
484 | ``` | |
485 | - change `foo` to return an argument of type `T`: | |
486 | ``` | |
487 | impl<T> Trait<T> for X { | |
488 | fn foo(&self, x: T) -> T { x } | |
489 | } | |
dfeec247 XL |
490 | ```", |
491 | ); | |
e1599b0c | 492 | } |
dfeec247 XL |
493 | db.note( |
494 | "for more information, visit \ | |
29967ef6 XL |
495 | https://doc.rust-lang.org/book/ch10-02-traits.html\ |
496 | #traits-as-parameters", | |
dfeec247 | 497 | ); |
e1599b0c | 498 | } |
1b1a35ee XL |
499 | (ty::Param(p), ty::Closure(..) | ty::Generator(..)) => { |
500 | let generics = self.generics_of(body_owner_def_id); | |
501 | let p_span = self.def_span(generics.type_param(p, self).def_id); | |
502 | if !sp.contains(p_span) { | |
503 | db.span_label(p_span, "this type parameter"); | |
504 | } | |
505 | db.help(&format!( | |
506 | "every closure has a distinct type and so could not always match the \ | |
507 | caller-chosen type of parameter `{}`", | |
508 | p | |
509 | )); | |
510 | } | |
f9f354fc XL |
511 | (ty::Param(p), _) | (_, ty::Param(p)) => { |
512 | let generics = self.generics_of(body_owner_def_id); | |
513 | let p_span = self.def_span(generics.type_param(p, self).def_id); | |
514 | if !sp.contains(p_span) { | |
515 | db.span_label(p_span, "this type parameter"); | |
e1599b0c | 516 | } |
f9f354fc XL |
517 | } |
518 | (ty::Projection(proj_ty), _) => { | |
519 | self.expected_projection( | |
520 | db, | |
521 | proj_ty, | |
522 | values, | |
523 | body_owner_def_id, | |
524 | &cause.code, | |
dfeec247 | 525 | ); |
e1599b0c | 526 | } |
f9f354fc XL |
527 | (_, ty::Projection(proj_ty)) => { |
528 | let msg = format!( | |
e1599b0c | 529 | "consider constraining the associated type `{}` to `{}`", |
dfeec247 | 530 | values.found, values.expected, |
dfeec247 | 531 | ); |
cdc7bbd5 XL |
532 | if !(self.suggest_constraining_opaque_associated_type( |
533 | db, | |
534 | &msg, | |
535 | proj_ty, | |
536 | values.expected, | |
537 | ) || self.suggest_constraint( | |
f9f354fc XL |
538 | db, |
539 | &msg, | |
540 | body_owner_def_id, | |
541 | proj_ty, | |
542 | values.expected, | |
cdc7bbd5 | 543 | )) { |
f9f354fc XL |
544 | db.help(&msg); |
545 | db.note( | |
546 | "for more information, visit \ | |
547 | https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", | |
548 | ); | |
549 | } | |
0bf4aa26 | 550 | } |
e1599b0c | 551 | _ => {} |
b7449926 | 552 | } |
e1599b0c XL |
553 | debug!( |
554 | "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})", | |
1b1a35ee XL |
555 | values.expected, |
556 | values.expected.kind(), | |
557 | values.found, | |
558 | values.found.kind(), | |
e1599b0c | 559 | ); |
dfeec247 | 560 | } |
ff7c6d11 XL |
561 | CyclicTy(ty) => { |
562 | // Watch out for various cases of cyclic types and try to explain. | |
563 | if ty.is_closure() || ty.is_generator() { | |
dfeec247 XL |
564 | db.note( |
565 | "closures cannot capture themselves or take themselves as argument;\n\ | |
74b04a01 XL |
566 | this error may be the result of a recent compiler bug-fix,\n\ |
567 | see issue #46062 <https://github.com/rust-lang/rust/issues/46062>\n\ | |
568 | for more information", | |
dfeec247 | 569 | ); |
ff7c6d11 XL |
570 | } |
571 | } | |
f9f354fc XL |
572 | TargetFeatureCast(def_id) => { |
573 | let attrs = self.get_attrs(*def_id); | |
574 | let target_spans = attrs | |
f9f354fc XL |
575 | .iter() |
576 | .filter(|attr| attr.has_name(sym::target_feature)) | |
577 | .map(|attr| attr.span); | |
578 | db.note( | |
579 | "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" | |
580 | ); | |
581 | db.span_labels(target_spans, "`#[target_feature]` added here"); | |
582 | } | |
e9174d1e SL |
583 | _ => {} |
584 | } | |
585 | } | |
f9f354fc XL |
586 | |
587 | fn suggest_constraint( | |
1b1a35ee | 588 | self, |
f9f354fc XL |
589 | db: &mut DiagnosticBuilder<'_>, |
590 | msg: &str, | |
591 | body_owner_def_id: DefId, | |
592 | proj_ty: &ty::ProjectionTy<'tcx>, | |
593 | ty: Ty<'tcx>, | |
594 | ) -> bool { | |
595 | let assoc = self.associated_item(proj_ty.item_def_id); | |
6a06907d | 596 | let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(self); |
f9f354fc XL |
597 | if let Some(item) = self.hir().get_if_local(body_owner_def_id) { |
598 | if let Some(hir_generics) = item.generics() { | |
599 | // Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`. | |
600 | // This will also work for `impl Trait`. | |
1b1a35ee | 601 | let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() { |
f9f354fc | 602 | let generics = self.generics_of(body_owner_def_id); |
1b1a35ee | 603 | generics.type_param(param_ty, self).def_id |
f9f354fc XL |
604 | } else { |
605 | return false; | |
606 | }; | |
607 | ||
608 | // First look in the `where` clause, as this might be | |
609 | // `fn foo<T>(x: T) where T: Trait`. | |
610 | for predicate in hir_generics.where_clause.predicates { | |
611 | if let hir::WherePredicate::BoundPredicate(pred) = predicate { | |
612 | if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = | |
613 | pred.bounded_ty.kind | |
614 | { | |
615 | if path.res.opt_def_id() == Some(def_id) { | |
616 | // This predicate is binding type param `A` in `<A as T>::Foo` to | |
617 | // something, potentially `T`. | |
618 | } else { | |
619 | continue; | |
620 | } | |
621 | } else { | |
622 | continue; | |
623 | } | |
624 | ||
625 | if self.constrain_generic_bound_associated_type_structured_suggestion( | |
626 | db, | |
627 | &trait_ref, | |
628 | pred.bounds, | |
629 | &assoc, | |
6a06907d | 630 | assoc_substs, |
f9f354fc XL |
631 | ty, |
632 | msg, | |
94222f64 | 633 | false, |
f9f354fc XL |
634 | ) { |
635 | return true; | |
636 | } | |
637 | } | |
638 | } | |
639 | for param in hir_generics.params { | |
640 | if self.hir().opt_local_def_id(param.hir_id).map(|id| id.to_def_id()) | |
641 | == Some(def_id) | |
642 | { | |
643 | // This is type param `A` in `<A as T>::Foo`. | |
644 | return self.constrain_generic_bound_associated_type_structured_suggestion( | |
645 | db, | |
646 | &trait_ref, | |
647 | param.bounds, | |
648 | &assoc, | |
6a06907d | 649 | assoc_substs, |
f9f354fc XL |
650 | ty, |
651 | msg, | |
94222f64 | 652 | false, |
f9f354fc XL |
653 | ); |
654 | } | |
655 | } | |
656 | } | |
657 | } | |
658 | false | |
659 | } | |
660 | ||
661 | /// An associated type was expected and a different type was found. | |
662 | /// | |
663 | /// We perform a few different checks to see what we can suggest: | |
664 | /// | |
665 | /// - In the current item, look for associated functions that return the expected type and | |
666 | /// suggest calling them. (Not a structured suggestion.) | |
667 | /// - If any of the item's generic bounds can be constrained, we suggest constraining the | |
668 | /// associated type to the found type. | |
669 | /// - If the associated type has a default type and was expected inside of a `trait`, we | |
670 | /// mention that this is disallowed. | |
671 | /// - If all other things fail, and the error is not because of a mismatch between the `trait` | |
672 | /// and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc | |
673 | /// fn that returns the type. | |
674 | fn expected_projection( | |
1b1a35ee | 675 | self, |
f9f354fc XL |
676 | db: &mut DiagnosticBuilder<'_>, |
677 | proj_ty: &ty::ProjectionTy<'tcx>, | |
678 | values: &ExpectedFound<Ty<'tcx>>, | |
679 | body_owner_def_id: DefId, | |
680 | cause_code: &ObligationCauseCode<'_>, | |
681 | ) { | |
682 | let msg = format!( | |
683 | "consider constraining the associated type `{}` to `{}`", | |
684 | values.expected, values.found | |
685 | ); | |
686 | let body_owner = self.hir().get_if_local(body_owner_def_id); | |
687 | let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name); | |
688 | ||
689 | // We don't want to suggest calling an assoc fn in a scope where that isn't feasible. | |
5869c6ff XL |
690 | let callable_scope = matches!( |
691 | body_owner, | |
f9f354fc XL |
692 | Some( |
693 | hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) | |
5869c6ff XL |
694 | | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) |
695 | | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }), | |
696 | ) | |
697 | ); | |
f9f354fc XL |
698 | let impl_comparison = matches!( |
699 | cause_code, | |
700 | ObligationCauseCode::CompareImplMethodObligation { .. } | |
701 | | ObligationCauseCode::CompareImplTypeObligation { .. } | |
702 | | ObligationCauseCode::CompareImplConstObligation | |
703 | ); | |
704 | let assoc = self.associated_item(proj_ty.item_def_id); | |
705 | if !callable_scope || impl_comparison { | |
706 | // We do not want to suggest calling functions when the reason of the | |
707 | // type error is a comparison of an `impl` with its `trait` or when the | |
708 | // scope is outside of a `Body`. | |
709 | } else { | |
710 | // If we find a suitable associated function that returns the expected type, we don't | |
711 | // want the more general suggestion later in this method about "consider constraining | |
712 | // the associated type or calling a method that returns the associated type". | |
713 | let point_at_assoc_fn = self.point_at_methods_that_satisfy_associated_type( | |
714 | db, | |
715 | assoc.container.id(), | |
716 | current_method_ident, | |
717 | proj_ty.item_def_id, | |
718 | values.expected, | |
719 | ); | |
720 | // Possibly suggest constraining the associated type to conform to the | |
721 | // found type. | |
722 | if self.suggest_constraint(db, &msg, body_owner_def_id, proj_ty, values.found) | |
723 | || point_at_assoc_fn | |
724 | { | |
725 | return; | |
726 | } | |
727 | } | |
728 | ||
cdc7bbd5 | 729 | self.suggest_constraining_opaque_associated_type(db, &msg, proj_ty, values.found); |
f9f354fc XL |
730 | |
731 | if self.point_at_associated_type(db, body_owner_def_id, values.found) { | |
732 | return; | |
733 | } | |
734 | ||
735 | if !impl_comparison { | |
736 | // Generic suggestion when we can't be more specific. | |
737 | if callable_scope { | |
738 | db.help(&format!("{} or calling a method that returns `{}`", msg, values.expected)); | |
739 | } else { | |
740 | db.help(&msg); | |
741 | } | |
742 | db.note( | |
743 | "for more information, visit \ | |
744 | https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", | |
745 | ); | |
746 | } | |
747 | if self.sess.teach(&db.get_code().unwrap()) { | |
748 | db.help( | |
749 | "given an associated type `T` and a method `foo`: | |
750 | ``` | |
751 | trait Trait { | |
752 | type T; | |
753 | fn foo(&self) -> Self::T; | |
754 | } | |
755 | ``` | |
756 | the only way of implementing method `foo` is to constrain `T` with an explicit associated type: | |
757 | ``` | |
758 | impl Trait for X { | |
759 | type T = String; | |
760 | fn foo(&self) -> Self::T { String::new() } | |
761 | } | |
762 | ```", | |
763 | ); | |
764 | } | |
765 | } | |
766 | ||
cdc7bbd5 XL |
767 | /// When the expected `impl Trait` is not defined in the current item, it will come from |
768 | /// a return type. This can occur when dealing with `TryStream` (#71035). | |
769 | fn suggest_constraining_opaque_associated_type( | |
770 | self, | |
771 | db: &mut DiagnosticBuilder<'_>, | |
772 | msg: &str, | |
773 | proj_ty: &ty::ProjectionTy<'tcx>, | |
774 | ty: Ty<'tcx>, | |
775 | ) -> bool { | |
776 | let assoc = self.associated_item(proj_ty.item_def_id); | |
777 | if let ty::Opaque(def_id, _) = *proj_ty.self_ty().kind() { | |
3c0e092e XL |
778 | let opaque_local_def_id = def_id.as_local(); |
779 | let opaque_hir_ty = if let Some(opaque_local_def_id) = opaque_local_def_id { | |
780 | let hir = self.hir(); | |
781 | let opaque_hir_id = hir.local_def_id_to_hir_id(opaque_local_def_id); | |
782 | match &hir.expect_item(opaque_hir_id).kind { | |
783 | hir::ItemKind::OpaqueTy(opaque_hir_ty) => opaque_hir_ty, | |
784 | _ => bug!("The HirId comes from a `ty::Opaque`"), | |
785 | } | |
786 | } else { | |
787 | return false; | |
94222f64 XL |
788 | }; |
789 | ||
790 | let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(self); | |
791 | ||
792 | self.constrain_generic_bound_associated_type_structured_suggestion( | |
cdc7bbd5 | 793 | db, |
94222f64 XL |
794 | &trait_ref, |
795 | opaque_hir_ty.bounds, | |
796 | assoc, | |
797 | assoc_substs, | |
cdc7bbd5 | 798 | ty, |
94222f64 XL |
799 | msg, |
800 | true, | |
cdc7bbd5 XL |
801 | ) |
802 | } else { | |
803 | false | |
804 | } | |
805 | } | |
806 | ||
f9f354fc | 807 | fn point_at_methods_that_satisfy_associated_type( |
1b1a35ee | 808 | self, |
f9f354fc XL |
809 | db: &mut DiagnosticBuilder<'_>, |
810 | assoc_container_id: DefId, | |
811 | current_method_ident: Option<Symbol>, | |
812 | proj_ty_item_def_id: DefId, | |
813 | expected: Ty<'tcx>, | |
814 | ) -> bool { | |
815 | let items = self.associated_items(assoc_container_id); | |
816 | // Find all the methods in the trait that could be called to construct the | |
817 | // expected associated type. | |
818 | // FIXME: consider suggesting the use of associated `const`s. | |
819 | let methods: Vec<(Span, String)> = items | |
820 | .items | |
821 | .iter() | |
822 | .filter(|(name, item)| { | |
823 | ty::AssocKind::Fn == item.kind && Some(**name) != current_method_ident | |
824 | }) | |
825 | .filter_map(|(_, item)| { | |
826 | let method = self.fn_sig(item.def_id); | |
1b1a35ee | 827 | match *method.output().skip_binder().kind() { |
f9f354fc XL |
828 | ty::Projection(ty::ProjectionTy { item_def_id, .. }) |
829 | if item_def_id == proj_ty_item_def_id => | |
830 | { | |
831 | Some(( | |
832 | self.sess.source_map().guess_head_span(self.def_span(item.def_id)), | |
833 | format!("consider calling `{}`", self.def_path_str(item.def_id)), | |
834 | )) | |
835 | } | |
836 | _ => None, | |
837 | } | |
838 | }) | |
839 | .collect(); | |
840 | if !methods.is_empty() { | |
841 | // Use a single `help:` to show all the methods in the trait that can | |
842 | // be used to construct the expected associated type. | |
843 | let mut span: MultiSpan = | |
844 | methods.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into(); | |
845 | let msg = format!( | |
846 | "{some} method{s} {are} available that return{r} `{ty}`", | |
847 | some = if methods.len() == 1 { "a" } else { "some" }, | |
848 | s = pluralize!(methods.len()), | |
849 | are = if methods.len() == 1 { "is" } else { "are" }, | |
850 | r = if methods.len() == 1 { "s" } else { "" }, | |
851 | ty = expected | |
852 | ); | |
853 | for (sp, label) in methods.into_iter() { | |
854 | span.push_span_label(sp, label); | |
855 | } | |
856 | db.span_help(span, &msg); | |
857 | return true; | |
858 | } | |
859 | false | |
860 | } | |
861 | ||
862 | fn point_at_associated_type( | |
1b1a35ee | 863 | self, |
f9f354fc XL |
864 | db: &mut DiagnosticBuilder<'_>, |
865 | body_owner_def_id: DefId, | |
866 | found: Ty<'tcx>, | |
867 | ) -> bool { | |
3dfed10e XL |
868 | let hir_id = |
869 | match body_owner_def_id.as_local().map(|id| self.hir().local_def_id_to_hir_id(id)) { | |
870 | Some(hir_id) => hir_id, | |
871 | None => return false, | |
872 | }; | |
f9f354fc XL |
873 | // When `body_owner` is an `impl` or `trait` item, look in its associated types for |
874 | // `expected` and point at it. | |
875 | let parent_id = self.hir().get_parent_item(hir_id); | |
876 | let item = self.hir().find(parent_id); | |
877 | debug!("expected_projection parent item {:?}", item); | |
878 | match item { | |
879 | Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. })) => { | |
880 | // FIXME: account for `#![feature(specialization)]` | |
881 | for item in &items[..] { | |
882 | match item.kind { | |
f035d41b | 883 | hir::AssocItemKind::Type => { |
f9f354fc XL |
884 | // FIXME: account for returning some type in a trait fn impl that has |
885 | // an assoc type as a return type (#72076). | |
886 | if let hir::Defaultness::Default { has_value: true } = item.defaultness | |
887 | { | |
6a06907d | 888 | if self.type_of(item.id.def_id) == found { |
f9f354fc XL |
889 | db.span_label( |
890 | item.span, | |
891 | "associated type defaults can't be assumed inside the \ | |
892 | trait defining them", | |
893 | ); | |
894 | return true; | |
895 | } | |
896 | } | |
897 | } | |
898 | _ => {} | |
899 | } | |
900 | } | |
901 | } | |
902 | Some(hir::Node::Item(hir::Item { | |
5869c6ff XL |
903 | kind: hir::ItemKind::Impl(hir::Impl { items, .. }), |
904 | .. | |
f9f354fc XL |
905 | })) => { |
906 | for item in &items[..] { | |
1b1a35ee | 907 | if let hir::AssocItemKind::Type = item.kind { |
6a06907d | 908 | if self.type_of(item.id.def_id) == found { |
1b1a35ee XL |
909 | db.span_label(item.span, "expected this associated type"); |
910 | return true; | |
f9f354fc | 911 | } |
f9f354fc XL |
912 | } |
913 | } | |
914 | } | |
915 | _ => {} | |
916 | } | |
917 | false | |
918 | } | |
919 | ||
920 | /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref` | |
5869c6ff | 921 | /// requirement, provide a structured suggestion to constrain it to a given type `ty`. |
94222f64 XL |
922 | /// |
923 | /// `is_bound_surely_present` indicates whether we know the bound we're looking for is | |
924 | /// inside `bounds`. If that's the case then we can consider `bounds` containing only one | |
925 | /// trait bound as the one we're looking for. This can help in cases where the associated | |
926 | /// type is defined on a supertrait of the one present in the bounds. | |
f9f354fc | 927 | fn constrain_generic_bound_associated_type_structured_suggestion( |
1b1a35ee | 928 | self, |
f9f354fc XL |
929 | db: &mut DiagnosticBuilder<'_>, |
930 | trait_ref: &ty::TraitRef<'tcx>, | |
931 | bounds: hir::GenericBounds<'_>, | |
932 | assoc: &ty::AssocItem, | |
6a06907d | 933 | assoc_substs: &[ty::GenericArg<'tcx>], |
f9f354fc XL |
934 | ty: Ty<'tcx>, |
935 | msg: &str, | |
94222f64 | 936 | is_bound_surely_present: bool, |
f9f354fc XL |
937 | ) -> bool { |
938 | // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting. | |
94222f64 XL |
939 | |
940 | let trait_bounds = bounds.iter().filter_map(|bound| match bound { | |
941 | hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => Some(ptr), | |
942 | _ => None, | |
943 | }); | |
944 | ||
945 | let matching_trait_bounds = trait_bounds | |
946 | .clone() | |
947 | .filter(|ptr| ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id)) | |
948 | .collect::<Vec<_>>(); | |
949 | ||
950 | let span = match &matching_trait_bounds[..] { | |
951 | &[ptr] => ptr.span, | |
952 | &[] if is_bound_surely_present => match &trait_bounds.collect::<Vec<_>>()[..] { | |
953 | &[ptr] => ptr.span, | |
954 | _ => return false, | |
955 | }, | |
956 | _ => return false, | |
957 | }; | |
958 | ||
959 | self.constrain_associated_type_structured_suggestion(db, span, assoc, assoc_substs, ty, msg) | |
f9f354fc XL |
960 | } |
961 | ||
962 | /// Given a span corresponding to a bound, provide a structured suggestion to set an | |
963 | /// associated type to a given type `ty`. | |
964 | fn constrain_associated_type_structured_suggestion( | |
1b1a35ee | 965 | self, |
f9f354fc XL |
966 | db: &mut DiagnosticBuilder<'_>, |
967 | span: Span, | |
968 | assoc: &ty::AssocItem, | |
6a06907d | 969 | assoc_substs: &[ty::GenericArg<'tcx>], |
f9f354fc XL |
970 | ty: Ty<'tcx>, |
971 | msg: &str, | |
972 | ) -> bool { | |
973 | if let Ok(has_params) = | |
974 | self.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>')) | |
975 | { | |
976 | let (span, sugg) = if has_params { | |
977 | let pos = span.hi() - BytePos(1); | |
c295e0f8 | 978 | let span = Span::new(pos, pos, span.ctxt(), span.parent()); |
f9f354fc XL |
979 | (span, format!(", {} = {}", assoc.ident, ty)) |
980 | } else { | |
6a06907d XL |
981 | let item_args = self.format_generic_args(assoc_substs); |
982 | (span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident, item_args, ty)) | |
f9f354fc XL |
983 | }; |
984 | db.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect); | |
985 | return true; | |
986 | } | |
987 | false | |
988 | } | |
6a06907d XL |
989 | |
990 | fn format_generic_args(self, args: &[ty::GenericArg<'tcx>]) -> String { | |
991 | let mut item_args = String::new(); | |
992 | FmtPrinter::new(self, &mut item_args, hir::def::Namespace::TypeNS) | |
993 | .path_generic_args(Ok, args) | |
994 | .expect("could not write to `String`."); | |
995 | item_args | |
996 | } | |
e9174d1e | 997 | } |