diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 23cc4b0ffc814..2cad8aab29ec9 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -35,6 +35,7 @@ use rustc_hir::{ExprKind, QPath}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::InferOk; +use rustc_middle::middle::stability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::error::TypeError::{FieldMisMatch, Sorts}; @@ -1720,9 +1721,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => { // prevent all specified fields from being suggested let skip_fields = skip_fields.iter().map(|x| x.ident.name); - if let Some(field_name) = - Self::suggest_field_name(variant, field.ident.name, skip_fields.collect()) - { + if let Some(field_name) = self.suggest_field_name( + variant, + field.ident.name, + skip_fields.collect(), + expr_span, + ) { err.span_suggestion( field.ident.span, "a field with a similar name exists", @@ -1743,7 +1747,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("`{}` does not have this field", ty), ); } - let available_field_names = self.available_field_names(variant); + let available_field_names = + self.available_field_names(variant, expr_span); if !available_field_names.is_empty() { err.note(&format!( "available fields are: {}", @@ -1759,19 +1764,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } - // Return an hint about the closest match in field names + // Return a hint about the closest match in field names fn suggest_field_name( + &self, variant: &'tcx ty::VariantDef, field: Symbol, skip: Vec, + // The span where stability will be checked + span: Span, ) -> Option { let names = variant .fields .iter() .filter_map(|field| { // ignore already set fields and private fields from non-local crates + // and unstable fields. if skip.iter().any(|&x| x == field.name) || (!variant.def_id.is_local() && !field.vis.is_public()) + || matches!( + self.tcx.eval_stability(field.did, None, span, None), + stability::EvalResult::Deny { .. } + ) { None } else { @@ -1783,7 +1796,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { find_best_match_for_name(&names, field, None) } - fn available_field_names(&self, variant: &'tcx ty::VariantDef) -> Vec { + fn available_field_names( + &self, + variant: &'tcx ty::VariantDef, + access_span: Span, + ) -> Vec { variant .fields .iter() @@ -1793,7 +1810,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .adjust_ident_and_get_scope(field.ident(self.tcx), variant.def_id, self.body_id) .1; field.vis.is_accessible_from(def_scope, self.tcx) + && !matches!( + self.tcx.eval_stability(field.did, None, access_span, None), + stability::EvalResult::Deny { .. } + ) }) + .filter(|field| !self.tcx.is_doc_hidden(field.did)) .map(|field| field.name) .collect() } @@ -1958,7 +1980,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_first_deref_field(&mut err, expr, base, field); } ty::Adt(def, _) if !def.is_enum() => { - self.suggest_fields_on_recordish(&mut err, def, field); + self.suggest_fields_on_recordish(&mut err, def, field, expr.span); } ty::Param(param_ty) => { self.point_at_param_definition(&mut err, param_ty); @@ -2121,9 +2143,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err: &mut DiagnosticBuilder<'_>, def: &'tcx ty::AdtDef, field: Ident, + access_span: Span, ) { if let Some(suggested_field_name) = - Self::suggest_field_name(def.non_enum_variant(), field.name, vec![]) + self.suggest_field_name(def.non_enum_variant(), field.name, vec![], access_span) { err.span_suggestion( field.span, @@ -2134,7 +2157,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { err.span_label(field.span, "unknown field"); let struct_variant_def = def.non_enum_variant(); - let field_names = self.available_field_names(struct_variant_def); + let field_names = self.available_field_names(struct_variant_def, access_span); if !field_names.is_empty() { err.note(&format!( "available fields are: {}", diff --git a/src/test/ui/did_you_mean/issue-93210-ignore-doc-hidden.rs b/src/test/ui/did_you_mean/issue-93210-ignore-doc-hidden.rs new file mode 100644 index 0000000000000..0efc7daa3e17b --- /dev/null +++ b/src/test/ui/did_you_mean/issue-93210-ignore-doc-hidden.rs @@ -0,0 +1,24 @@ +#[derive(Default)] +pub struct A { + #[doc(hidden)] + pub hello: i32, + pub bye: i32, +} + +#[derive(Default)] +pub struct B { + pub hello: i32, + pub bye: i32, +} + +fn main() { + A::default().hey; + //~^ ERROR no field `hey` on type `A` + //~| NOTE unknown field + //~| NOTE available fields are: `bye` + + B::default().hey; + //~^ ERROR no field `hey` on type `B` + //~| NOTE unknown field + //~| NOTE available fields are: `hello`, `bye` +} diff --git a/src/test/ui/did_you_mean/issue-93210-ignore-doc-hidden.stderr b/src/test/ui/did_you_mean/issue-93210-ignore-doc-hidden.stderr new file mode 100644 index 0000000000000..784986d3b95fb --- /dev/null +++ b/src/test/ui/did_you_mean/issue-93210-ignore-doc-hidden.stderr @@ -0,0 +1,19 @@ +error[E0609]: no field `hey` on type `A` + --> $DIR/issue-93210-ignore-doc-hidden.rs:15:18 + | +LL | A::default().hey; + | ^^^ unknown field + | + = note: available fields are: `bye` + +error[E0609]: no field `hey` on type `B` + --> $DIR/issue-93210-ignore-doc-hidden.rs:20:18 + | +LL | B::default().hey; + | ^^^ unknown field + | + = note: available fields are: `hello`, `bye` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0609`.