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)

`