Add more info on type/trait mismatches for different crate versions · rust-lang/rust@16bf722 (original) (raw)
`@@ -52,7 +52,9 @@ use std::{cmp, fmt, iter};
`
52
52
``
53
53
`use rustc_abi::ExternAbi;
`
54
54
`use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
`
55
``
`-
use rustc_errors::{Applicability, Diag, DiagStyledString, IntoDiagArg, StringPart, pluralize};
`
``
55
`+
use rustc_errors::{
`
``
56
`+
Applicability, Diag, DiagStyledString, IntoDiagArg, MultiSpan, StringPart, pluralize,
`
``
57
`+
};
`
56
58
`use rustc_hir::def::DefKind;
`
57
59
`use rustc_hir::def_id::DefId;
`
58
60
`use rustc_hir::intravisit::Visitor;
`
`@@ -67,6 +69,7 @@ use rustc_middle::ty::{
`
67
69
`self, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
`
68
70
`TypeVisitableExt,
`
69
71
`};
`
``
72
`+
use rustc_span::def_id::LOCAL_CRATE;
`
70
73
`use rustc_span::{BytePos, DesugaringKind, Pos, Span, sym};
`
71
74
`use tracing::{debug, instrument};
`
72
75
``
`@@ -211,7 +214,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
`
211
214
`}
`
212
215
``
213
216
`/// Adds a note if the types come from similarly named crates
`
214
``
`-
fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) {
`
``
217
`+
fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) -> bool {
`
``
218
`` +
// FIXME(estebank): unify with report_similar_impl_candidates
. The message is similar,
``
``
219
`+
// even if the logic needed to detect the case is very different.
`
215
220
`use hir::def_id::CrateNum;
`
216
221
`use rustc_hir::definitions::DisambiguatedDefPathData;
`
217
222
`use ty::GenericArg;
`
`@@ -285,7 +290,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
`
285
290
`}
`
286
291
`}
`
287
292
``
288
``
`-
let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId| {
`
``
293
`+
let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId, ty: &str| -> bool {
`
289
294
`// Only report definitions from different crates. If both definitions
`
290
295
`// are from a local module we could have false positives, e.g.
`
291
296
`// let _ = [{struct Foo; Foo}, {struct Foo; Foo}];
`
`@@ -297,24 +302,112 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
`
297
302
``
298
303
`// We compare strings because DefPath can be different
`
299
304
`// for imported and non-imported crates
`
``
305
`+
let expected_str = self.tcx.def_path_str(did1);
`
``
306
`+
let found_str = self.tcx.def_path_str(did2);
`
``
307
`+
let Ok(expected_abs) = abs_path(did1) else { return false };
`
``
308
`+
let Ok(found_abs) = abs_path(did2) else { return false };
`
300
309
`let same_path = || -> Result<_, PrintError> {
`
301
``
`-
Ok(self.tcx.def_path_str(did1) == self.tcx.def_path_str(did2)
`
302
``
`-
|| abs_path(did1)? == abs_path(did2)?)
`
``
310
`+
Ok(expected_str == found_str || expected_abs == found_abs)
`
``
311
`+
};
`
``
312
`+
// We want to use as unique a type path as possible. If both types are "locally
`
``
313
`+
// known" by the same name, we use the "absolute path" which uses the original
`
``
314
`+
// crate name instead.
`
``
315
`+
let (expected, found) = if expected_str == found_str {
`
``
316
`+
(expected_abs.join("::"), found_abs.join("::"))
`
``
317
`+
} else {
`
``
318
`+
(expected_str.clone(), found_str.clone())
`
303
319
`};
`
304
320
`if same_path().unwrap_or(false) {
`
305
``
`-
let crate_name = self.tcx.crate_name(did1.krate);
`
306
``
`-
let msg = if did1.is_local() || did2.is_local() {
`
``
321
`` +
// We've displayed "expected a::b
, found a::b
". We add context to
``
``
322
`+
// differentiate the different cases where that might happen.
`
``
323
`+
let expected_crate_name = self.tcx.crate_name(did1.krate);
`
``
324
`+
let found_crate_name = self.tcx.crate_name(did2.krate);
`
``
325
`+
let same_crate = expected_crate_name == found_crate_name;
`
``
326
`+
let expected_sp = self.tcx.def_span(did1);
`
``
327
`+
let found_sp = self.tcx.def_span(did2);
`
``
328
+
``
329
`+
let both_direct_dependencies = if !did1.is_local()
`
``
330
`+
&& !did2.is_local()
`
``
331
`+
&& let Some(data1) = self.tcx.extern_crate(did1.krate)
`
``
332
`+
&& let Some(data2) = self.tcx.extern_crate(did2.krate)
`
``
333
`+
&& data1.dependency_of == LOCAL_CRATE
`
``
334
`+
&& data2.dependency_of == LOCAL_CRATE
`
``
335
`+
{
`
``
336
`+
// If both crates are directly depended on, we don't want to mention that
`
``
337
`+
// in the final message, as it is redundant wording.
`
``
338
`+
// We skip the case of semver trick, where one version of the local crate
`
``
339
`+
// depends on another version of itself by checking that both crates at play
`
``
340
`+
// are not the current one.
`
``
341
`+
true
`
``
342
`+
} else {
`
``
343
`+
false
`
``
344
`+
};
`
``
345
+
``
346
`+
let mut span: MultiSpan = vec![expected_sp, found_sp].into();
`
``
347
`+
span.push_span_label(
`
``
348
`+
self.tcx.def_span(did1),
`
``
349
`` +
format!("this is the expected {ty} {expected}
"),
``
``
350
`+
);
`
``
351
`+
span.push_span_label(
`
``
352
`+
self.tcx.def_span(did2),
`
``
353
`` +
format!("this is the found {ty} {found}
"),
``
``
354
`+
);
`
``
355
`+
for def_id in [did1, did2] {
`
``
356
`+
let crate_name = self.tcx.crate_name(def_id.krate);
`
``
357
`+
if !def_id.is_local()
`
``
358
`+
&& let Some(data) = self.tcx.extern_crate(def_id.krate)
`
``
359
`+
{
`
``
360
`+
let descr = if same_crate {
`
``
361
`+
"one version of".to_string()
`
``
362
`+
} else {
`
``
363
`+
format!("one {ty} comes from")
`
``
364
`+
};
`
``
365
`+
let dependency = if both_direct_dependencies {
`
``
366
`+
if let rustc_session::cstore::ExternCrateSource::Extern(def_id) =
`
``
367
`+
data.src
`
``
368
`+
&& let Some(name) = self.tcx.opt_item_name(def_id)
`
``
369
`+
{
`
``
370
`` +
format!(", which is renamed locally to {name}
")
``
``
371
`+
} else {
`
``
372
`+
String::new()
`
``
373
`+
}
`
``
374
`+
} else if data.dependency_of == LOCAL_CRATE {
`
``
375
`+
", as a direct dependency of the current crate".to_string()
`
``
376
`+
} else {
`
``
377
`+
let dep = self.tcx.crate_name(data.dependency_of);
`
``
378
`` +
format!(", as a dependency of crate {dep}
")
``
``
379
`+
};
`
``
380
`+
span.push_span_label(
`
``
381
`+
data.span,
`
``
382
`` +
format!("{descr} crate {crate_name}
used here{dependency}"),
``
``
383
`+
);
`
``
384
`+
}
`
``
385
`+
}
`
``
386
`+
let msg = if (did1.is_local() || did2.is_local()) && same_crate {
`
``
387
`+
format!(
`
``
388
`` +
"the crate {expected_crate_name}
is compiled multiple times, \
``
``
389
`+
possibly with different configurations",
`
``
390
`+
)
`
``
391
`+
} else if same_crate {
`
307
392
`format!(
`
308
``
`` -
"the crate {crate_name}
is compiled multiple times, possibly with different configurations"
``
``
393
`` +
"two different versions of crate {expected_crate_name}
are being \
``
``
394
`+
used; two types coming from two different versions of the same crate \
`
``
395
`+
are different types even if they look the same",
`
309
396
`)
`
310
397
`} else {
`
311
398
`format!(
`
312
``
`` -
"perhaps two different versions of crate {crate_name}
are being used?"
``
``
399
`+
"two types coming from two different crates are different types even \
`
``
400
`+
if they look the same",
`
313
401
`)
`
314
402
`};
`
315
``
`-
err.note(msg);
`
``
403
`+
err.span_note(span, msg);
`
``
404
`+
if same_crate {
`
``
405
`` +
err.help("you can use cargo tree
to explore your dependency tree");
``
``
406
`+
}
`
``
407
`+
return true;
`
316
408
`}
`
317
409
`}
`
``
410
`+
false
`
318
411
`};
`
319
412
`match terr {
`
320
413
`TypeError::Sorts(ref exp_found) => {
`
`@@ -323,14 +416,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
`
323
416
`if let (&ty::Adt(exp_adt, _), &ty::Adt(found_adt, _)) =
`
324
417
`(exp_found.expected.kind(), exp_found.found.kind())
`
325
418
`{
`
326
``
`-
report_path_match(err, exp_adt.did(), found_adt.did());
`
``
419
`+
return report_path_match(err, exp_adt.did(), found_adt.did(), "type");
`
327
420
`}
`
328
421
`}
`
329
422
`TypeError::Traits(ref exp_found) => {
`
330
``
`-
report_path_match(err, exp_found.expected, exp_found.found);
`
``
423
`+
return report_path_match(err, exp_found.expected, exp_found.found, "trait");
`
331
424
`}
`
332
425
` _ => (), // FIXME(#22750) handle traits and stuff
`
333
426
`}
`
``
427
`+
false
`
334
428
`}
`
335
429
``
336
430
`fn note_error_origin(
`
`@@ -1409,6 +1503,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
`
1409
1503
`label_or_note(span, terr.to_string(self.tcx));
`
1410
1504
`}
`
1411
1505
``
``
1506
`+
if self.check_and_note_conflicting_crates(diag, terr) {
`
``
1507
`+
return;
`
``
1508
`+
}
`
``
1509
+
1412
1510
`if let Some((expected, found, path)) = expected_found {
`
1413
1511
`let (expected_label, found_label, exp_found) = match exp_found {
`
1414
1512
`Mismatch::Variable(ef) => (
`
`@@ -1470,15 +1568,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
`
1470
1568
` |prim: Ty<'tcx>, shadow: Ty<'tcx>, defid: DefId, diag: &mut Diag<'_>| {
`
1471
1569
`let name = shadow.sort_string(self.tcx);
`
1472
1570
` diag.note(format!(
`
1473
``
`-
"{prim} and {name} have similar names, but are actually distinct types"
`
``
1571
`` +
"{prim}
and {name} have similar names, but are actually distinct types"
``
``
1572
`+
));
`
``
1573
`+
diag.note(format!(
`
``
1574
`` +
"one {prim}
is a primitive defined by the language",
``
1474
1575
`));
`
1475
``
`-
diag.note(format!("{prim} is a primitive defined by the language"));
`
1476
1576
`let def_span = self.tcx.def_span(defid);
`
1477
1577
`let msg = if defid.is_local() {
`
1478
``
`-
format!("{name} is defined in the current crate")
`
``
1578
`+
format!("the other {name} is defined in the current crate")
`
1479
1579
`} else {
`
1480
1580
`let crate_name = self.tcx.crate_name(defid.krate);
`
1481
``
`` -
format!("{name} is defined in crate {crate_name}
")
``
``
1581
`` +
format!("the other {name} is defined in crate {crate_name}
")
``
1482
1582
`};
`
1483
1583
` diag.span_note(def_span, msg);
`
1484
1584
`};
`
`@@ -1666,8 +1766,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
`
1666
1766
`}
`
1667
1767
`}
`
1668
1768
``
1669
``
`-
self.check_and_note_conflicting_crates(diag, terr);
`
1670
``
-
1671
1769
`self.note_and_explain_type_err(diag, terr, cause, span, cause.body_id.to_def_id());
`
1672
1770
`if let Some(exp_found) = exp_found
`
1673
1771
` && let exp_found = TypeError::Sorts(exp_found)
`