1use std::borrow::Cow;
2use std::fs::File;
3use std::hash::{DefaultHasher, Hash, Hasher};
4use std::io::{Read, Write};
5use std::path::PathBuf;
6
7use rustc_data_structures::Limit;
8use rustc_errors::pluralize;
9use rustc_hir as hir;
10use rustc_hir::def::{CtorOf, DefKind};
11use rustc_macros::extension;
12pub use rustc_type_ir::error::ExpectedFound;
13
14use crate::ty::print::{FmtPrinter, Print, with_forced_trimmed_paths};
15use crate::ty::{self, Ty, TyCtxt};
16
17pub type TypeError<'tcx> = rustc_type_ir::error::TypeError<TyCtxt<'tcx>>;
18
19impl<'tcx> TypeErrorToStringExt<'tcx> for TypeError<'tcx> {
fn to_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> {
fn report_maybe_different(expected: &str, found: &str) -> String {
if expected == found {
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected {0}, found a different {1}",
expected, found))
})
} else {
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected {0}, found {1}",
expected, found))
})
}
}
match self {
TypeError::CyclicTy(_) =>
"recursive type with infinite-size name".into(),
TypeError::CyclicConst(_) =>
"encountered a self-referencing constant".into(),
TypeError::Mismatch => "types differ".into(),
TypeError::PolarityMismatch(values) => {
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected {0} polarity, found {1} polarity",
values.expected, values.found))
}).into()
}
TypeError::SafetyMismatch(values) => {
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected {0} fn, found {1} fn",
values.expected, values.found))
}).into()
}
TypeError::AbiMismatch(values) => {
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected {0} fn, found {1} fn",
values.expected, values.found))
}).into()
}
TypeError::ArgumentMutability(_) | TypeError::Mutability => {
"types differ in mutability".into()
}
TypeError::TupleSize(values) =>
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected a tuple with {0} element{1}, found one with {2} element{3}",
values.expected,
if values.expected == 1 { "" } else { "s" }, values.found,
if values.found == 1 { "" } else { "s" }))
}).into(),
TypeError::ArraySize(values) =>
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected an array with a size of {0}, found one with a size of {1}",
values.expected, values.found))
}).into(),
TypeError::ArgCount =>
"incorrect number of function parameters".into(),
TypeError::RegionsDoesNotOutlive(..) =>
"lifetime mismatch".into(),
TypeError::RegionsInsufficientlyPolymorphic(..) => {
"one type is more general than the other".into()
}
TypeError::RegionsPlaceholderMismatch => {
"one type is more general than the other".into()
}
TypeError::ArgumentSorts(values, _) | TypeError::Sorts(values) =>
{
let expected = values.expected.sort_string(tcx);
let found = values.found.sort_string(tcx);
report_maybe_different(&expected, &found).into()
}
TypeError::Traits(values) => {
let (mut expected, mut found) =
{
let _guard = ForceTrimmedGuard::new();
(tcx.def_path_str(values.expected),
tcx.def_path_str(values.found))
};
if expected == found {
expected = tcx.def_path_str(values.expected);
found = tcx.def_path_str(values.found);
}
report_maybe_different(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("trait `{0}`", expected))
}),
&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("trait `{0}`", found))
})).into()
}
TypeError::VariadicMismatch(ref values) =>
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected {0} fn, found {1} function",
if values.expected { "variadic" } else { "non-variadic" },
if values.found { "variadic" } else { "non-variadic" }))
}).into(),
TypeError::SplatMismatch(ref values) =>
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected fn with {0}, found fn with {1}",
if let Some(index) = values.expected {
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("arg {0} splatted",
index))
})
} else { "no splatted arg".to_string() },
if let Some(index) = values.found {
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("arg {0} splatted",
index))
})
} else { "no splatted arg".to_string() }))
}).into(),
TypeError::ProjectionMismatched(ref values) =>
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected `{0}`, found `{1}`",
tcx.alias_term_kind_def_path_str(values.expected),
tcx.alias_term_kind_def_path_str(values.found)))
}).into(),
TypeError::ExistentialMismatch(ref values) =>
report_maybe_different(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("trait `{0}`",
values.expected))
}),
&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("trait `{0}`",
values.found))
})).into(),
TypeError::ConstMismatch(ref values) => {
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected `{0}`, found `{1}`",
values.expected, values.found))
}).into()
}
TypeError::ForceInlineCast => {
"cannot coerce functions which must be inlined to function pointers".into()
}
TypeError::IntrinsicCast =>
"cannot coerce intrinsics to function pointers".into(),
TypeError::TargetFeatureCast(_) => {
"cannot coerce functions with `#[target_feature(..)]` to safe function pointers".into()
}
}
}
}#[extension(pub trait TypeErrorToStringExt<'tcx>)]
25impl<'tcx> TypeError<'tcx> {
26 fn to_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> {
27 fn report_maybe_different(expected: &str, found: &str) -> String {
28 if expected == found {
31 format!("expected {expected}, found a different {found}")
32 } else {
33 format!("expected {expected}, found {found}")
34 }
35 }
36
37 match self {
38 TypeError::CyclicTy(_) => "recursive type with infinite-size name".into(),
39 TypeError::CyclicConst(_) => "encountered a self-referencing constant".into(),
40 TypeError::Mismatch => "types differ".into(),
41 TypeError::PolarityMismatch(values) => {
42 format!("expected {} polarity, found {} polarity", values.expected, values.found)
43 .into()
44 }
45 TypeError::SafetyMismatch(values) => {
46 format!("expected {} fn, found {} fn", values.expected, values.found).into()
47 }
48 TypeError::AbiMismatch(values) => {
49 format!("expected {} fn, found {} fn", values.expected, values.found).into()
50 }
51 TypeError::ArgumentMutability(_) | TypeError::Mutability => {
52 "types differ in mutability".into()
53 }
54 TypeError::TupleSize(values) => format!(
55 "expected a tuple with {} element{}, found one with {} element{}",
56 values.expected,
57 pluralize!(values.expected),
58 values.found,
59 pluralize!(values.found)
60 )
61 .into(),
62 TypeError::ArraySize(values) => format!(
63 "expected an array with a size of {}, found one with a size of {}",
64 values.expected, values.found,
65 )
66 .into(),
67 TypeError::ArgCount => "incorrect number of function parameters".into(),
68 TypeError::RegionsDoesNotOutlive(..) => "lifetime mismatch".into(),
69 TypeError::RegionsInsufficientlyPolymorphic(..) => {
71 "one type is more general than the other".into()
72 }
73 TypeError::RegionsPlaceholderMismatch => {
74 "one type is more general than the other".into()
75 }
76 TypeError::ArgumentSorts(values, _) | TypeError::Sorts(values) => {
77 let expected = values.expected.sort_string(tcx);
78 let found = values.found.sort_string(tcx);
79 report_maybe_different(&expected, &found).into()
80 }
81 TypeError::Traits(values) => {
82 let (mut expected, mut found) = with_forced_trimmed_paths!((
83 tcx.def_path_str(values.expected),
84 tcx.def_path_str(values.found),
85 ));
86 if expected == found {
87 expected = tcx.def_path_str(values.expected);
88 found = tcx.def_path_str(values.found);
89 }
90 report_maybe_different(&format!("trait `{expected}`"), &format!("trait `{found}`"))
91 .into()
92 }
93 TypeError::VariadicMismatch(ref values) => format!(
94 "expected {} fn, found {} function",
95 if values.expected { "variadic" } else { "non-variadic" },
96 if values.found { "variadic" } else { "non-variadic" }
97 )
98 .into(),
99 TypeError::SplatMismatch(ref values) => format!(
100 "expected fn with {}, found fn with {}",
101 if let Some(index) = values.expected {
102 format!("arg {index} splatted")
103 } else {
104 "no splatted arg".to_string()
105 },
106 if let Some(index) = values.found {
107 format!("arg {index} splatted")
108 } else {
109 "no splatted arg".to_string()
110 }
111 )
112 .into(),
113 TypeError::ProjectionMismatched(ref values) => format!(
114 "expected `{}`, found `{}`",
115 tcx.alias_term_kind_def_path_str(values.expected),
116 tcx.alias_term_kind_def_path_str(values.found)
117 )
118 .into(),
119 TypeError::ExistentialMismatch(ref values) => report_maybe_different(
120 &format!("trait `{}`", values.expected),
121 &format!("trait `{}`", values.found),
122 )
123 .into(),
124 TypeError::ConstMismatch(ref values) => {
125 format!("expected `{}`, found `{}`", values.expected, values.found).into()
126 }
127 TypeError::ForceInlineCast => {
128 "cannot coerce functions which must be inlined to function pointers".into()
129 }
130 TypeError::IntrinsicCast => "cannot coerce intrinsics to function pointers".into(),
131 TypeError::TargetFeatureCast(_) => {
132 "cannot coerce functions with `#[target_feature(..)]` to safe function pointers"
133 .into()
134 }
135 }
136 }
137}
138
139impl<'tcx> Ty<'tcx> {
140 pub fn sort_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> {
141 match *self.kind() {
142 ty::Foreign(def_id) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("extern type `{0}`",
tcx.def_path_str(def_id)))
})format!("extern type `{}`", tcx.def_path_str(def_id)).into(),
143 ty::FnDef(def_id, ..) => match tcx.def_kind(def_id) {
144 DefKind::Ctor(CtorOf::Struct, _) => "struct constructor".into(),
145 DefKind::Ctor(CtorOf::Variant, _) => "enum constructor".into(),
146 _ => "fn item".into(),
147 },
148 ty::FnPtr(..) => "fn pointer".into(),
149 ty::Dynamic(inner, ..) if let Some(principal) = inner.principal() => {
150 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`dyn {0}`",
tcx.def_path_str(principal.def_id())))
})format!("`dyn {}`", tcx.def_path_str(principal.def_id())).into()
151 }
152 ty::Dynamic(..) => "trait object".into(),
153 ty::Closure(..) => "closure".into(),
154 ty::Coroutine(def_id, ..) => {
155 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:#}",
tcx.coroutine_kind(def_id).unwrap()))
})format!("{:#}", tcx.coroutine_kind(def_id).unwrap()).into()
156 }
157 ty::CoroutineWitness(..) => "coroutine witness".into(),
158 ty::Infer(ty::TyVar(_)) => "inferred type".into(),
159 ty::Infer(ty::IntVar(_)) => "integer".into(),
160 ty::Infer(ty::FloatVar(_)) => "floating-point number".into(),
161 ty::Placeholder(..) => "placeholder type".into(),
162 ty::Bound(..) => "bound type".into(),
163 ty::Infer(ty::FreshTy(_)) => "fresh type".into(),
164 ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".into(),
165 ty::Infer(ty::FreshFloatTy(_)) => "fresh floating-point type".into(),
166 ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. } | ty::Inherent { .. }, .. }) => {
167 "associated type".into()
168 }
169 ty::Param(p) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("type parameter `{0}`", p))
})format!("type parameter `{p}`").into(),
170 ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => {
171 if tcx.ty_is_opaque_future(self) { "future".into() } else { "opaque type".into() }
172 }
173 ty::Error(_) => "type error".into(),
174 _ => {
175 let width = tcx.sess.diagnostic_width();
176 let length_limit = std::cmp::max(width / 4, 40);
177 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}`",
tcx.string_with_limit(self, length_limit,
hir::def::Namespace::TypeNS)))
})format!(
178 "`{}`",
179 tcx.string_with_limit(self, length_limit, hir::def::Namespace::TypeNS)
180 )
181 .into()
182 }
183 }
184 }
185
186 pub fn prefix_string(self, tcx: TyCtxt<'_>) -> Cow<'static, str> {
187 match *self.kind() {
188 ty::Infer(_)
189 | ty::Error(_)
190 | ty::Bool
191 | ty::Char
192 | ty::Int(_)
193 | ty::Uint(_)
194 | ty::Float(_)
195 | ty::Str
196 | ty::Never => "type".into(),
197 ty::Tuple(tys) if tys.is_empty() => "unit type".into(),
198 ty::Adt(def, _) => def.descr().into(),
199 ty::Foreign(_) => "extern type".into(),
200 ty::Array(..) => "array".into(),
201 ty::Pat(..) => "pattern type".into(),
202 ty::Slice(_) => "slice".into(),
203 ty::RawPtr(_, _) => "raw pointer".into(),
204 ty::Ref(.., mutbl) => match mutbl {
205 hir::Mutability::Mut => "mutable reference",
206 _ => "reference",
207 }
208 .into(),
209 ty::FnDef(def_id, ..) => match tcx.def_kind(def_id) {
210 DefKind::Ctor(CtorOf::Struct, _) => "struct constructor".into(),
211 DefKind::Ctor(CtorOf::Variant, _) => "enum constructor".into(),
212 _ => "fn item".into(),
213 },
214 ty::FnPtr(..) => "fn pointer".into(),
215 ty::UnsafeBinder(_) => "unsafe binder".into(),
216 ty::Dynamic(..) => "trait object".into(),
217 ty::Closure(..) | ty::CoroutineClosure(..) => "closure".into(),
218 ty::Coroutine(def_id, ..) => {
219 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:#}",
tcx.coroutine_kind(def_id).unwrap()))
})format!("{:#}", tcx.coroutine_kind(def_id).unwrap()).into()
220 }
221 ty::CoroutineWitness(..) => "coroutine witness".into(),
222 ty::Tuple(..) => "tuple".into(),
223 ty::Placeholder(..) => "higher-ranked type".into(),
224 ty::Bound(..) => "bound type variable".into(),
225 ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. } | ty::Inherent { .. }, .. }) => {
226 "associated type".into()
227 }
228 ty::Alias(_, ty::AliasTy { kind: ty::Free { .. }, .. }) => "type alias".into(),
229 ty::Param(_) => "type parameter".into(),
230 ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => "opaque type".into(),
231 }
232 }
233}
234
235impl<'tcx> TyCtxt<'tcx> {
236 pub fn string_with_limit<T>(self, t: T, length_limit: usize, ns: hir::def::Namespace) -> String
237 where
238 T: Copy + for<'a> Print<FmtPrinter<'a, 'tcx>>,
239 {
240 let mut type_limit = 50;
241 let regular = FmtPrinter::print_string(self, ns, |p| t.print(p))
242 .expect("could not write to `String`");
243 if regular.len() <= length_limit {
244 return regular;
245 }
246 let mut short;
247 loop {
248 short = {
let _guard = ForceTrimmedGuard::new();
{
let mut p = FmtPrinter::new_with_limit(self, ns, Limit(type_limit));
t.print(&mut p).expect("could not print type");
p.into_buffer()
}
}with_forced_trimmed_paths!({
250 let mut p = FmtPrinter::new_with_limit(self, ns, Limit(type_limit));
251 t.print(&mut p).expect("could not print type");
252 p.into_buffer()
253 });
254 if short.len() <= length_limit || type_limit == 0 {
255 break;
256 }
257 type_limit -= 1;
258 }
259 short
260 }
261
262 pub fn short_string<T>(self, t: T, path: &mut Option<PathBuf>) -> String
267 where
268 T: Copy + Hash + for<'a> Print<FmtPrinter<'a, 'tcx>>,
269 {
270 self.short_string_namespace(t, path, hir::def::Namespace::TypeNS)
271 }
272
273 pub fn short_string_namespace<T>(
278 self,
279 t: T,
280 path: &mut Option<PathBuf>,
281 namespace: hir::def::Namespace,
282 ) -> String
283 where
284 T: Copy + Hash + for<'a> Print<FmtPrinter<'a, 'tcx>>,
285 {
286 let regular = FmtPrinter::print_string(self, namespace, |p| t.print(p))
287 .expect("could not write to `String`");
288
289 if !self.sess.opts.unstable_opts.write_long_types_to_disk || self.sess.opts.verbose {
290 return regular;
291 }
292
293 let width = self.sess.diagnostic_width();
294 let length_limit = width / 2;
295 if regular.len() <= width * 2 / 3 {
296 return regular;
297 }
298 let short = self.string_with_limit(t, length_limit, namespace);
299 if regular == short {
300 return regular;
301 }
302 let mut s = DefaultHasher::new();
304 t.hash(&mut s);
305 let hash = s.finish();
306 *path = Some(path.take().unwrap_or_else(|| {
307 self.output_filenames(()).temp_path_for_diagnostic(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("long-type-{0}.txt", hash))
})format!("long-type-{hash}.txt"))
308 }));
309 let Ok(mut file) =
310 File::options().create(true).read(true).append(true).open(&path.as_ref().unwrap())
311 else {
312 return regular;
313 };
314
315 let mut contents = String::new();
317 let _ = file.read_to_string(&mut contents);
318 if let Some(_) = contents.lines().find(|line| line == ®ular) {
319 return short;
320 }
321
322 match file.write_fmt(format_args!("{0}\n", regular))write!(file, "{regular}\n") {
323 Ok(_) => short,
324 Err(_) => regular,
325 }
326 }
327
328 pub fn alias_term_kind_def_path_str(self, alias: ty::AliasTermKind<'tcx>) -> String {
329 match alias {
330 ty::AliasTermKind::ProjectionTy { def_id }
331 | ty::AliasTermKind::InherentTy { def_id }
332 | ty::AliasTermKind::OpaqueTy { def_id }
333 | ty::AliasTermKind::FreeTy { def_id }
334 | ty::AliasTermKind::AnonConst { def_id }
335 | ty::AliasTermKind::ProjectionConst { def_id }
336 | ty::AliasTermKind::FreeConst { def_id }
337 | ty::AliasTermKind::InherentConst { def_id } => self.def_path_str(def_id),
338 }
339 }
340}