Rollup merge of #100098 - compiler-errors:field-suggestion-fixups, r=davidtwco
Some "this expression has a field"-related fixes Each commit does something different and is worth reviewing, but the final diff from `master..HEAD` contains the sum of the changes to the UI tests, since some commits added UI tests "regressions" which were later removed in other commits. The only change I could see adding on top of this is suppressing `Clone::clone` from the "this expression has a field that has this method" suggestion, since it's so commonly implemented by types that it's not worthwhile suggesting in general.
This commit is contained in:
commit
867453e2df
8 changed files with 117 additions and 41 deletions
|
|
@ -2526,15 +2526,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
);
|
||||
|
||||
// try to add a suggestion in case the field is a nested field of a field of the Adt
|
||||
if let Some((fields, substs)) = self.get_field_candidates(span, expr_t) {
|
||||
for candidate_field in fields.iter() {
|
||||
let mod_id = self.tcx.parent_module(id).to_def_id();
|
||||
if let Some((fields, substs)) =
|
||||
self.get_field_candidates_considering_privacy(span, expr_t, mod_id)
|
||||
{
|
||||
for candidate_field in fields {
|
||||
if let Some(mut field_path) = self.check_for_nested_field_satisfying(
|
||||
span,
|
||||
&|candidate_field, _| candidate_field.ident(self.tcx()) == field,
|
||||
candidate_field,
|
||||
substs,
|
||||
vec![],
|
||||
self.tcx.parent_module(id).to_def_id(),
|
||||
mod_id,
|
||||
) {
|
||||
// field_path includes `field` that we're looking for, so pop it.
|
||||
field_path.pop();
|
||||
|
|
@ -2558,22 +2561,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
err
|
||||
}
|
||||
|
||||
pub(crate) fn get_field_candidates(
|
||||
pub(crate) fn get_field_candidates_considering_privacy(
|
||||
&self,
|
||||
span: Span,
|
||||
base_t: Ty<'tcx>,
|
||||
) -> Option<(&[ty::FieldDef], SubstsRef<'tcx>)> {
|
||||
debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_t);
|
||||
base_ty: Ty<'tcx>,
|
||||
mod_id: DefId,
|
||||
) -> Option<(impl Iterator<Item = &'tcx ty::FieldDef> + 'tcx, SubstsRef<'tcx>)> {
|
||||
debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_ty);
|
||||
|
||||
for (base_t, _) in self.autoderef(span, base_t) {
|
||||
for (base_t, _) in self.autoderef(span, base_ty) {
|
||||
match base_t.kind() {
|
||||
ty::Adt(base_def, substs) if !base_def.is_enum() => {
|
||||
let tcx = self.tcx;
|
||||
let fields = &base_def.non_enum_variant().fields;
|
||||
// For compile-time reasons put a limit on number of fields we search
|
||||
if fields.len() > 100 {
|
||||
return None;
|
||||
// Some struct, e.g. some that impl `Deref`, have all private fields
|
||||
// because you're expected to deref them to access the _real_ fields.
|
||||
// This, for example, will help us suggest accessing a field through a `Box<T>`.
|
||||
if fields.iter().all(|field| !field.vis.is_accessible_from(mod_id, tcx)) {
|
||||
continue;
|
||||
}
|
||||
return Some((fields, substs));
|
||||
return Some((
|
||||
fields
|
||||
.iter()
|
||||
.filter(move |field| field.vis.is_accessible_from(mod_id, tcx))
|
||||
// For compile-time reasons put a limit on number of fields we search
|
||||
.take(100),
|
||||
substs,
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -2590,7 +2604,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
candidate_field: &ty::FieldDef,
|
||||
subst: SubstsRef<'tcx>,
|
||||
mut field_path: Vec<Ident>,
|
||||
id: DefId,
|
||||
mod_id: DefId,
|
||||
) -> Option<Vec<Ident>> {
|
||||
debug!(
|
||||
"check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}",
|
||||
|
|
@ -2602,24 +2616,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// up to a depth of three
|
||||
None
|
||||
} else {
|
||||
// recursively search fields of `candidate_field` if it's a ty::Adt
|
||||
field_path.push(candidate_field.ident(self.tcx).normalize_to_macros_2_0());
|
||||
let field_ty = candidate_field.ty(self.tcx, subst);
|
||||
if let Some((nested_fields, subst)) = self.get_field_candidates(span, field_ty) {
|
||||
for field in nested_fields.iter() {
|
||||
if field.vis.is_accessible_from(id, self.tcx) {
|
||||
if matches(candidate_field, field_ty) {
|
||||
return Some(field_path);
|
||||
} else if let Some(field_path) = self.check_for_nested_field_satisfying(
|
||||
span,
|
||||
matches,
|
||||
field,
|
||||
subst,
|
||||
field_path.clone(),
|
||||
id,
|
||||
) {
|
||||
return Some(field_path);
|
||||
}
|
||||
if matches(candidate_field, field_ty) {
|
||||
return Some(field_path);
|
||||
} else if let Some((nested_fields, subst)) =
|
||||
self.get_field_candidates_considering_privacy(span, field_ty, mod_id)
|
||||
{
|
||||
// recursively search fields of `candidate_field` if it's a ty::Adt
|
||||
for field in nested_fields {
|
||||
if let Some(field_path) = self.check_for_nested_field_satisfying(
|
||||
span,
|
||||
matches,
|
||||
field,
|
||||
subst,
|
||||
field_path.clone(),
|
||||
mod_id,
|
||||
) {
|
||||
return Some(field_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1000,7 +1000,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
label_span_not_found(&mut err);
|
||||
}
|
||||
|
||||
self.check_for_field_method(&mut err, source, span, actual, item_name);
|
||||
// Don't suggest (for example) `expr.field.method()` if `expr.method()`
|
||||
// doesn't exist due to unsatisfied predicates.
|
||||
if unsatisfied_predicates.is_empty() {
|
||||
self.check_for_field_method(&mut err, source, span, actual, item_name);
|
||||
}
|
||||
|
||||
self.check_for_unwrap_self(&mut err, source, span, actual, item_name);
|
||||
|
||||
|
|
@ -1334,10 +1338,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
item_name: Ident,
|
||||
) {
|
||||
if let SelfSource::MethodCall(expr) = source
|
||||
&& let Some((fields, substs)) = self.get_field_candidates(span, actual)
|
||||
&& let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id()
|
||||
&& let Some((fields, substs)) = self.get_field_candidates_considering_privacy(span, actual, mod_id)
|
||||
{
|
||||
let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
|
||||
for candidate_field in fields.iter() {
|
||||
for candidate_field in fields {
|
||||
if let Some(field_path) = self.check_for_nested_field_satisfying(
|
||||
span,
|
||||
&|_, field_ty| {
|
||||
|
|
@ -1353,7 +1358,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
candidate_field,
|
||||
substs,
|
||||
vec![],
|
||||
self.tcx.parent_module(expr.hir_id).to_def_id(),
|
||||
mod_id,
|
||||
) {
|
||||
let field_path_str = field_path
|
||||
.iter()
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ LL | let _y = x.clone();
|
|||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `clone`, perhaps you need to implement it:
|
||||
candidate #1: `Clone`
|
||||
help: one of the expressions' fields has a method of the same name
|
||||
|
|
||||
LL | let _y = x.i.clone();
|
||||
| ++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
|||
|
|
@ -18,10 +18,6 @@ note: the following trait bounds were not satisfied:
|
|||
|
|
||||
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
|
||||
| --------- - ^^^^^^ unsatisfied trait bound introduced here
|
||||
help: one of the expressions' fields has a method of the same name
|
||||
|
|
||||
LL | let filter = map.stream.filterx(|x: &_| true);
|
||||
| +++++++
|
||||
|
||||
error[E0599]: the method `countx` exists for struct `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:129:30: 129:37]>`, but its trait bounds were not satisfied
|
||||
--> $DIR/issue-30786.rs:130:24
|
||||
|
|
@ -43,10 +39,6 @@ note: the following trait bounds were not satisfied:
|
|||
|
|
||||
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
|
||||
| --------- - ^^^^^^ unsatisfied trait bound introduced here
|
||||
help: one of the expressions' fields has a method of the same name
|
||||
|
|
||||
LL | let count = filter.stream.countx();
|
||||
| +++++++
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ LL | let _d = c.clone();
|
|||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `clone`, perhaps you need to implement it:
|
||||
candidate #1: `Clone`
|
||||
help: one of the expressions' fields has a method of the same name
|
||||
|
|
||||
LL | let _d = c.x.clone();
|
||||
| ++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,14 @@ LL | let _y = x.clone();
|
|||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `clone`, perhaps you need to implement it:
|
||||
candidate #1: `Clone`
|
||||
help: one of the expressions' fields has a method of the same name
|
||||
|
|
||||
LL | let _y = x.i.clone();
|
||||
| ++
|
||||
help: one of the expressions' fields has a method of the same name
|
||||
|
|
||||
LL | let _y = x.j.x.clone();
|
||||
| ++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
|||
35
src/test/ui/suggestions/field-access-considering-privacy.rs
Normal file
35
src/test/ui/suggestions/field-access-considering-privacy.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
use a::TyCtxt;
|
||||
|
||||
mod a {
|
||||
use std::ops::Deref;
|
||||
pub struct TyCtxt<'tcx> {
|
||||
gcx: &'tcx GlobalCtxt<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> Deref for TyCtxt<'tcx> {
|
||||
type Target = &'tcx GlobalCtxt<'tcx>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.gcx
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GlobalCtxt<'tcx> {
|
||||
pub sess: &'tcx Session,
|
||||
_t: &'tcx (),
|
||||
}
|
||||
|
||||
pub struct Session {
|
||||
pub opts: (),
|
||||
}
|
||||
}
|
||||
|
||||
mod b {
|
||||
fn foo<'tcx>(tcx: crate::TyCtxt<'tcx>) {
|
||||
tcx.opts;
|
||||
//~^ ERROR no field `opts` on type `TyCtxt<'tcx>`
|
||||
//~| HELP one of the expressions' fields has a field of the same name
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
error[E0609]: no field `opts` on type `TyCtxt<'tcx>`
|
||||
--> $DIR/field-access-considering-privacy.rs:29:13
|
||||
|
|
||||
LL | tcx.opts;
|
||||
| ^^^^ unknown field
|
||||
|
|
||||
help: one of the expressions' fields has a field of the same name
|
||||
|
|
||||
LL | tcx.sess.opts;
|
||||
| +++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0609`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue