Skip to content

Commit 13cdaae

Browse files
committed
Clarify private field autoderef notes
1 parent ce46df2 commit 13cdaae

3 files changed

Lines changed: 275 additions & 82 deletions

File tree

compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs

Lines changed: 96 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ use rustc_hir::{
2222
expr_needs_parens,
2323
};
2424
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk};
25+
use rustc_infer::traits::ImplSource;
2526
use rustc_middle::middle::privacy::Level;
2627
use rustc_middle::traits::IsConstable;
28+
use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind};
2729
use rustc_middle::ty::error::TypeError;
2830
use rustc_middle::ty::print::{
2931
PrintPolyTraitPredicateExt as _, PrintPolyTraitRefExt, PrintTraitPredicateExt as _,
@@ -49,7 +51,7 @@ use crate::error_reporting::TypeErrCtxt;
4951
use crate::errors;
5052
use crate::infer::InferCtxtExt as _;
5153
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
52-
use crate::traits::{ImplDerivedCause, NormalizeExt, ObligationCtxt};
54+
use crate::traits::{ImplDerivedCause, NormalizeExt, ObligationCtxt, SelectionContext};
5355

5456
#[derive(Debug)]
5557
pub enum CoroutineInteriorOrUpvar {
@@ -326,7 +328,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
326328
}
327329

328330
let fn_body_hir_id = self.tcx.local_def_id_to_hir_id(typeck_results.hir_owner.def_id);
329-
let mut private_candidate = None;
331+
let mut private_candidate: Option<(Ty<'tcx>, Ty<'tcx>, Span)> = None;
330332

331333
for (deref_base_ty, _) in (self.autoderef_steps)(base_ty) {
332334
let ty::Adt(base_def, args) = deref_base_ty.kind() else {
@@ -347,20 +349,107 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
347349
else {
348350
continue;
349351
};
352+
let field_span = self
353+
.tcx
354+
.def_ident_span(field_def.did)
355+
.unwrap_or_else(|| self.tcx.def_span(field_def.did));
350356

351357
if field_def.vis.is_accessible_from(def_scope, self.tcx) {
352358
let accessible_field_ty = field_def.ty(self.tcx, args);
353-
if let Some((private_base_ty, private_field_ty)) = private_candidate
359+
if let Some((private_base_ty, private_field_ty, private_field_span)) =
360+
private_candidate
354361
&& !self.can_eq(param_env, private_field_ty, accessible_field_ty)
355362
{
356-
err.note(format!(
357-
"there is a field `{field_ident}` on `{private_base_ty}` with type `{private_field_ty}`, but it is private"
358-
));
363+
let private_struct_span = match private_base_ty.kind() {
364+
ty::Adt(private_base_def, _) => self
365+
.tcx
366+
.def_ident_span(private_base_def.did())
367+
.unwrap_or_else(|| self.tcx.def_span(private_base_def.did())),
368+
_ => DUMMY_SP,
369+
};
370+
let accessible_struct_span = self
371+
.tcx
372+
.def_ident_span(base_def.did())
373+
.unwrap_or_else(|| self.tcx.def_span(base_def.did()));
374+
let deref_impl_span = (typeck_results
375+
.expr_adjustments(base_expr)
376+
.iter()
377+
.filter(|adj| {
378+
matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_)))
379+
})
380+
.count()
381+
== 1)
382+
.then(|| {
383+
self.probe(|_| {
384+
let deref_trait_did =
385+
self.tcx.require_lang_item(LangItem::Deref, DUMMY_SP);
386+
let trait_ref =
387+
ty::TraitRef::new(self.tcx, deref_trait_did, [private_base_ty]);
388+
let obligation: Obligation<'tcx, ty::Predicate<'tcx>> =
389+
Obligation::new(
390+
self.tcx,
391+
ObligationCause::dummy(),
392+
param_env,
393+
trait_ref,
394+
);
395+
let Ok(Some(ImplSource::UserDefined(impl_data))) =
396+
SelectionContext::new(self)
397+
.select(&obligation.with(self.tcx, trait_ref))
398+
else {
399+
return None;
400+
};
401+
Some(self.tcx.def_span(impl_data.impl_def_id))
402+
})
403+
})
404+
.flatten();
405+
406+
let mut note_spans: MultiSpan = private_struct_span.into();
407+
if private_struct_span != DUMMY_SP {
408+
note_spans.push_span_label(private_struct_span, "in this struct");
409+
}
410+
if private_field_span != DUMMY_SP {
411+
note_spans.push_span_label(
412+
private_field_span,
413+
"if this field wasn't private, it would be accessible",
414+
);
415+
}
416+
if accessible_struct_span != DUMMY_SP {
417+
note_spans.push_span_label(
418+
accessible_struct_span,
419+
"this struct is accessible through auto-deref",
420+
);
421+
}
422+
if field_span != DUMMY_SP {
423+
note_spans
424+
.push_span_label(field_span, "this is the field that was accessed");
425+
}
426+
if let Some(deref_impl_span) = deref_impl_span
427+
&& deref_impl_span != DUMMY_SP
428+
{
429+
note_spans.push_span_label(
430+
deref_impl_span,
431+
"the field was accessed through this `Deref`",
432+
);
433+
}
434+
435+
err.span_note(
436+
note_spans,
437+
format!(
438+
"there is a field `{field_ident}` on `{private_base_ty}` with type `{private_field_ty}` but it is private; `{field_ident}` from `{deref_base_ty}` was accessed through auto-deref instead"
439+
),
440+
);
359441
}
442+
443+
// we finally get to the accessible field,
444+
// so we can return early without checking the rest of the autoderef candidates
360445
return;
361446
}
362447

