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_errors::pluralize;
8use rustc_hir as hir;
9use rustc_hir::def::{CtorOf, DefKind};
10use rustc_hir::limit::Limit;
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, Lift, Ty, TyCtxt};
16
17pub type TypeError<'tcx> = rustc_type_ir::error::TypeError<TyCtxt<'tcx>>;
18
19#[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(_) => "cyclic type of infinite size".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::ProjectionMismatched(ref values) => format!(
100 "expected `{}`, found `{}`",
101 tcx.def_path_str(values.expected),
102 tcx.def_path_str(values.found)
103 )
104 .into(),
105 TypeError::ExistentialMismatch(ref values) => report_maybe_different(
106 &format!("trait `{}`", values.expected),
107 &format!("trait `{}`", values.found),
108 )
109 .into(),
110 TypeError::ConstMismatch(ref values) => {
111 format!("expected `{}`, found `{}`", values.expected, values.found).into()
112 }
113 TypeError::ForceInlineCast => {
114 "cannot coerce functions which must be inlined to function pointers".into()
115 }
116 TypeError::IntrinsicCast => "cannot coerce intrinsics to function pointers".into(),
117 TypeError::TargetFeatureCast(_) => {
118 "cannot coerce functions with `#[target_feature]` to safe function pointers".into()
119 }
120 }
121 }
122}
123
124impl<'tcx> Ty<'tcx> {
125 pub fn sort_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> {
126 match *self.kind() {
127 ty::Foreign(def_id) => format!("extern type `{}`", tcx.def_path_str(def_id)).into(),
128 ty::FnDef(def_id, ..) => match tcx.def_kind(def_id) {
129 DefKind::Ctor(CtorOf::Struct, _) => "struct constructor".into(),
130 DefKind::Ctor(CtorOf::Variant, _) => "enum constructor".into(),
131 _ => "fn item".into(),
132 },
133 ty::FnPtr(..) => "fn pointer".into(),
134 ty::Dynamic(inner, ..) if let Some(principal) = inner.principal() => {
135 format!("`dyn {}`", tcx.def_path_str(principal.def_id())).into()
136 }
137 ty::Dynamic(..) => "trait object".into(),
138 ty::Closure(..) => "closure".into(),
139 ty::Coroutine(def_id, ..) => {
140 format!("{:#}", tcx.coroutine_kind(def_id).unwrap()).into()
141 }
142 ty::CoroutineWitness(..) => "coroutine witness".into(),
143 ty::Infer(ty::TyVar(_)) => "inferred type".into(),
144 ty::Infer(ty::IntVar(_)) => "integer".into(),
145 ty::Infer(ty::FloatVar(_)) => "floating-point number".into(),
146 ty::Placeholder(..) => "placeholder type".into(),
147 ty::Bound(..) => "bound type".into(),
148 ty::Infer(ty::FreshTy(_)) => "fresh type".into(),
149 ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".into(),
150 ty::Infer(ty::FreshFloatTy(_)) => "fresh floating-point type".into(),
151 ty::Alias(ty::Projection | ty::Inherent, _) => "associated type".into(),
152 ty::Param(p) => format!("type parameter `{p}`").into(),
153 ty::Alias(ty::Opaque, ..) => {
154 if tcx.ty_is_opaque_future(self) {
155 "future".into()
156 } else {
157 "opaque type".into()
158 }
159 }
160 ty::Error(_) => "type error".into(),
161 _ => {
162 let width = tcx.sess.diagnostic_width();
163 let length_limit = std::cmp::max(width / 4, 40);
164 format!(
165 "`{}`",
166 tcx.string_with_limit(self, length_limit, hir::def::Namespace::TypeNS)
167 )
168 .into()
169 }
170 }
171 }
172
173 pub fn prefix_string(self, tcx: TyCtxt<'_>) -> Cow<'static, str> {
174 match *self.kind() {
175 ty::Infer(_)
176 | ty::Error(_)
177 | ty::Bool
178 | ty::Char
179 | ty::Int(_)
180 | ty::Uint(_)
181 | ty::Float(_)
182 | ty::Str
183 | ty::Never => "type".into(),
184 ty::Tuple(tys) if tys.is_empty() => "unit type".into(),
185 ty::Adt(def, _) => def.descr().into(),
186 ty::Foreign(_) => "extern type".into(),
187 ty::Array(..) => "array".into(),
188 ty::Pat(..) => "pattern type".into(),
189 ty::Slice(_) => "slice".into(),
190 ty::RawPtr(_, _) => "raw pointer".into(),
191 ty::Ref(.., mutbl) => match mutbl {
192 hir::Mutability::Mut => "mutable reference",
193 _ => "reference",
194 }
195 .into(),
196 ty::FnDef(def_id, ..) => match tcx.def_kind(def_id) {
197 DefKind::Ctor(CtorOf::Struct, _) => "struct constructor".into(),
198 DefKind::Ctor(CtorOf::Variant, _) => "enum constructor".into(),
199 _ => "fn item".into(),
200 },
201 ty::FnPtr(..) => "fn pointer".into(),
202 ty::UnsafeBinder(_) => "unsafe binder".into(),
203 ty::Dynamic(..) => "trait object".into(),
204 ty::Closure(..) | ty::CoroutineClosure(..) => "closure".into(),
205 ty::Coroutine(def_id, ..) => {
206 format!("{:#}", tcx.coroutine_kind(def_id).unwrap()).into()
207 }
208 ty::CoroutineWitness(..) => "coroutine witness".into(),
209 ty::Tuple(..) => "tuple".into(),
210 ty::Placeholder(..) => "higher-ranked type".into(),
211 ty::Bound(..) => "bound type variable".into(),
212 ty::Alias(ty::Projection | ty::Inherent, _) => "associated type".into(),
213 ty::Alias(ty::Free, _) => "type alias".into(),
214 ty::Param(_) => "type parameter".into(),
215 ty::Alias(ty::Opaque, ..) => "opaque type".into(),
216 }
217 }
218}
219
220impl<'tcx> TyCtxt<'tcx> {
221 pub fn string_with_limit<T>(self, t: T, length_limit: usize, ns: hir::def::Namespace) -> String
222 where
223 T: Copy + for<'a, 'b> Lift<TyCtxt<'b>, Lifted: Print<'b, FmtPrinter<'a, 'b>>>,
224 {
225 let mut type_limit = 50;
226 let regular = FmtPrinter::print_string(self, ns, |p| {
227 self.lift(t).expect("could not lift for printing").print(p)
228 })
229 .expect("could not write to `String`");
230 if regular.len() <= length_limit {
231 return regular;
232 }
233 let mut short;
234 loop {
235 short = with_forced_trimmed_paths!({
237 let mut p = FmtPrinter::new_with_limit(self, ns, Limit(type_limit));
238 self.lift(t)
239 .expect("could not lift for printing")
240 .print(&mut p)
241 .expect("could not print type");
242 p.into_buffer()
243 });
244 if short.len() <= length_limit || type_limit == 0 {
245 break;
246 }
247 type_limit -= 1;
248 }
249 short
250 }
251
252 pub fn short_string<T>(self, t: T, path: &mut Option<PathBuf>) -> String
257 where
258 T: Copy + Hash + for<'a, 'b> Lift<TyCtxt<'b>, Lifted: Print<'b, FmtPrinter<'a, 'b>>>,
259 {
260 self.short_string_namespace(t, path, hir::def::Namespace::TypeNS)
261 }
262
263 pub fn short_string_namespace<T>(
268 self,
269 t: T,
270 path: &mut Option<PathBuf>,
271 namespace: hir::def::Namespace,
272 ) -> String
273 where
274 T: Copy + Hash + for<'a, 'b> Lift<TyCtxt<'b>, Lifted: Print<'b, FmtPrinter<'a, 'b>>>,
275 {
276 let regular = FmtPrinter::print_string(self, namespace, |p| {
277 self.lift(t).expect("could not lift for printing").print(p)
278 })
279 .expect("could not write to `String`");
280
281 if !self.sess.opts.unstable_opts.write_long_types_to_disk || self.sess.opts.verbose {
282 return regular;
283 }
284
285 let width = self.sess.diagnostic_width();
286 let length_limit = width / 2;
287 if regular.len() <= width * 2 / 3 {
288 return regular;
289 }
290 let short = self.string_with_limit(t, length_limit, namespace);
291 if regular == short {
292 return regular;
293 }
294 let mut s = DefaultHasher::new();
296 t.hash(&mut s);
297 let hash = s.finish();
298 *path = Some(path.take().unwrap_or_else(|| {
299 self.output_filenames(()).temp_path_for_diagnostic(&format!("long-type-{hash}.txt"))
300 }));
301 let Ok(mut file) =
302 File::options().create(true).read(true).append(true).open(&path.as_ref().unwrap())
303 else {
304 return regular;
305 };
306
307 let mut contents = String::new();
309 let _ = file.read_to_string(&mut contents);
310 if let Some(_) = contents.lines().find(|line| line == ®ular) {
311 return short;
312 }
313
314 match write!(file, "{regular}\n") {
315 Ok(_) => short,
316 Err(_) => regular,
317 }
318 }
319}