mirror of
https://github.com/rust-lang/rust
synced 2024-10-19 23:13:41 +00:00
Auto merge of #90677 - bobrippling:suggest-tuple-parens, r=camelid
Suggest tuple-parentheses for enum variants This follows on from #86493 / #86481, making the parentheses suggestion. To summarise, given the following code: ```rust fn f() -> Option<(i32, i8)> { Some(1, 2) } ``` The current output is: ``` error[E0061]: this enum variant takes 1 argument but 2 arguments were supplied --> b.rs:2:5 | 2 | Some(1, 2) | ^^^^ - - supplied 2 arguments | | | expected 1 argument error: aborting due to previous error For more information about this error, try `rustc --explain E0061`. ``` With this change, `rustc` will now suggest parentheses when: - The callee is expecting a single tuple argument - The number of arguments passed matches the element count in the above tuple - The arguments' types match the tuple's fields ``` error[E0061]: this enum variant takes 1 argument but 2 arguments were supplied --> b.rs:2:5 | 2 | Some(1, 2) | ^^^^ - - supplied 2 arguments | help: use parentheses to construct a tuple | 2 | Some((1, 2)) | + + ```
This commit is contained in:
commit
e0e70c0c2c
|
@ -28,6 +28,11 @@
|
|||
use std::iter;
|
||||
use std::slice;
|
||||
|
||||
struct FnArgsAsTuple<'hir> {
|
||||
first: &'hir hir::Expr<'hir>,
|
||||
last: &'hir hir::Expr<'hir>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pub(in super::super) fn check_casts(&self) {
|
||||
let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
|
||||
|
@ -127,8 +132,8 @@ pub(in super::super) fn check_argument_types(
|
|||
|
||||
let expected_arg_count = formal_input_tys.len();
|
||||
|
||||
// expected_count, arg_count, error_code, sugg_unit
|
||||
let mut error: Option<(usize, usize, &str, bool)> = None;
|
||||
// expected_count, arg_count, error_code, sugg_unit, sugg_tuple_wrap_args
|
||||
let mut error: Option<(usize, usize, &str, bool, Option<FnArgsAsTuple<'_>>)> = None;
|
||||
|
||||
// If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
|
||||
let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments {
|
||||
|
@ -138,7 +143,7 @@ pub(in super::super) fn check_argument_types(
|
|||
ty::Tuple(arg_types) => {
|
||||
// Argument length differs
|
||||
if arg_types.len() != provided_args.len() {
|
||||
error = Some((arg_types.len(), provided_args.len(), "E0057", false));
|
||||
error = Some((arg_types.len(), provided_args.len(), "E0057", false, None));
|
||||
}
|
||||
let expected_input_tys = match expected_input_tys.get(0) {
|
||||
Some(&ty) => match ty.kind() {
|
||||
|
@ -169,7 +174,7 @@ pub(in super::super) fn check_argument_types(
|
|||
if supplied_arg_count >= expected_arg_count {
|
||||
(formal_input_tys.to_vec(), expected_input_tys)
|
||||
} else {
|
||||
error = Some((expected_arg_count, supplied_arg_count, "E0060", false));
|
||||
error = Some((expected_arg_count, supplied_arg_count, "E0060", false, None));
|
||||
(self.err_args(supplied_arg_count), vec![])
|
||||
}
|
||||
} else {
|
||||
|
@ -181,7 +186,25 @@ pub(in super::super) fn check_argument_types(
|
|||
} else {
|
||||
false
|
||||
};
|
||||
error = Some((expected_arg_count, supplied_arg_count, "E0061", sugg_unit));
|
||||
|
||||
// are we passing elements of a tuple without the tuple parentheses?
|
||||
let expected_input_tys = if expected_input_tys.is_empty() {
|
||||
// In most cases we can use expected_input_tys, but some callers won't have the type
|
||||
// information, in which case we fall back to the types from the input expressions.
|
||||
formal_input_tys
|
||||
} else {
|
||||
&*expected_input_tys
|
||||
};
|
||||
|
||||
let sugg_tuple_wrap_args = self.suggested_tuple_wrap(expected_input_tys, provided_args);
|
||||
|
||||
error = Some((
|
||||
expected_arg_count,
|
||||
supplied_arg_count,
|
||||
"E0061",
|
||||
sugg_unit,
|
||||
sugg_tuple_wrap_args,
|
||||
));
|
||||
(self.err_args(supplied_arg_count), vec![])
|
||||
};
|
||||
|
||||
|
@ -305,7 +328,8 @@ pub(in super::super) fn check_argument_types(
|
|||
}
|
||||
|
||||
// If there was an error in parameter count, emit that here
|
||||
if let Some((expected_count, arg_count, err_code, sugg_unit)) = error {
|
||||
if let Some((expected_count, arg_count, err_code, sugg_unit, sugg_tuple_wrap_args)) = error
|
||||
{
|
||||
let (span, start_span, args, ctor_of) = match &call_expr.kind {
|
||||
hir::ExprKind::Call(
|
||||
hir::Expr {
|
||||
|
@ -408,6 +432,15 @@ pub(in super::super) fn check_argument_types(
|
|||
String::from("()"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if let Some(FnArgsAsTuple { first, last }) = sugg_tuple_wrap_args {
|
||||
err.multipart_suggestion(
|
||||
"use parentheses to construct a tuple",
|
||||
vec![
|
||||
(first.span.shrink_to_lo(), '('.to_string()),
|
||||
(last.span.shrink_to_hi(), ')'.to_string()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
err.span_label(
|
||||
span,
|
||||
|
@ -457,6 +490,35 @@ fn variadic_error<'tcx>(sess: &Session, span: Span, ty: Ty<'tcx>, cast_ty: &str)
|
|||
}
|
||||
}
|
||||
|
||||
fn suggested_tuple_wrap(
|
||||
&self,
|
||||
expected_input_tys: &[Ty<'tcx>],
|
||||
provided_args: &'tcx [hir::Expr<'tcx>],
|
||||
) -> Option<FnArgsAsTuple<'_>> {
|
||||
let [expected_arg_type] = &expected_input_tys[..] else { return None };
|
||||
|
||||
let ty::Tuple(expected_elems) = self.resolve_vars_if_possible(*expected_arg_type).kind()
|
||||
else { return None };
|
||||
|
||||
let expected_types: Vec<_> = expected_elems.iter().map(|k| k.expect_ty()).collect();
|
||||
let supplied_types: Vec<_> = provided_args.iter().map(|arg| self.check_expr(arg)).collect();
|
||||
|
||||
let all_match = iter::zip(expected_types, supplied_types)
|
||||
.all(|(expected, supplied)| self.can_eq(self.param_env, expected, supplied).is_ok());
|
||||
|
||||
if all_match {
|
||||
match provided_args {
|
||||
[] => None,
|
||||
[_] => unreachable!(
|
||||
"shouldn't reach here - need count mismatch between 1-tuple and 1-argument"
|
||||
),
|
||||
[first, .., last] => Some(FnArgsAsTuple { first, last }),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// AST fragment checking
|
||||
pub(in super::super) fn check_lit(
|
||||
&self,
|
||||
|
|
16
src/test/ui/suggestions/args-instead-of-tuple-errors.rs
Normal file
16
src/test/ui/suggestions/args-instead-of-tuple-errors.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Ensure we don't suggest tuple-wrapping when we'd end up with a type error
|
||||
|
||||
fn main() {
|
||||
// we shouldn't suggest to fix these - `2` isn't a `bool`
|
||||
|
||||
let _: Option<(i32, bool)> = Some(1, 2);
|
||||
//~^ ERROR this enum variant takes 1 argument but 2 arguments were supplied
|
||||
int_bool(1, 2);
|
||||
//~^ ERROR this function takes 1 argument but 2 arguments were supplied
|
||||
|
||||
let _: Option<(i8,)> = Some();
|
||||
//~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
|
||||
}
|
||||
|
||||
fn int_bool(_: (i32, bool)) {
|
||||
}
|
33
src/test/ui/suggestions/args-instead-of-tuple-errors.stderr
Normal file
33
src/test/ui/suggestions/args-instead-of-tuple-errors.stderr
Normal file
|
@ -0,0 +1,33 @@
|
|||
error[E0061]: this enum variant takes 1 argument but 2 arguments were supplied
|
||||
--> $DIR/args-instead-of-tuple-errors.rs:6:34
|
||||
|
|
||||
LL | let _: Option<(i32, bool)> = Some(1, 2);
|
||||
| ^^^^ - - supplied 2 arguments
|
||||
| |
|
||||
| expected 1 argument
|
||||
|
||||
error[E0061]: this function takes 1 argument but 2 arguments were supplied
|
||||
--> $DIR/args-instead-of-tuple-errors.rs:8:5
|
||||
|
|
||||
LL | int_bool(1, 2);
|
||||
| ^^^^^^^^ - - supplied 2 arguments
|
||||
| |
|
||||
| expected 1 argument
|
||||
|
|
||||
note: function defined here
|
||||
--> $DIR/args-instead-of-tuple-errors.rs:15:4
|
||||
|
|
||||
LL | fn int_bool(_: (i32, bool)) {
|
||||
| ^^^^^^^^ --------------
|
||||
|
||||
error[E0061]: this enum variant takes 1 argument but 0 arguments were supplied
|
||||
--> $DIR/args-instead-of-tuple-errors.rs:11:28
|
||||
|
|
||||
LL | let _: Option<(i8,)> = Some();
|
||||
| ^^^^-- supplied 0 arguments
|
||||
| |
|
||||
| expected 1 argument
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0061`.
|
27
src/test/ui/suggestions/args-instead-of-tuple.fixed
Normal file
27
src/test/ui/suggestions/args-instead-of-tuple.fixed
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Test suggesting tuples where bare arguments may have been passed
|
||||
// See issue #86481 for details.
|
||||
|
||||
// run-rustfix
|
||||
|
||||
fn main() {
|
||||
let _: Result<(i32, i8), ()> = Ok((1, 2));
|
||||
//~^ ERROR this enum variant takes 1 argument but 2 arguments were supplied
|
||||
let _: Option<(i32, i8, &'static str)> = Some((1, 2, "hi"));
|
||||
//~^ ERROR this enum variant takes 1 argument but 3 arguments were supplied
|
||||
let _: Option<()> = Some(());
|
||||
//~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
|
||||
|
||||
two_ints((1, 2)); //~ ERROR this function takes 1 argument
|
||||
|
||||
with_generic((3, 4)); //~ ERROR this function takes 1 argument
|
||||
}
|
||||
|
||||
fn two_ints(_: (i32, i32)) {
|
||||
}
|
||||
|
||||
fn with_generic<T: Copy + Send>((a, b): (i32, T)) {
|
||||
if false {
|
||||
// test generics/bound handling
|
||||
with_generic((a, b)); //~ ERROR this function takes 1 argument
|
||||
}
|
||||
}
|
27
src/test/ui/suggestions/args-instead-of-tuple.rs
Normal file
27
src/test/ui/suggestions/args-instead-of-tuple.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Test suggesting tuples where bare arguments may have been passed
|
||||
// See issue #86481 for details.
|
||||
|
||||
// run-rustfix
|
||||
|
||||
fn main() {
|
||||
let _: Result<(i32, i8), ()> = Ok(1, 2);
|
||||
//~^ ERROR this enum variant takes 1 argument but 2 arguments were supplied
|
||||
let _: Option<(i32, i8, &'static str)> = Some(1, 2, "hi");
|
||||
//~^ ERROR this enum variant takes 1 argument but 3 arguments were supplied
|
||||
let _: Option<()> = Some();
|
||||
//~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
|
||||
|
||||
two_ints(1, 2); //~ ERROR this function takes 1 argument
|
||||
|
||||
with_generic(3, 4); //~ ERROR this function takes 1 argument
|
||||
}
|
||||
|
||||
fn two_ints(_: (i32, i32)) {
|
||||
}
|
||||
|
||||
fn with_generic<T: Copy + Send>((a, b): (i32, T)) {
|
||||
if false {
|
||||
// test generics/bound handling
|
||||
with_generic(a, b); //~ ERROR this function takes 1 argument
|
||||
}
|
||||
}
|
84
src/test/ui/suggestions/args-instead-of-tuple.stderr
Normal file
84
src/test/ui/suggestions/args-instead-of-tuple.stderr
Normal file
|
@ -0,0 +1,84 @@
|
|||
error[E0061]: this enum variant takes 1 argument but 2 arguments were supplied
|
||||
--> $DIR/args-instead-of-tuple.rs:7:36
|
||||
|
|
||||
LL | let _: Result<(i32, i8), ()> = Ok(1, 2);
|
||||
| ^^ - - supplied 2 arguments
|
||||
|
|
||||
help: use parentheses to construct a tuple
|
||||
|
|
||||
LL | let _: Result<(i32, i8), ()> = Ok((1, 2));
|
||||
| + +
|
||||
|
||||
error[E0061]: this enum variant takes 1 argument but 3 arguments were supplied
|
||||
--> $DIR/args-instead-of-tuple.rs:9:46
|
||||
|
|
||||
LL | let _: Option<(i32, i8, &'static str)> = Some(1, 2, "hi");
|
||||
| ^^^^ - - ---- supplied 3 arguments
|
||||
|
|
||||
help: use parentheses to construct a tuple
|
||||
|
|
||||
LL | let _: Option<(i32, i8, &'static str)> = Some((1, 2, "hi"));
|
||||
| + +
|
||||
|
||||
error[E0061]: this enum variant takes 1 argument but 0 arguments were supplied
|
||||
--> $DIR/args-instead-of-tuple.rs:11:25
|
||||
|
|
||||
LL | let _: Option<()> = Some();
|
||||
| ^^^^-- supplied 0 arguments
|
||||
|
|
||||
help: expected the unit value `()`; create it with empty parentheses
|
||||
|
|
||||
LL | let _: Option<()> = Some(());
|
||||
| ++
|
||||
|
||||
error[E0061]: this function takes 1 argument but 2 arguments were supplied
|
||||
--> $DIR/args-instead-of-tuple.rs:14:5
|
||||
|
|
||||
LL | two_ints(1, 2);
|
||||
| ^^^^^^^^ - - supplied 2 arguments
|
||||
|
|
||||
note: function defined here
|
||||
--> $DIR/args-instead-of-tuple.rs:19:4
|
||||
|
|
||||
LL | fn two_ints(_: (i32, i32)) {
|
||||
| ^^^^^^^^ -------------
|
||||
help: use parentheses to construct a tuple
|
||||
|
|
||||
LL | two_ints((1, 2));
|
||||
| + +
|
||||
|
||||
error[E0061]: this function takes 1 argument but 2 arguments were supplied
|
||||
--> $DIR/args-instead-of-tuple.rs:16:5
|
||||
|
|
||||
LL | with_generic(3, 4);
|
||||
| ^^^^^^^^^^^^ - - supplied 2 arguments
|
||||
|
|
||||
note: function defined here
|
||||
--> $DIR/args-instead-of-tuple.rs:22:4
|
||||
|
|
||||
LL | fn with_generic<T: Copy + Send>((a, b): (i32, T)) {
|
||||
| ^^^^^^^^^^^^ ----------------
|
||||
help: use parentheses to construct a tuple
|
||||
|
|
||||
LL | with_generic((3, 4));
|
||||
| + +
|
||||
|
||||
error[E0061]: this function takes 1 argument but 2 arguments were supplied
|
||||
--> $DIR/args-instead-of-tuple.rs:25:9
|
||||
|
|
||||
LL | with_generic(a, b);
|
||||
| ^^^^^^^^^^^^ - - supplied 2 arguments
|
||||
|
|
||||
note: function defined here
|
||||
--> $DIR/args-instead-of-tuple.rs:22:4
|
||||
|
|
||||
LL | fn with_generic<T: Copy + Send>((a, b): (i32, T)) {
|
||||
| ^^^^^^^^^^^^ ----------------
|
||||
help: use parentheses to construct a tuple
|
||||
|
|
||||
LL | with_generic((a, b));
|
||||
| + +
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0061`.
|
Loading…
Reference in a new issue