suggest replacing field when using the same type
This commit is contained in:
parent
185a3f0a11
commit
9e4d019fee
|
@ -10,6 +10,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||||
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
|
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::error_reporting::same_type_modulo_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::middle::stability::EvalResult;
|
||||||
use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeFoldable};
|
use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeFoldable};
|
||||||
|
@ -1258,7 +1259,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
self.field_ty(span, f, substs)
|
self.field_ty(span, f, substs)
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
inexistent_fields.push(field.ident);
|
inexistent_fields.push(field);
|
||||||
no_field_errors = false;
|
no_field_errors = false;
|
||||||
tcx.ty_error()
|
tcx.ty_error()
|
||||||
})
|
})
|
||||||
|
@ -1276,13 +1277,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let inexistent_fields_err = if !(inexistent_fields.is_empty() || variant.is_recovered())
|
let inexistent_fields_err = if !(inexistent_fields.is_empty() || variant.is_recovered())
|
||||||
&& !inexistent_fields.iter().any(|field| field.name == kw::Underscore)
|
&& !inexistent_fields.iter().any(|field| field.ident.name == kw::Underscore)
|
||||||
{
|
{
|
||||||
Some(self.error_inexistent_fields(
|
Some(self.error_inexistent_fields(
|
||||||
adt.variant_descr(),
|
adt.variant_descr(),
|
||||||
&inexistent_fields,
|
&inexistent_fields,
|
||||||
&mut unmentioned_fields,
|
&mut unmentioned_fields,
|
||||||
variant,
|
variant,
|
||||||
|
substs,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -1448,20 +1450,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
fn error_inexistent_fields(
|
fn error_inexistent_fields(
|
||||||
&self,
|
&self,
|
||||||
kind_name: &str,
|
kind_name: &str,
|
||||||
inexistent_fields: &[Ident],
|
inexistent_fields: &[&hir::PatField<'tcx>],
|
||||||
unmentioned_fields: &mut Vec<(&ty::FieldDef, Ident)>,
|
unmentioned_fields: &mut Vec<(&'tcx ty::FieldDef, Ident)>,
|
||||||
variant: &ty::VariantDef,
|
variant: &ty::VariantDef,
|
||||||
|
substs: &'tcx ty::List<ty::subst::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 inexistent_fields.len() == 1 {
|
||||||
(format!("a field named `{}`", inexistent_fields[0]), "this", "")
|
(format!("a field named `{}`", inexistent_fields[0].ident), "this", "")
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
format!(
|
format!(
|
||||||
"fields named {}",
|
"fields named {}",
|
||||||
inexistent_fields
|
inexistent_fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ident| format!("`{}`", ident))
|
.map(|field| format!("`{}`", field.ident))
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
),
|
),
|
||||||
|
@ -1469,7 +1472,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
"s",
|
"s",
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let spans = inexistent_fields.iter().map(|ident| ident.span).collect::<Vec<_>>();
|
let spans = inexistent_fields.iter().map(|field| field.ident.span).collect::<Vec<_>>();
|
||||||
let mut err = struct_span_err!(
|
let mut err = struct_span_err!(
|
||||||
tcx.sess,
|
tcx.sess,
|
||||||
spans,
|
spans,
|
||||||
|
@ -1479,9 +1482,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
tcx.def_path_str(variant.def_id),
|
tcx.def_path_str(variant.def_id),
|
||||||
field_names
|
field_names
|
||||||
);
|
);
|
||||||
if let Some(ident) = inexistent_fields.last() {
|
if let Some(pat_field) = inexistent_fields.last() {
|
||||||
err.span_label(
|
err.span_label(
|
||||||
ident.span,
|
pat_field.ident.span,
|
||||||
format!(
|
format!(
|
||||||
"{} `{}` does not have {} field{}",
|
"{} `{}` does not have {} field{}",
|
||||||
kind_name,
|
kind_name,
|
||||||
|
@ -1494,10 +1497,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
if unmentioned_fields.len() == 1 {
|
if unmentioned_fields.len() == 1 {
|
||||||
let input =
|
let input =
|
||||||
unmentioned_fields.iter().map(|(_, field)| field.name).collect::<Vec<_>>();
|
unmentioned_fields.iter().map(|(_, field)| field.name).collect::<Vec<_>>();
|
||||||
let suggested_name = find_best_match_for_name(&input, ident.name, None);
|
let suggested_name = find_best_match_for_name(&input, 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(
|
||||||
ident.span,
|
pat_field.ident.span,
|
||||||
"a field with a similar name exists",
|
"a field with a similar name exists",
|
||||||
suggested_name.to_string(),
|
suggested_name.to_string(),
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
|
@ -1513,17 +1516,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
unmentioned_fields.retain(|&(_, x)| x.name != suggested_name);
|
unmentioned_fields.retain(|&(_, x)| x.name != suggested_name);
|
||||||
}
|
}
|
||||||
} else if inexistent_fields.len() == 1 {
|
} else if inexistent_fields.len() == 1 {
|
||||||
let unmentioned_field = unmentioned_fields[0].1.name;
|
match pat_field.pat.kind {
|
||||||
err.span_suggestion_short(
|
PatKind::Lit(expr)
|
||||||
ident.span,
|
if !same_type_modulo_infer(
|
||||||
&format!(
|
self.typeck_results.borrow().expr_ty(expr),
|
||||||
"`{}` has a field named `{}`",
|
self.field_ty(
|
||||||
tcx.def_path_str(variant.def_id),
|
unmentioned_fields[0].1.span,
|
||||||
unmentioned_field
|
unmentioned_fields[0].0,
|
||||||
),
|
substs,
|
||||||
unmentioned_field.to_string(),
|
),
|
||||||
Applicability::MaybeIncorrect,
|
) => {}
|
||||||
);
|
_ => {
|
||||||
|
let unmentioned_field = unmentioned_fields[0].1.name;
|
||||||
|
err.span_suggestion_short(
|
||||||
|
pat_field.ident.span,
|
||||||
|
&format!(
|
||||||
|
"`{}` has a field named `{}`",
|
||||||
|
tcx.def_path_str(variant.def_id),
|
||||||
|
unmentioned_field
|
||||||
|
),
|
||||||
|
unmentioned_field.to_string(),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,7 @@ error[E0026]: struct `SimpleStruct` does not have a field named `state`
|
||||||
--> $DIR/issue-51102.rs:13:17
|
--> $DIR/issue-51102.rs:13:17
|
||||||
|
|
|
|
||||||
LL | state: 0,
|
LL | state: 0,
|
||||||
| ^^^^^
|
| ^^^^^ struct `SimpleStruct` does not have this field
|
||||||
| |
|
|
||||||
| struct `SimpleStruct` does not have this field
|
|
||||||
| help: `SimpleStruct` has a field named `no_state_here`
|
|
||||||
|
|
||||||
error[E0025]: field `no_state_here` bound multiple times in the pattern
|
error[E0025]: field `no_state_here` bound multiple times in the pattern
|
||||||
--> $DIR/issue-51102.rs:24:17
|
--> $DIR/issue-51102.rs:24:17
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
enum Foo {
|
||||||
|
Bar { a: u8, b: i8, c: u8 },
|
||||||
|
Baz { a: f32 },
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo = Foo::None;
|
||||||
|
match foo {
|
||||||
|
Foo::Bar { a, aa: 1, c } => (),
|
||||||
|
//~^ ERROR variant `Foo::Bar` does not have a field named `aa` [E0026]
|
||||||
|
//~| ERROR pattern does not mention field `b` [E0027]
|
||||||
|
Foo::Baz { bb: 1.0 } => (),
|
||||||
|
//~^ ERROR variant `Foo::Baz` does not have a field named `bb` [E0026]
|
||||||
|
//~| ERROR pattern does not mention field `a` [E0027]
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
match foo {
|
||||||
|
Foo::Bar { a, aa: "", c } => (),
|
||||||
|
//~^ ERROR variant `Foo::Bar` does not have a field named `aa` [E0026]
|
||||||
|
//~| ERROR pattern does not mention field `b` [E0027]
|
||||||
|
Foo::Baz { bb: "" } => (),
|
||||||
|
//~^ ERROR variant `Foo::Baz` does not have a field named `bb` [E0026]
|
||||||
|
//~| pattern does not mention field `a` [E0027]
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
error[E0026]: variant `Foo::Bar` does not have a field named `aa`
|
||||||
|
--> $DIR/suggest-replacing-field-when-specifying-same-type.rs:10:23
|
||||||
|
|
|
||||||
|
LL | Foo::Bar { a, aa: 1, c } => (),
|
||||||
|
| ^^
|
||||||
|
| |
|
||||||
|
| variant `Foo::Bar` does not have this field
|
||||||
|
| help: `Foo::Bar` has a field named `b`
|
||||||
|
|
||||||
|
error[E0027]: pattern does not mention field `b`
|
||||||
|
--> $DIR/suggest-replacing-field-when-specifying-same-type.rs:10:9
|
||||||
|
|
|
||||||
|
LL | Foo::Bar { a, aa: 1, c } => (),
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ missing field `b`
|
||||||
|
|
|
||||||
|
help: include the missing field in the pattern
|
||||||
|
|
|
||||||
|
LL | Foo::Bar { a, aa: 1, c, b } => (),
|
||||||
|
| ~~~~~
|
||||||
|
help: if you don't care about this missing field, you can explicitly ignore it
|
||||||
|
|
|
||||||
|
LL | Foo::Bar { a, aa: 1, c, .. } => (),
|
||||||
|
| ~~~~~~
|
||||||
|
|
||||||
|
error[E0026]: variant `Foo::Baz` does not have a field named `bb`
|
||||||
|
--> $DIR/suggest-replacing-field-when-specifying-same-type.rs:13:20
|
||||||
|
|
|
||||||
|
LL | Foo::Baz { bb: 1.0 } => (),
|
||||||
|
| ^^
|
||||||
|
| |
|
||||||
|
| variant `Foo::Baz` does not have this field
|
||||||
|
| help: `Foo::Baz` has a field named `a`
|
||||||
|
|
||||||
|
error[E0027]: pattern does not mention field `a`
|
||||||
|
--> $DIR/suggest-replacing-field-when-specifying-same-type.rs:13:9
|
||||||
|
|
|
||||||
|
LL | Foo::Baz { bb: 1.0 } => (),
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^ missing field `a`
|
||||||
|
|
|
||||||
|
help: include the missing field in the pattern
|
||||||
|
|
|
||||||
|
LL | Foo::Baz { bb: 1.0, a } => (),
|
||||||
|
| ~~~~~
|
||||||
|
help: if you don't care about this missing field, you can explicitly ignore it
|
||||||
|
|
|
||||||
|
LL | Foo::Baz { bb: 1.0, .. } => (),
|
||||||
|
| ~~~~~~
|
||||||
|
|
||||||
|
error[E0026]: variant `Foo::Bar` does not have a field named `aa`
|
||||||
|
--> $DIR/suggest-replacing-field-when-specifying-same-type.rs:20:23
|
||||||
|
|
|
||||||
|
LL | Foo::Bar { a, aa: "", c } => (),
|
||||||
|
| ^^ variant `Foo::Bar` does not have this field
|
||||||
|
|
||||||
|
error[E0027]: pattern does not mention field `b`
|
||||||
|
--> $DIR/suggest-replacing-field-when-specifying-same-type.rs:20:9
|
||||||
|
|
|
||||||
|
LL | Foo::Bar { a, aa: "", c } => (),
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ missing field `b`
|
||||||
|
|
|
||||||
|
help: include the missing field in the pattern
|
||||||
|
|
|
||||||
|
LL | Foo::Bar { a, aa: "", c, b } => (),
|
||||||
|
| ~~~~~
|
||||||
|
help: if you don't care about this missing field, you can explicitly ignore it
|
||||||
|
|
|
||||||
|
LL | Foo::Bar { a, aa: "", c, .. } => (),
|
||||||
|
| ~~~~~~
|
||||||
|
|
||||||
|
error[E0026]: variant `Foo::Baz` does not have a field named `bb`
|
||||||
|
--> $DIR/suggest-replacing-field-when-specifying-same-type.rs:23:20
|
||||||
|
|
|
||||||
|
LL | Foo::Baz { bb: "" } => (),
|
||||||
|
| ^^ variant `Foo::Baz` does not have this field
|
||||||
|
|
||||||
|
error[E0027]: pattern does not mention field `a`
|
||||||
|
--> $DIR/suggest-replacing-field-when-specifying-same-type.rs:23:9
|
||||||
|
|
|
||||||
|
LL | Foo::Baz { bb: "" } => (),
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^ missing field `a`
|
||||||
|
|
|
||||||
|
help: include the missing field in the pattern
|
||||||
|
|
|
||||||
|
LL | Foo::Baz { bb: "", a } => (),
|
||||||
|
| ~~~~~
|
||||||
|
help: if you don't care about this missing field, you can explicitly ignore it
|
||||||
|
|
|
||||||
|
LL | Foo::Baz { bb: "", .. } => (),
|
||||||
|
| ~~~~~~
|
||||||
|
|
||||||
|
error: aborting due to 8 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0026, E0027.
|
||||||
|
For more information about an error, try `rustc --explain E0026`.
|
Loading…
Reference in a new issue