363-
private_candidate.get_or_insert((deref_base_ty, field_def.ty(self.tcx, args)));
448+
private_candidate.get_or_insert((
449+
deref_base_ty,
450+
field_def.ty(self.tcx, args),
451+
field_span,
452+
));
364453
}
365454
}
366455

Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Field lookup still resolves to the public field on the Deref target, but
22
// follow-up diagnostics should explain that the original type has a same-named
33
// private field with a different type.
4+
//@ dont-require-annotations: ERROR
45

56
mod structs {
67
pub struct A {
@@ -24,114 +25,64 @@ mod structs {
2425
use structs::A;
2526

2627
fn takes_usize(_: usize) {}
27-
//~^ NOTE function defined here
2828

2929
trait Marker {}
3030

3131
impl Marker for usize {}
32-
//~^ HELP the trait `Marker` is implemented for `usize`
3332

3433
struct Wrapper(i32);
3534

3635
impl<T: Marker> std::ops::Add<T> for Wrapper {
37-
//~^ NOTE required for `Wrapper` to implement `Add<bool>`
38-
//~| NOTE unsatisfied trait bound introduced here
3936
type Output = ();
4037

4138
fn add(self, _: T) {}
4239
}
4340

4441
fn by_value(a: A) {
4542
a.field + 5;
46-
//~^ ERROR cannot add `{integer}` to `bool`
47-
//~| NOTE bool
48-
//~| NOTE {integer}
49-
//~| NOTE there is a field `field` on `A` with type `usize`, but it is private
5043
}
5144

5245
fn by_ref(a: &A) {
5346
a.field + 5;
54-
//~^ ERROR cannot add `{integer}` to `bool`
55-
//~| NOTE bool
56-
//~| NOTE {integer}
57-
//~| NOTE there is a field `field` on `A` with type `usize`, but it is private
5847
}
5948

6049
fn rhs_by_value(a: A) {
6150
5 + a.field;
62-
//~^ ERROR cannot add `bool` to `{integer}`
63-
//~| NOTE no implementation for `{integer} + bool`
64-
//~| HELP the trait `Add<bool>` is not implemented for `{integer}`
65-
//~| HELP the following other types implement trait `Add<Rhs>`:
66-
//~| NOTE there is a field `field` on `A` with type `usize`, but it is private
6751
}
6852

6953
fn rhs_by_ref(a: &A) {
7054
5 + a.field;
71-
//~^ ERROR cannot add `bool` to `{integer}`
72-
//~| NOTE no implementation for `{integer} + bool`
73-
//~| HELP the trait `Add<bool>` is not implemented for `{integer}`
74-
//~| HELP the following other types implement trait `Add<Rhs>`:
75-
//~| NOTE there is a field `field` on `A` with type `usize`, but it is private
7655
}
7756

7857
fn rhs_assign_op_by_value(a: A) {
7958
let mut n = 5;
8059
n += a.field;
81-
//~^ ERROR cannot add-assign `bool` to `{integer}`
82-
//~| NOTE no implementation for `{integer} += bool`
83-
//~| HELP the trait `AddAssign<bool>` is not implemented for `{integer}`
84-
//~| HELP the following other types implement trait `AddAssign<Rhs>`:
85-
//~| NOTE there is a field `field` on `A` with type `usize`, but it is private
8660
}
8761

8862
fn rhs_assign_op_by_ref(a: &A) {
8963
let mut n = 5;
9064
n += a.field;
91-
//~^ ERROR cannot add-assign `bool` to `{integer}`
92-
//~| NOTE no implementation for `{integer} += bool`
93-
//~| HELP the trait `AddAssign<bool>` is not implemented for `{integer}`
94-
//~| HELP the following other types implement trait `AddAssign<Rhs>`:
95-
//~| NOTE there is a field `field` on `A` with type `usize`, but it is private
9665
}
9766

9867
fn rhs_nested_obligation(a: A) {
9968
Wrapper(5) + a.field;
100-
//~^ ERROR the trait bound `bool: Marker` is not satisfied
101-
//~| NOTE the trait `Marker` is not implemented for `bool`
102-
//~| NOTE there is a field `field` on `A` with type `usize`, but it is private
10369
}
10470

10571
fn method_call(a: A) {
10672
a.field.count_ones();
107-
//~^ ERROR no method named `count_ones` found for type `bool` in the current scope
108-
//~| NOTE method not found in `bool`
109-
//~| NOTE there is a field `field` on `A` with type `usize`, but it is private
11073
}
11174

11275
fn type_mismatch(a: A) {
11376
let value: usize = a.field;
114-
//~^ ERROR mismatched types
115-
//~| NOTE expected `usize`, found `bool`
116-
//~| NOTE expected due to this
117-
//~| NOTE there is a field `field` on `A` with type `usize`, but it is private
11877
eprintln!("value: {value}");
11978
}
12079

12180
fn function_arg(a: A) {
12281
takes_usize(a.field);
123-
//~^ ERROR mismatched types
124-
//~| NOTE expected `usize`, found `bool`
125-
//~| NOTE arguments to this function are incorrect
126-
//~| NOTE there is a field `field` on `A` with type `usize`, but it is private
12782
}
12883

12984
fn return_value(a: &A) -> usize {
130-
//~^ NOTE expected `usize` because of return type
13185
a.field
132-
//~^ ERROR mismatched types
133-
//~| NOTE expected `usize`, found `bool`
134-
//~| NOTE there is a field `field` on `A` with type `usize`, but it is private
13586
}
13687

13788
fn main() {}

0 commit comments

Comments
 (0)