Suggest using :: instead of . for enums in some cases.
Suggest replacing `.` with `::` when encountering "expected value, found enum": - in a method-call expression and the method has the same name as a tuple variant - in a field-access expression and the field has the same name as a unit or tuple variant
This commit is contained in:
parent
fe37adab4b
commit
bfde43c84b
3 changed files with 339 additions and 14 deletions
|
@ -8,7 +8,7 @@ use rustc_ast::ptr::P;
|
||||||
use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt, Visitor, walk_ty};
|
use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt, Visitor, walk_ty};
|
||||||
use rustc_ast::{
|
use rustc_ast::{
|
||||||
self as ast, AssocItemKind, DUMMY_NODE_ID, Expr, ExprKind, GenericParam, GenericParamKind,
|
self as ast, AssocItemKind, DUMMY_NODE_ID, Expr, ExprKind, GenericParam, GenericParamKind,
|
||||||
Item, ItemKind, MethodCall, NodeId, Path, Ty, TyKind,
|
Item, ItemKind, MethodCall, NodeId, Path, PathSegment, Ty, TyKind,
|
||||||
};
|
};
|
||||||
use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
|
use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
|
||||||
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
|
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
|
||||||
|
@ -2469,31 +2469,73 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
span: Span,
|
span: Span,
|
||||||
) {
|
) {
|
||||||
let Some(variants) = self.collect_enum_ctors(def_id) else {
|
let Some(variant_ctors) = self.collect_enum_ctors(def_id) else {
|
||||||
err.note("you might have meant to use one of the enum's variants");
|
err.note("you might have meant to use one of the enum's variants");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let suggest_only_tuple_variants =
|
// If the expression is a field-access or method-call, try to find a variant with the field/method name
|
||||||
matches!(source, PathSource::TupleStruct(..)) || source.is_call();
|
// that could have been intended, and suggest replacing the `.` with `::`.
|
||||||
if suggest_only_tuple_variants {
|
// Otherwise, suggest adding `::VariantName` after the enum;
|
||||||
|
// and if the expression is call-like, only suggest tuple variants.
|
||||||
|
let (suggest_path_sep_dot_span, suggest_only_tuple_variants) = match source {
|
||||||
|
// `Type(a, b)` in a pattern, only suggest adding a tuple variant after `Type`.
|
||||||
|
PathSource::TupleStruct(..) => (None, true),
|
||||||
|
PathSource::Expr(Some(expr)) => match &expr.kind {
|
||||||
|
// `Type(a, b)`, only suggest adding a tuple variant after `Type`.
|
||||||
|
ExprKind::Call(..) => (None, true),
|
||||||
|
// `Type.Foo(a, b)`, suggest replacing `.` -> `::` if variant `Foo` exists and is a tuple variant,
|
||||||
|
// otherwise suggest adding a variant after `Type`.
|
||||||
|
ExprKind::MethodCall(box MethodCall {
|
||||||
|
receiver,
|
||||||
|
span,
|
||||||
|
seg: PathSegment { ident, .. },
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
let dot_span = receiver.span.between(*span);
|
||||||
|
let found_tuple_variant = variant_ctors.iter().any(|(path, _, ctor_kind)| {
|
||||||
|
*ctor_kind == CtorKind::Fn
|
||||||
|
&& path.segments.last().is_some_and(|seg| seg.ident == *ident)
|
||||||
|
});
|
||||||
|
(found_tuple_variant.then_some(dot_span), false)
|
||||||
|
}
|
||||||
|
// `Type.Foo`, suggest replacing `.` -> `::` if variant `Foo` exists and is a unit or tuple variant,
|
||||||
|
// otherwise suggest adding a variant after `Type`.
|
||||||
|
ExprKind::Field(base, ident) => {
|
||||||
|
let dot_span = base.span.between(ident.span);
|
||||||
|
let found_tuple_or_unit_variant = variant_ctors.iter().any(|(path, ..)| {
|
||||||
|
path.segments.last().is_some_and(|seg| seg.ident == *ident)
|
||||||
|
});
|
||||||
|
(found_tuple_or_unit_variant.then_some(dot_span), false)
|
||||||
|
}
|
||||||
|
_ => (None, false),
|
||||||
|
},
|
||||||
|
_ => (None, false),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(dot_span) = suggest_path_sep_dot_span {
|
||||||
|
err.span_suggestion_verbose(
|
||||||
|
dot_span,
|
||||||
|
"use the path separator to refer to a variant",
|
||||||
|
"::",
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
} else if suggest_only_tuple_variants {
|
||||||
// Suggest only tuple variants regardless of whether they have fields and do not
|
// Suggest only tuple variants regardless of whether they have fields and do not
|
||||||
// suggest path with added parentheses.
|
// suggest path with added parentheses.
|
||||||
let mut suggestable_variants = variants
|
let mut suggestable_variants = variant_ctors
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(.., kind)| *kind == CtorKind::Fn)
|
.filter(|(.., kind)| *kind == CtorKind::Fn)
|
||||||
.map(|(variant, ..)| path_names_to_string(variant))
|
.map(|(variant, ..)| path_names_to_string(variant))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
suggestable_variants.sort();
|
suggestable_variants.sort();
|
||||||
|
|
||||||
let non_suggestable_variant_count = variants.len() - suggestable_variants.len();
|
let non_suggestable_variant_count = variant_ctors.len() - suggestable_variants.len();
|
||||||
|
|
||||||
let source_msg = if source.is_call() {
|
let source_msg = if matches!(source, PathSource::TupleStruct(..)) {
|
||||||
"to construct"
|
|
||||||
} else if matches!(source, PathSource::TupleStruct(..)) {
|
|
||||||
"to match against"
|
"to match against"
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
"to construct"
|
||||||
};
|
};
|
||||||
|
|
||||||
if !suggestable_variants.is_empty() {
|
if !suggestable_variants.is_empty() {
|
||||||
|
@ -2512,7 +2554,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the enum has no tuple variants..
|
// If the enum has no tuple variants..
|
||||||
if non_suggestable_variant_count == variants.len() {
|
if non_suggestable_variant_count == variant_ctors.len() {
|
||||||
err.help(format!("the enum has no tuple variants {source_msg}"));
|
err.help(format!("the enum has no tuple variants {source_msg}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2535,7 +2577,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut suggestable_variants = variants
|
let mut suggestable_variants = variant_ctors
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind))
|
.filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind))
|
||||||
.map(|(variant, _, kind)| (path_names_to_string(variant), kind))
|
.map(|(variant, _, kind)| (path_names_to_string(variant), kind))
|
||||||
|
@ -2562,7 +2604,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut suggestable_variants_with_placeholders = variants
|
let mut suggestable_variants_with_placeholders = variant_ctors
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, def_id, kind)| needs_placeholder(*def_id, *kind))
|
.filter(|(_, def_id, kind)| needs_placeholder(*def_id, *kind))
|
||||||
.map(|(variant, _, kind)| (path_names_to_string(variant), kind))
|
.map(|(variant, _, kind)| (path_names_to_string(variant), kind))
|
||||||
|
|
58
tests/ui/resolve/enum-expected-value-suggest-variants.rs
Normal file
58
tests/ui/resolve/enum-expected-value-suggest-variants.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
enum Foo {
|
||||||
|
//~^ HELP consider importing this tuple variant
|
||||||
|
A(u32),
|
||||||
|
B(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Bar {
|
||||||
|
C(u32),
|
||||||
|
D(u32),
|
||||||
|
E,
|
||||||
|
F,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _: Foo = Foo(0);
|
||||||
|
//~^ ERROR expected function
|
||||||
|
//~| HELP try to construct one of the enum's variants
|
||||||
|
|
||||||
|
let _: Foo = Foo.A(0);
|
||||||
|
//~^ ERROR expected value, found enum `Foo`
|
||||||
|
//~| HELP use the path separator to refer to a variant
|
||||||
|
|
||||||
|
let _: Foo = Foo.Bad(0);
|
||||||
|
//~^ ERROR expected value, found enum `Foo`
|
||||||
|
//~| HELP the following enum variants are available
|
||||||
|
|
||||||
|
let _: Bar = Bar(0);
|
||||||
|
//~^ ERROR expected function
|
||||||
|
//~| HELP try to construct one of the enum's variants
|
||||||
|
//~| HELP you might have meant to construct one of the enum's non-tuple variants
|
||||||
|
|
||||||
|
let _: Bar = Bar.C(0);
|
||||||
|
//~^ ERROR expected value, found enum `Bar`
|
||||||
|
//~| HELP use the path separator to refer to a variant
|
||||||
|
|
||||||
|
let _: Bar = Bar.E;
|
||||||
|
//~^ ERROR expected value, found enum `Bar`
|
||||||
|
//~| HELP use the path separator to refer to a variant
|
||||||
|
|
||||||
|
let _: Bar = Bar.Bad(0);
|
||||||
|
//~^ ERROR expected value, found enum `Bar`
|
||||||
|
//~| HELP you might have meant to use one of the following enum variants
|
||||||
|
//~| HELP alternatively, the following enum variants are also available
|
||||||
|
|
||||||
|
let _: Bar = Bar.Bad;
|
||||||
|
//~^ ERROR expected value, found enum `Bar`
|
||||||
|
//~| HELP you might have meant to use one of the following enum variants
|
||||||
|
//~| HELP alternatively, the following enum variants are also available
|
||||||
|
|
||||||
|
match Foo::A(42) {
|
||||||
|
A(..) => {}
|
||||||
|
//~^ ERROR cannot find tuple struct or tuple variant `A` in this scope
|
||||||
|
Foo(..) => {}
|
||||||
|
//~^ ERROR expected tuple struct or tuple variant
|
||||||
|
//~| HELP try to match against one of the enum's variants
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
225
tests/ui/resolve/enum-expected-value-suggest-variants.stderr
Normal file
225
tests/ui/resolve/enum-expected-value-suggest-variants.stderr
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
error[E0423]: expected value, found enum `Foo`
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:19:18
|
||||||
|
|
|
||||||
|
LL | let _: Foo = Foo.A(0);
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
note: the enum is defined here
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:1:1
|
||||||
|
|
|
||||||
|
LL | / enum Foo {
|
||||||
|
LL | |
|
||||||
|
LL | | A(u32),
|
||||||
|
LL | | B(u32),
|
||||||
|
LL | | }
|
||||||
|
| |_^
|
||||||
|
help: use the path separator to refer to a variant
|
||||||
|
|
|
||||||
|
LL | let _: Foo = Foo::A(0);
|
||||||
|
| ~~
|
||||||
|
|
||||||
|
error[E0423]: expected value, found enum `Foo`
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:23:18
|
||||||
|
|
|
||||||
|
LL | let _: Foo = Foo.Bad(0);
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
note: the enum is defined here
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:1:1
|
||||||
|
|
|
||||||
|
LL | / enum Foo {
|
||||||
|
LL | |
|
||||||
|
LL | | A(u32),
|
||||||
|
LL | | B(u32),
|
||||||
|
LL | | }
|
||||||
|
| |_^
|
||||||
|
help: the following enum variants are available
|
||||||
|
|
|
||||||
|
LL | let _: Foo = (Foo::A(/* fields */)).Bad(0);
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
LL | let _: Foo = (Foo::B(/* fields */)).Bad(0);
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error[E0423]: expected value, found enum `Bar`
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:32:18
|
||||||
|
|
|
||||||
|
LL | let _: Bar = Bar.C(0);
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
note: the enum is defined here
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:7:1
|
||||||
|
|
|
||||||
|
LL | / enum Bar {
|
||||||
|
LL | | C(u32),
|
||||||
|
LL | | D(u32),
|
||||||
|
LL | | E,
|
||||||
|
LL | | F,
|
||||||
|
LL | | }
|
||||||
|
| |_^
|
||||||
|
help: use the path separator to refer to a variant
|
||||||
|
|
|
||||||
|
LL | let _: Bar = Bar::C(0);
|
||||||
|
| ~~
|
||||||
|
|
||||||
|
error[E0423]: expected value, found enum `Bar`
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:36:18
|
||||||
|
|
|
||||||
|
LL | let _: Bar = Bar.E;
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
note: the enum is defined here
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:7:1
|
||||||
|
|
|
||||||
|
LL | / enum Bar {
|
||||||
|
LL | | C(u32),
|
||||||
|
LL | | D(u32),
|
||||||
|
LL | | E,
|
||||||
|
LL | | F,
|
||||||
|
LL | | }
|
||||||
|
| |_^
|
||||||
|
help: use the path separator to refer to a variant
|
||||||
|
|
|
||||||
|
LL | let _: Bar = Bar::E;
|
||||||
|
| ~~
|
||||||
|
|
||||||
|
error[E0423]: expected value, found enum `Bar`
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:40:18
|
||||||
|
|
|
||||||
|
LL | let _: Bar = Bar.Bad(0);
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
note: the enum is defined here
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:7:1
|
||||||
|
|
|
||||||
|
LL | / enum Bar {
|
||||||
|
LL | | C(u32),
|
||||||
|
LL | | D(u32),
|
||||||
|
LL | | E,
|
||||||
|
LL | | F,
|
||||||
|
LL | | }
|
||||||
|
| |_^
|
||||||
|
help: you might have meant to use one of the following enum variants
|
||||||
|
|
|
||||||
|
LL | let _: Bar = Bar::E.Bad(0);
|
||||||
|
| ~~~~~~
|
||||||
|
LL | let _: Bar = Bar::F.Bad(0);
|
||||||
|
| ~~~~~~
|
||||||
|
help: alternatively, the following enum variants are also available
|
||||||
|
|
|
||||||
|
LL | let _: Bar = (Bar::C(/* fields */)).Bad(0);
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
LL | let _: Bar = (Bar::D(/* fields */)).Bad(0);
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error[E0423]: expected value, found enum `Bar`
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:45:18
|
||||||
|
|
|
||||||
|
LL | let _: Bar = Bar.Bad;
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
note: the enum is defined here
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:7:1
|
||||||
|
|
|
||||||
|
LL | / enum Bar {
|
||||||
|
LL | | C(u32),
|
||||||
|
LL | | D(u32),
|
||||||
|
LL | | E,
|
||||||
|
LL | | F,
|
||||||
|
LL | | }
|
||||||
|
| |_^
|
||||||
|
help: you might have meant to use one of the following enum variants
|
||||||
|
|
|
||||||
|
LL | let _: Bar = Bar::E.Bad;
|
||||||
|
| ~~~~~~
|
||||||
|
LL | let _: Bar = Bar::F.Bad;
|
||||||
|
| ~~~~~~
|
||||||
|
help: alternatively, the following enum variants are also available
|
||||||
|
|
|
||||||
|
LL | let _: Bar = (Bar::C(/* fields */)).Bad;
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
LL | let _: Bar = (Bar::D(/* fields */)).Bad;
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error[E0531]: cannot find tuple struct or tuple variant `A` in this scope
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:51:9
|
||||||
|
|
|
||||||
|
LL | A(..) => {}
|
||||||
|
| ^ not found in this scope
|
||||||
|
|
|
||||||
|
help: consider importing this tuple variant
|
||||||
|
|
|
||||||
|
LL + use Foo::A;
|
||||||
|
|
|
||||||
|
|
||||||
|
error[E0532]: expected tuple struct or tuple variant, found enum `Foo`
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:53:9
|
||||||
|
|
|
||||||
|
LL | Foo(..) => {}
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
note: the enum is defined here
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:1:1
|
||||||
|
|
|
||||||
|
LL | / enum Foo {
|
||||||
|
LL | |
|
||||||
|
LL | | A(u32),
|
||||||
|
LL | | B(u32),
|
||||||
|
LL | | }
|
||||||
|
| |_^
|
||||||
|
help: try to match against one of the enum's variants
|
||||||
|
|
|
||||||
|
LL | Foo::A(..) => {}
|
||||||
|
| ~~~~~~
|
||||||
|
LL | Foo::B(..) => {}
|
||||||
|
| ~~~~~~
|
||||||
|
|
||||||
|
error[E0423]: expected function, tuple struct or tuple variant, found enum `Foo`
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:15:18
|
||||||
|
|
|
||||||
|
LL | let _: Foo = Foo(0);
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
note: the enum is defined here
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:1:1
|
||||||
|
|
|
||||||
|
LL | / enum Foo {
|
||||||
|
LL | |
|
||||||
|
LL | | A(u32),
|
||||||
|
LL | | B(u32),
|
||||||
|
LL | | }
|
||||||
|
| |_^
|
||||||
|
help: try to construct one of the enum's variants
|
||||||
|
|
|
||||||
|
LL | let _: Foo = Foo::A(0);
|
||||||
|
| ~~~~~~
|
||||||
|
LL | let _: Foo = Foo::B(0);
|
||||||
|
| ~~~~~~
|
||||||
|
|
||||||
|
error[E0423]: expected function, tuple struct or tuple variant, found enum `Bar`
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:27:18
|
||||||
|
|
|
||||||
|
LL | let _: Bar = Bar(0);
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
= help: you might have meant to construct one of the enum's non-tuple variants
|
||||||
|
note: the enum is defined here
|
||||||
|
--> $DIR/enum-expected-value-suggest-variants.rs:7:1
|
||||||
|
|
|
||||||
|
LL | / enum Bar {
|
||||||
|
LL | | C(u32),
|
||||||
|
LL | | D(u32),
|
||||||
|
LL | | E,
|
||||||
|
LL | | F,
|
||||||
|
LL | | }
|
||||||
|
| |_^
|
||||||
|
help: try to construct one of the enum's variants
|
||||||
|
|
|
||||||
|
LL | let _: Bar = Bar::C(0);
|
||||||
|
| ~~~~~~
|
||||||
|
LL | let _: Bar = Bar::D(0);
|
||||||
|
| ~~~~~~
|
||||||
|
|
||||||
|
error: aborting due to 10 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0423, E0531, E0532.
|
||||||
|
For more information about an error, try `rustc --explain E0423`.
|
Loading…
Add table
Add a link
Reference in a new issue