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