clean up struct field suggestions
This commit is contained in:
parent
e3c631b3de
commit
867cc41b5b
@ -41,7 +41,6 @@ use rustc_infer::infer::DefineOpaqueTypes;
|
|||||||
use rustc_infer::infer::InferOk;
|
use rustc_infer::infer::InferOk;
|
||||||
use rustc_infer::traits::query::NoSolution;
|
use rustc_infer::traits::query::NoSolution;
|
||||||
use rustc_infer::traits::ObligationCause;
|
use rustc_infer::traits::ObligationCause;
|
||||||
use rustc_middle::middle::stability;
|
|
||||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
|
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
|
||||||
use rustc_middle::ty::error::{
|
use rustc_middle::ty::error::{
|
||||||
ExpectedFound,
|
ExpectedFound,
|
||||||
@ -1585,12 +1584,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
self.check_expr_struct_fields(
|
self.check_expr_struct_fields(
|
||||||
adt_ty,
|
adt_ty,
|
||||||
expected,
|
expected,
|
||||||
expr.hir_id,
|
expr,
|
||||||
qpath.span(),
|
qpath.span(),
|
||||||
variant,
|
variant,
|
||||||
fields,
|
fields,
|
||||||
base_expr,
|
base_expr,
|
||||||
expr.span,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized);
|
self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized);
|
||||||
@ -1601,12 +1599,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
&self,
|
&self,
|
||||||
adt_ty: Ty<'tcx>,
|
adt_ty: Ty<'tcx>,
|
||||||
expected: Expectation<'tcx>,
|
expected: Expectation<'tcx>,
|
||||||
expr_id: hir::HirId,
|
expr: &hir::Expr<'_>,
|
||||||
span: Span,
|
span: Span,
|
||||||
variant: &'tcx ty::VariantDef,
|
variant: &'tcx ty::VariantDef,
|
||||||
ast_fields: &'tcx [hir::ExprField<'tcx>],
|
ast_fields: &'tcx [hir::ExprField<'tcx>],
|
||||||
base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
|
base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
|
||||||
expr_span: Span,
|
|
||||||
) {
|
) {
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
|
|
||||||
@ -1646,7 +1643,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
// struct-like enums (yet...), but it's definitely not
|
// struct-like enums (yet...), but it's definitely not
|
||||||
// a bug to have constructed one.
|
// a bug to have constructed one.
|
||||||
if adt_kind != AdtKind::Enum {
|
if adt_kind != AdtKind::Enum {
|
||||||
tcx.check_stability(v_field.did, Some(expr_id), field.span, None);
|
tcx.check_stability(v_field.did, Some(expr.hir_id), field.span, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.field_ty(field.span, v_field, args)
|
self.field_ty(field.span, v_field, args)
|
||||||
@ -1662,10 +1659,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
self.report_unknown_field(
|
self.report_unknown_field(
|
||||||
adt_ty,
|
adt_ty,
|
||||||
variant,
|
variant,
|
||||||
|
expr,
|
||||||
field,
|
field,
|
||||||
ast_fields,
|
ast_fields,
|
||||||
adt.variant_descr(),
|
adt.variant_descr(),
|
||||||
expr_span,
|
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1731,7 +1728,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|f| {
|
.map(|f| {
|
||||||
let fru_ty = self
|
let fru_ty = self
|
||||||
.normalize(expr_span, self.field_ty(base_expr.span, f, fresh_args));
|
.normalize(expr.span, self.field_ty(base_expr.span, f, fresh_args));
|
||||||
let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id);
|
let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id);
|
||||||
if let Some(_) = remaining_fields.remove(&ident) {
|
if let Some(_) = remaining_fields.remove(&ident) {
|
||||||
let target_ty = self.field_ty(base_expr.span, f, args);
|
let target_ty = self.field_ty(base_expr.span, f, args);
|
||||||
@ -1814,7 +1811,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
ty::Adt(adt, args) if adt.is_struct() => variant
|
ty::Adt(adt, args) if adt.is_struct() => variant
|
||||||
.fields
|
.fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|f| self.normalize(expr_span, f.ty(self.tcx, args)))
|
.map(|f| self.normalize(expr.span, f.ty(self.tcx, args)))
|
||||||
.collect(),
|
.collect(),
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx
|
self.tcx
|
||||||
@ -1824,13 +1821,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys);
|
self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr.hir_id, fru_tys);
|
||||||
} else if adt_kind != AdtKind::Union && !remaining_fields.is_empty() {
|
} else if adt_kind != AdtKind::Union && !remaining_fields.is_empty() {
|
||||||
debug!(?remaining_fields);
|
debug!(?remaining_fields);
|
||||||
let private_fields: Vec<&ty::FieldDef> = variant
|
let private_fields: Vec<&ty::FieldDef> = variant
|
||||||
.fields
|
.fields
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|field| !field.vis.is_accessible_from(tcx.parent_module(expr_id), tcx))
|
.filter(|field| !field.vis.is_accessible_from(tcx.parent_module(expr.hir_id), tcx))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if !private_fields.is_empty() {
|
if !private_fields.is_empty() {
|
||||||
@ -2049,16 +2046,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
&self,
|
&self,
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
variant: &'tcx ty::VariantDef,
|
variant: &'tcx ty::VariantDef,
|
||||||
|
expr: &hir::Expr<'_>,
|
||||||
field: &hir::ExprField<'_>,
|
field: &hir::ExprField<'_>,
|
||||||
skip_fields: &[hir::ExprField<'_>],
|
skip_fields: &[hir::ExprField<'_>],
|
||||||
kind_name: &str,
|
kind_name: &str,
|
||||||
expr_span: Span,
|
|
||||||
) -> ErrorGuaranteed {
|
) -> ErrorGuaranteed {
|
||||||
if variant.is_recovered() {
|
if variant.is_recovered() {
|
||||||
let guar = self
|
let guar = self
|
||||||
.tcx
|
.tcx
|
||||||
.sess
|
.sess
|
||||||
.delay_span_bug(expr_span, "parser recovered but no error was emitted");
|
.delay_span_bug(expr.span, "parser recovered but no error was emitted");
|
||||||
self.set_tainted_by_errors(guar);
|
self.set_tainted_by_errors(guar);
|
||||||
return guar;
|
return guar;
|
||||||
}
|
}
|
||||||
@ -2102,7 +2099,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
);
|
);
|
||||||
err.span_label(field.ident.span, "field does not exist");
|
err.span_label(field.ident.span, "field does not exist");
|
||||||
err.span_suggestion_verbose(
|
err.span_suggestion_verbose(
|
||||||
expr_span,
|
expr.span,
|
||||||
format!(
|
format!(
|
||||||
"`{adt}::{variant}` is a tuple {kind_name}, use the appropriate syntax",
|
"`{adt}::{variant}` is a tuple {kind_name}, use the appropriate syntax",
|
||||||
adt = ty,
|
adt = ty,
|
||||||
@ -2120,7 +2117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
err.span_label(variant_ident_span, format!("`{ty}` defined here"));
|
err.span_label(variant_ident_span, format!("`{ty}` defined here"));
|
||||||
err.span_label(field.ident.span, "field does not exist");
|
err.span_label(field.ident.span, "field does not exist");
|
||||||
err.span_suggestion_verbose(
|
err.span_suggestion_verbose(
|
||||||
expr_span,
|
expr.span,
|
||||||
format!("`{ty}` is a tuple {kind_name}, use the appropriate syntax",),
|
format!("`{ty}` is a tuple {kind_name}, use the appropriate syntax",),
|
||||||
format!("{ty}(/* fields */)"),
|
format!("{ty}(/* fields */)"),
|
||||||
Applicability::HasPlaceholders,
|
Applicability::HasPlaceholders,
|
||||||
@ -2129,9 +2126,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
// prevent all specified fields from being suggested
|
// prevent all specified fields from being suggested
|
||||||
let skip_fields: Vec<_> = skip_fields.iter().map(|x| x.ident.name).collect();
|
let available_field_names = self.available_field_names(variant, expr, skip_fields);
|
||||||
if let Some(field_name) =
|
if let Some(field_name) =
|
||||||
self.suggest_field_name(variant, field.ident.name, &skip_fields, expr_span)
|
find_best_match_for_name(&available_field_names, field.ident.name, None)
|
||||||
{
|
{
|
||||||
err.span_suggestion(
|
err.span_suggestion(
|
||||||
field.ident.span,
|
field.ident.span,
|
||||||
@ -2153,10 +2150,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
format!("`{ty}` does not have this field"),
|
format!("`{ty}` does not have this field"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let mut available_field_names =
|
|
||||||
self.available_field_names(variant, expr_span);
|
|
||||||
available_field_names
|
|
||||||
.retain(|name| skip_fields.iter().all(|skip| name != skip));
|
|
||||||
if available_field_names.is_empty() {
|
if available_field_names.is_empty() {
|
||||||
err.note("all struct fields are already assigned");
|
err.note("all struct fields are already assigned");
|
||||||
} else {
|
} else {
|
||||||
@ -2174,63 +2167,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
err.emit()
|
err.emit()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a hint about the closest match in field names
|
|
||||||
fn suggest_field_name(
|
|
||||||
&self,
|
|
||||||
variant: &'tcx ty::VariantDef,
|
|
||||||
field: Symbol,
|
|
||||||
skip: &[Symbol],
|
|
||||||
// The span where stability will be checked
|
|
||||||
span: Span,
|
|
||||||
) -> Option<Symbol> {
|
|
||||||
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 {
|
|
||||||
Some(field.name)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<Symbol>>();
|
|
||||||
|
|
||||||
find_best_match_for_name(&names, field, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn available_field_names(
|
fn available_field_names(
|
||||||
&self,
|
&self,
|
||||||
variant: &'tcx ty::VariantDef,
|
variant: &'tcx ty::VariantDef,
|
||||||
access_span: Span,
|
expr: &hir::Expr<'_>,
|
||||||
|
skip_fields: &[hir::ExprField<'_>],
|
||||||
) -> Vec<Symbol> {
|
) -> Vec<Symbol> {
|
||||||
let body_owner_hir_id = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
|
|
||||||
variant
|
variant
|
||||||
.fields
|
.fields
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|field| {
|
.filter(|field| {
|
||||||
let def_scope = self
|
skip_fields.iter().all(|&skip| skip.ident.name != field.name)
|
||||||
.tcx
|
&& self.is_field_suggestable(field, expr.hir_id, expr.span)
|
||||||
.adjust_ident_and_get_scope(
|
|
||||||
field.ident(self.tcx),
|
|
||||||
variant.def_id,
|
|
||||||
body_owner_hir_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)
|
.map(|field| field.name)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@ -2460,7 +2409,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
self.suggest_first_deref_field(&mut err, expr, base, ident);
|
self.suggest_first_deref_field(&mut err, expr, base, ident);
|
||||||
}
|
}
|
||||||
ty::Adt(def, _) if !def.is_enum() => {
|
ty::Adt(def, _) if !def.is_enum() => {
|
||||||
self.suggest_fields_on_recordish(&mut err, def, ident, expr.span);
|
self.suggest_fields_on_recordish(&mut err, expr, def, ident);
|
||||||
}
|
}
|
||||||
ty::Param(param_ty) => {
|
ty::Param(param_ty) => {
|
||||||
self.point_at_param_definition(&mut err, param_ty);
|
self.point_at_param_definition(&mut err, param_ty);
|
||||||
@ -2622,12 +2571,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
fn suggest_fields_on_recordish(
|
fn suggest_fields_on_recordish(
|
||||||
&self,
|
&self,
|
||||||
err: &mut Diagnostic,
|
err: &mut Diagnostic,
|
||||||
|
expr: &hir::Expr<'_>,
|
||||||
def: ty::AdtDef<'tcx>,
|
def: ty::AdtDef<'tcx>,
|
||||||
field: Ident,
|
field: Ident,
|
||||||
access_span: Span,
|
|
||||||
) {
|
) {
|
||||||
|
let available_field_names = self.available_field_names(def.non_enum_variant(), expr, &[]);
|
||||||
if let Some(suggested_field_name) =
|
if let Some(suggested_field_name) =
|
||||||
self.suggest_field_name(def.non_enum_variant(), field.name, &[], access_span)
|
find_best_match_for_name(&available_field_names, field.name, None)
|
||||||
{
|
{
|
||||||
err.span_suggestion(
|
err.span_suggestion(
|
||||||
field.span,
|
field.span,
|
||||||
@ -2637,12 +2587,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
err.span_label(field.span, "unknown field");
|
err.span_label(field.span, "unknown field");
|
||||||
let struct_variant_def = def.non_enum_variant();
|
if !available_field_names.is_empty() {
|
||||||
let field_names = self.available_field_names(struct_variant_def, access_span);
|
|
||||||
if !field_names.is_empty() {
|
|
||||||
err.note(format!(
|
err.note(format!(
|
||||||
"available fields are: {}",
|
"available fields are: {}",
|
||||||
self.name_series_display(field_names),
|
self.name_series_display(available_field_names),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1687,4 +1687,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_field_suggestable(
|
||||||
|
&self,
|
||||||
|
field: &ty::FieldDef,
|
||||||
|
hir_id: HirId,
|
||||||
|
span: Span,
|
||||||
|
) -> bool {
|
||||||
|
// The field must be visible in the containing module.
|
||||||
|
field.vis.is_accessible_from(self.tcx.parent_module(hir_id), self.tcx)
|
||||||
|
// The field must not be unstable.
|
||||||
|
&& !matches!(
|
||||||
|
self.tcx.eval_stability(field.did, None, rustc_span::DUMMY_SP, None),
|
||||||
|
rustc_middle::middle::stability::EvalResult::Deny { .. }
|
||||||
|
)
|
||||||
|
// If the field is from an external crate it must not be `doc(hidden)`.
|
||||||
|
&& (field.did.is_local() || !self.tcx.is_doc_hidden(field.did))
|
||||||
|
// If the field is hygienic it must come from the same syntax context.
|
||||||
|
&& self.tcx.def_ident_span(field.did).unwrap().normalize_to_macros_2_0().eq_ctxt(span)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ use rustc_hir::pat_util::EnumerateAndAdjustIterator;
|
|||||||
use rustc_hir::{HirId, Pat, PatKind};
|
use rustc_hir::{HirId, Pat, PatKind};
|
||||||
use rustc_infer::infer;
|
use rustc_infer::infer;
|
||||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||||
use rustc_middle::middle::stability::EvalResult;
|
|
||||||
use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeVisitableExt};
|
use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeVisitableExt};
|
||||||
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
|
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
|
||||||
use rustc_span::edit_distance::find_best_match_for_name;
|
use rustc_span::edit_distance::find_best_match_for_name;
|
||||||
@ -1408,6 +1407,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
adt.variant_descr(),
|
adt.variant_descr(),
|
||||||
&inexistent_fields,
|
&inexistent_fields,
|
||||||
&mut unmentioned_fields,
|
&mut unmentioned_fields,
|
||||||
|
pat,
|
||||||
variant,
|
variant,
|
||||||
args,
|
args,
|
||||||
))
|
))
|
||||||
@ -1434,15 +1434,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
let accessible_unmentioned_fields: Vec<_> = unmentioned_fields
|
let accessible_unmentioned_fields: Vec<_> = unmentioned_fields
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
.filter(|(field, _)| {
|
.filter(|(field, _)| self.is_field_suggestable(field, pat.hir_id, pat.span))
|
||||||
field.vis.is_accessible_from(tcx.parent_module(pat.hir_id), tcx)
|
|
||||||
&& !matches!(
|
|
||||||
tcx.eval_stability(field.did, None, DUMMY_SP, None),
|
|
||||||
EvalResult::Deny { .. }
|
|
||||||
)
|
|
||||||
// We only want to report the error if it is hidden and not local
|
|
||||||
&& !(tcx.is_doc_hidden(field.did) && !field.did.is_local())
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if !has_rest_pat {
|
if !has_rest_pat {
|
||||||
@ -1578,12 +1570,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
kind_name: &str,
|
kind_name: &str,
|
||||||
inexistent_fields: &[&hir::PatField<'tcx>],
|
inexistent_fields: &[&hir::PatField<'tcx>],
|
||||||
unmentioned_fields: &mut Vec<(&'tcx ty::FieldDef, Ident)>,
|
unmentioned_fields: &mut Vec<(&'tcx ty::FieldDef, Ident)>,
|
||||||
|
pat: &'tcx Pat<'tcx>,
|
||||||
variant: &ty::VariantDef,
|
variant: &ty::VariantDef,
|
||||||
args: &'tcx ty::List<ty::GenericArg<'tcx>>,
|
args: &'tcx ty::List<ty::GenericArg<'tcx>>,
|
||||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
let (field_names, t, plural) = if inexistent_fields.len() == 1 {
|
let (field_names, t, plural) = if let [field] = inexistent_fields {
|
||||||
(format!("a field named `{}`", inexistent_fields[0].ident), "this", "")
|
(format!("a field named `{}`", field.ident), "this", "")
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
format!(
|
format!(
|
||||||
@ -1620,10 +1613,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if unmentioned_fields.len() == 1 {
|
if let [(field_def, field)] = unmentioned_fields.as_slice()
|
||||||
let input =
|
&& self.is_field_suggestable(field_def, pat.hir_id, pat.span)
|
||||||
unmentioned_fields.iter().map(|(_, field)| field.name).collect::<Vec<_>>();
|
{
|
||||||
let suggested_name = find_best_match_for_name(&input, pat_field.ident.name, None);
|
let suggested_name =
|
||||||
|
find_best_match_for_name(&[field.name], pat_field.ident.name, None);
|
||||||
if let Some(suggested_name) = suggested_name {
|
if let Some(suggested_name) = suggested_name {
|
||||||
err.span_suggestion(
|
err.span_suggestion(
|
||||||
pat_field.ident.span,
|
pat_field.ident.span,
|
||||||
@ -1646,22 +1640,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
PatKind::Lit(expr)
|
PatKind::Lit(expr)
|
||||||
if !self.can_coerce(
|
if !self.can_coerce(
|
||||||
self.typeck_results.borrow().expr_ty(expr),
|
self.typeck_results.borrow().expr_ty(expr),
|
||||||
self.field_ty(
|
self.field_ty(field.span, field_def, args),
|
||||||
unmentioned_fields[0].1.span,
|
|
||||||
unmentioned_fields[0].0,
|
|
||||||
args,
|
|
||||||
),
|
|
||||||
) => {}
|
) => {}
|
||||||
_ => {
|
_ => {
|
||||||
let unmentioned_field = unmentioned_fields[0].1.name;
|
|
||||||
err.span_suggestion_short(
|
err.span_suggestion_short(
|
||||||
pat_field.ident.span,
|
pat_field.ident.span,
|
||||||
format!(
|
format!(
|
||||||
"`{}` has a field named `{}`",
|
"`{}` has a field named `{}`",
|
||||||
tcx.def_path_str(variant.def_id),
|
tcx.def_path_str(variant.def_id),
|
||||||
unmentioned_field
|
field.name,
|
||||||
),
|
),
|
||||||
unmentioned_field.to_string(),
|
field.name,
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1871,8 +1860,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
fields: &'tcx [hir::PatField<'tcx>],
|
fields: &'tcx [hir::PatField<'tcx>],
|
||||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||||
let inaccessible = if have_inaccessible_fields { " and inaccessible fields" } else { "" };
|
let inaccessible = if have_inaccessible_fields { " and inaccessible fields" } else { "" };
|
||||||
let field_names = if unmentioned_fields.len() == 1 {
|
let field_names = if let [(_, field)] = unmentioned_fields {
|
||||||
format!("field `{}`{}", unmentioned_fields[0].1, inaccessible)
|
format!("field `{field}`{inaccessible}")
|
||||||
} else {
|
} else {
|
||||||
let fields = unmentioned_fields
|
let fields = unmentioned_fields
|
||||||
.iter()
|
.iter()
|
||||||
|
6
tests/ui/did_you_mean/auxiliary/doc-hidden-fields.rs
Normal file
6
tests/ui/did_you_mean/auxiliary/doc-hidden-fields.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#[derive(Default)]
|
||||||
|
pub struct B {
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub hello: i32,
|
||||||
|
pub bye: i32,
|
||||||
|
}
|
38
tests/ui/did_you_mean/dont-suggest-doc-hidden-fields.rs
Normal file
38
tests/ui/did_you_mean/dont-suggest-doc-hidden-fields.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Regression test for issue #93210.
|
||||||
|
|
||||||
|
// aux-crate:doc_hidden_fields=doc-hidden-fields.rs
|
||||||
|
// edition: 2021
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct A {
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub hello: i32,
|
||||||
|
pub bye: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct C {
|
||||||
|
pub hello: i32,
|
||||||
|
pub bye: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// We want to list the field `hello` despite being marked
|
||||||
|
// `doc(hidden)` because it's defined in this crate.
|
||||||
|
A::default().hey;
|
||||||
|
//~^ ERROR no field `hey` on type `A`
|
||||||
|
//~| NOTE unknown field
|
||||||
|
//~| NOTE available fields are: `hello`, `bye`
|
||||||
|
|
||||||
|
// Here we want to hide the field `hello` since it's marked
|
||||||
|
// `doc(hidden)` and comes from an external crate.
|
||||||
|
doc_hidden_fields::B::default().hey;
|
||||||
|
//~^ ERROR no field `hey` on type `B`
|
||||||
|
//~| NOTE unknown field
|
||||||
|
//~| NOTE available fields are: `bye`
|
||||||
|
|
||||||
|
C::default().hey;
|
||||||
|
//~^ ERROR no field `hey` on type `C`
|
||||||
|
//~| NOTE unknown field
|
||||||
|
//~| NOTE available fields are: `hello`, `bye`
|
||||||
|
}
|
27
tests/ui/did_you_mean/dont-suggest-doc-hidden-fields.stderr
Normal file
27
tests/ui/did_you_mean/dont-suggest-doc-hidden-fields.stderr
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
error[E0609]: no field `hey` on type `A`
|
||||||
|
--> $DIR/dont-suggest-doc-hidden-fields.rs:22:18
|
||||||
|
|
|
||||||
|
LL | A::default().hey;
|
||||||
|
| ^^^ unknown field
|
||||||
|
|
|
||||||
|
= note: available fields are: `hello`, `bye`
|
||||||
|
|
||||||
|
error[E0609]: no field `hey` on type `B`
|
||||||
|
--> $DIR/dont-suggest-doc-hidden-fields.rs:29:37
|
||||||
|
|
|
||||||
|
LL | doc_hidden_fields::B::default().hey;
|
||||||
|
| ^^^ unknown field
|
||||||
|
|
|
||||||
|
= note: available fields are: `bye`
|
||||||
|
|
||||||
|
error[E0609]: no field `hey` on type `C`
|
||||||
|
--> $DIR/dont-suggest-doc-hidden-fields.rs:34:18
|
||||||
|
|
|
||||||
|
LL | C::default().hey;
|
||||||
|
| ^^^ unknown field
|
||||||
|
|
|
||||||
|
= note: available fields are: `hello`, `bye`
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0609`.
|
47
tests/ui/did_you_mean/dont-suggest-hygienic-fields.rs
Normal file
47
tests/ui/did_you_mean/dont-suggest-hygienic-fields.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Regression test for issue #116334.
|
||||||
|
// Don't include hygienic fields from different syntax contexts in
|
||||||
|
// the list of available or similarly named fields.
|
||||||
|
|
||||||
|
#![feature(decl_macro)]
|
||||||
|
|
||||||
|
macro compound($Ty:ident) {
|
||||||
|
#[derive(Default)]
|
||||||
|
struct $Ty {
|
||||||
|
field: u32, // field `field` is hygienic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro component($Ty:ident) {
|
||||||
|
struct $Ty(u64); // field `0` is hygienic (but still accessible via the constructor)
|
||||||
|
}
|
||||||
|
|
||||||
|
compound! { Compound }
|
||||||
|
component! { Component }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let ty = Compound::default();
|
||||||
|
|
||||||
|
let _ = ty.field; //~ ERROR no field `field` on type `Compound`
|
||||||
|
let _ = ty.fieeld; //~ ERROR no field `fieeld` on type `Compound`
|
||||||
|
|
||||||
|
let Compound { field } = ty;
|
||||||
|
//~^ ERROR struct `Compound` does not have a field named `field`
|
||||||
|
//~| ERROR pattern requires `..` due to inaccessible fields
|
||||||
|
//~| HELP ignore the inaccessible and unused fields
|
||||||
|
|
||||||
|
let ty = Component(90);
|
||||||
|
|
||||||
|
let _ = ty.0; //~ ERROR no field `0` on type `Component`
|
||||||
|
}
|
||||||
|
|
||||||
|
environment!();
|
||||||
|
|
||||||
|
macro environment() {
|
||||||
|
struct Crate { field: () }
|
||||||
|
|
||||||
|
// Here, we do want to suggest `field` even though it's hygienic
|
||||||
|
// precisely because they come from the same syntax context.
|
||||||
|
const CRATE: Crate = Crate { fiel: () };
|
||||||
|
//~^ ERROR struct `Crate` has no field named `fiel`
|
||||||
|
//~| HELP a field with a similar name exists
|
||||||
|
}
|
50
tests/ui/did_you_mean/dont-suggest-hygienic-fields.stderr
Normal file
50
tests/ui/did_you_mean/dont-suggest-hygienic-fields.stderr
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
error[E0560]: struct `Crate` has no field named `fiel`
|
||||||
|
--> $DIR/dont-suggest-hygienic-fields.rs:44:34
|
||||||
|
|
|
||||||
|
LL | environment!();
|
||||||
|
| -------------- in this macro invocation
|
||||||
|
...
|
||||||
|
LL | const CRATE: Crate = Crate { fiel: () };
|
||||||
|
| ^^^^ help: a field with a similar name exists: `field`
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `environment` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error[E0609]: no field `field` on type `Compound`
|
||||||
|
--> $DIR/dont-suggest-hygienic-fields.rs:24:16
|
||||||
|
|
|
||||||
|
LL | let _ = ty.field;
|
||||||
|
| ^^^^^ unknown field
|
||||||
|
|
||||||
|
error[E0609]: no field `fieeld` on type `Compound`
|
||||||
|
--> $DIR/dont-suggest-hygienic-fields.rs:25:16
|
||||||
|
|
|
||||||
|
LL | let _ = ty.fieeld;
|
||||||
|
| ^^^^^^ unknown field
|
||||||
|
|
||||||
|
error[E0026]: struct `Compound` does not have a field named `field`
|
||||||
|
--> $DIR/dont-suggest-hygienic-fields.rs:27:20
|
||||||
|
|
|
||||||
|
LL | let Compound { field } = ty;
|
||||||
|
| ^^^^^ struct `Compound` does not have this field
|
||||||
|
|
||||||
|
error: pattern requires `..` due to inaccessible fields
|
||||||
|
--> $DIR/dont-suggest-hygienic-fields.rs:27:9
|
||||||
|
|
|
||||||
|
LL | let Compound { field } = ty;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: ignore the inaccessible and unused fields
|
||||||
|
|
|
||||||
|
LL | let Compound { field, .. } = ty;
|
||||||
|
| ++++
|
||||||
|
|
||||||
|
error[E0609]: no field `0` on type `Component`
|
||||||
|
--> $DIR/dont-suggest-hygienic-fields.rs:34:16
|
||||||
|
|
|
||||||
|
LL | let _ = ty.0;
|
||||||
|
| ^ unknown field
|
||||||
|
|
||||||
|
error: aborting due to 6 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0026, E0560, E0609.
|
||||||
|
For more information about an error, try `rustc --explain E0026`.
|
@ -1,24 +0,0 @@
|
|||||||
#[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`
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
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`.
|
|
Loading…
x
Reference in New Issue
Block a user