suggest wrapping single-expr blocks in square brackets
This commit is contained in:
parent
7941b3f147
commit
91ac9cf595
8 changed files with 221 additions and 90 deletions
|
@ -1919,17 +1919,13 @@ impl<'a> Parser<'a> {
|
||||||
match snapshot.parse_array_or_repeat_expr(attrs, token::Brace) {
|
match snapshot.parse_array_or_repeat_expr(attrs, token::Brace) {
|
||||||
Ok(arr) => {
|
Ok(arr) => {
|
||||||
let hi = snapshot.prev_token.span;
|
let hi = snapshot.prev_token.span;
|
||||||
self.struct_span_err(
|
self.struct_span_err(arr.span, "this is a block expression, not an array")
|
||||||
arr.span,
|
.multipart_suggestion(
|
||||||
"this code is interpreted as a block expression, not an array",
|
"to make an array, use square brackets instead of curly braces",
|
||||||
)
|
vec![(lo, "[".to_owned()), (hi, "]".to_owned())],
|
||||||
.multipart_suggestion(
|
Applicability::MaybeIncorrect,
|
||||||
"try using [] instead of {}",
|
)
|
||||||
vec![(lo, "[".to_owned()), (hi, "]".to_owned())],
|
.emit();
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
)
|
|
||||||
.note("to define an array, one would use square brackets instead of curly braces")
|
|
||||||
.emit();
|
|
||||||
|
|
||||||
self.restore_snapshot(snapshot);
|
self.restore_snapshot(snapshot);
|
||||||
Some(self.mk_expr_err(arr.span))
|
Some(self.mk_expr_err(arr.span))
|
||||||
|
|
|
@ -39,6 +39,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
self.suggest_no_capture_closure(err, expected, expr_ty);
|
self.suggest_no_capture_closure(err, expected, expr_ty);
|
||||||
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
|
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
|
||||||
self.suggest_missing_parentheses(err, expr);
|
self.suggest_missing_parentheses(err, expr);
|
||||||
|
self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected);
|
||||||
self.note_need_for_fn_pointer(err, expected, expr_ty);
|
self.note_need_for_fn_pointer(err, expected, expr_ty);
|
||||||
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
|
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
|
||||||
self.report_closure_inferred_return_type(err, expected);
|
self.report_closure_inferred_return_type(err, expected);
|
||||||
|
|
|
@ -774,57 +774,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let prev_diverges = self.diverges.get();
|
let prev_diverges = self.diverges.get();
|
||||||
let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false };
|
let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false };
|
||||||
|
|
||||||
let (ctxt, ()) =
|
let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || {
|
||||||
self.with_breakable_ctxt(blk.hir_id, ctxt, || {
|
for (pos, s) in blk.stmts.iter().enumerate() {
|
||||||
for (pos, s) in blk.stmts.iter().enumerate() {
|
self.check_stmt(s, blk.stmts.len() - 1 == pos);
|
||||||
self.check_stmt(s, blk.stmts.len() - 1 == pos);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// check the tail expression **without** holding the
|
// check the tail expression **without** holding the
|
||||||
// `enclosing_breakables` lock below.
|
// `enclosing_breakables` lock below.
|
||||||
let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected));
|
let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected));
|
||||||
|
|
||||||
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
|
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
|
||||||
let ctxt = enclosing_breakables.find_breakable(blk.hir_id);
|
let ctxt = enclosing_breakables.find_breakable(blk.hir_id);
|
||||||
let coerce = ctxt.coerce.as_mut().unwrap();
|
let coerce = ctxt.coerce.as_mut().unwrap();
|
||||||
if let Some(tail_expr_ty) = tail_expr_ty {
|
if let Some(tail_expr_ty) = tail_expr_ty {
|
||||||
let tail_expr = tail_expr.unwrap();
|
let tail_expr = tail_expr.unwrap();
|
||||||
let span = self.get_expr_coercion_span(tail_expr);
|
let span = self.get_expr_coercion_span(tail_expr);
|
||||||
let cause =
|
let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
|
||||||
self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
|
let ty_for_diagnostic = coerce.merged_ty();
|
||||||
coerce.coerce(self, &cause, tail_expr, tail_expr_ty);
|
// We use coerce_inner here because we want to augment the error
|
||||||
} else {
|
// suggesting to wrap the block in square brackets if it might've
|
||||||
// Subtle: if there is no explicit tail expression,
|
// been mistaken array syntax
|
||||||
// that is typically equivalent to a tail expression
|
coerce.coerce_inner(
|
||||||
// of `()` -- except if the block diverges. In that
|
self,
|
||||||
// case, there is no value supplied from the tail
|
&cause,
|
||||||
// expression (assuming there are no other breaks,
|
Some(tail_expr),
|
||||||
// this implies that the type of the block will be
|
tail_expr_ty,
|
||||||
// `!`).
|
Some(&mut |diag: &mut Diagnostic| {
|
||||||
//
|
self.suggest_block_to_brackets(diag, blk, tail_expr_ty, ty_for_diagnostic);
|
||||||
// #41425 -- label the implicit `()` as being the
|
}),
|
||||||
// "found type" here, rather than the "expected type".
|
false,
|
||||||
if !self.diverges.get().is_always() {
|
);
|
||||||
// #50009 -- Do not point at the entire fn block span, point at the return type
|
} else {
|
||||||
// span, as it is the cause of the requirement, and
|
// Subtle: if there is no explicit tail expression,
|
||||||
// `consider_hint_about_removing_semicolon` will point at the last expression
|
// that is typically equivalent to a tail expression
|
||||||
// if it were a relevant part of the error. This improves usability in editors
|
// of `()` -- except if the block diverges. In that
|
||||||
// that highlight errors inline.
|
// case, there is no value supplied from the tail
|
||||||
let mut sp = blk.span;
|
// expression (assuming there are no other breaks,
|
||||||
let mut fn_span = None;
|
// this implies that the type of the block will be
|
||||||
if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) {
|
// `!`).
|
||||||
let ret_sp = decl.output.span();
|
//
|
||||||
if let Some(block_sp) = self.parent_item_span(blk.hir_id) {
|
// #41425 -- label the implicit `()` as being the
|
||||||
// HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the
|
// "found type" here, rather than the "expected type".
|
||||||
// output would otherwise be incorrect and even misleading. Make sure
|
if !self.diverges.get().is_always() {
|
||||||
// the span we're aiming at correspond to a `fn` body.
|
// #50009 -- Do not point at the entire fn block span, point at the return type
|
||||||
if block_sp == blk.span {
|
// span, as it is the cause of the requirement, and
|
||||||
sp = ret_sp;
|
// `consider_hint_about_removing_semicolon` will point at the last expression
|
||||||
fn_span = Some(ident.span);
|
// if it were a relevant part of the error. This improves usability in editors
|
||||||
}
|
// that highlight errors inline.
|
||||||
|
let mut sp = blk.span;
|
||||||
|
let mut fn_span = None;
|
||||||
|
if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) {
|
||||||
|
let ret_sp = decl.output.span();
|
||||||
|
if let Some(block_sp) = self.parent_item_span(blk.hir_id) {
|
||||||
|
// HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the
|
||||||
|
// output would otherwise be incorrect and even misleading. Make sure
|
||||||
|
// the span we're aiming at correspond to a `fn` body.
|
||||||
|
if block_sp == blk.span {
|
||||||
|
sp = ret_sp;
|
||||||
|
fn_span = Some(ident.span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
coerce.coerce_forced_unit(
|
}
|
||||||
|
coerce.coerce_forced_unit(
|
||||||
self,
|
self,
|
||||||
&self.misc(sp),
|
&self.misc(sp),
|
||||||
&mut |err| {
|
&mut |err| {
|
||||||
|
@ -837,21 +848,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
// Our block must be a `assign desugar local; assignment`
|
// Our block must be a `assign desugar local; assignment`
|
||||||
if let Some(hir::Node::Block(hir::Block {
|
if let Some(hir::Node::Block(hir::Block {
|
||||||
stmts:
|
stmts:
|
||||||
[hir::Stmt {
|
[
|
||||||
kind:
|
hir::Stmt {
|
||||||
hir::StmtKind::Local(hir::Local {
|
kind:
|
||||||
source: hir::LocalSource::AssignDesugar(_),
|
hir::StmtKind::Local(hir::Local {
|
||||||
..
|
source:
|
||||||
}),
|
hir::LocalSource::AssignDesugar(_),
|
||||||
..
|
..
|
||||||
}, hir::Stmt {
|
}),
|
||||||
kind:
|
..
|
||||||
hir::StmtKind::Expr(hir::Expr {
|
},
|
||||||
kind: hir::ExprKind::Assign(..),
|
hir::Stmt {
|
||||||
..
|
kind:
|
||||||
}),
|
hir::StmtKind::Expr(hir::Expr {
|
||||||
..
|
kind: hir::ExprKind::Assign(..),
|
||||||
}],
|
..
|
||||||
|
}),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
],
|
||||||
..
|
..
|
||||||
})) = self.tcx.hir().find(blk.hir_id)
|
})) = self.tcx.hir().find(blk.hir_id)
|
||||||
{
|
{
|
||||||
|
@ -871,9 +886,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if ctxt.may_break {
|
if ctxt.may_break {
|
||||||
// If we can break from the block, then the block's exit is always reachable
|
// If we can break from the block, then the block's exit is always reachable
|
||||||
|
|
|
@ -757,6 +757,77 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Given an expression type mismatch, peel any `&` expressions until we get to
|
||||||
|
/// a block expression, and then suggest replacing the braces with square braces
|
||||||
|
/// if it was possibly mistaken array syntax.
|
||||||
|
pub(crate) fn suggest_block_to_brackets_peeling_refs(
|
||||||
|
&self,
|
||||||
|
diag: &mut Diagnostic,
|
||||||
|
mut expr: &hir::Expr<'_>,
|
||||||
|
mut expr_ty: Ty<'tcx>,
|
||||||
|
mut expected_ty: Ty<'tcx>,
|
||||||
|
) {
|
||||||
|
loop {
|
||||||
|
match (&expr.kind, expr_ty.kind(), expected_ty.kind()) {
|
||||||
|
(
|
||||||
|
hir::ExprKind::AddrOf(_, _, inner_expr),
|
||||||
|
ty::Ref(_, inner_expr_ty, _),
|
||||||
|
ty::Ref(_, inner_expected_ty, _),
|
||||||
|
) => {
|
||||||
|
expr = *inner_expr;
|
||||||
|
expr_ty = *inner_expr_ty;
|
||||||
|
expected_ty = *inner_expected_ty;
|
||||||
|
}
|
||||||
|
(hir::ExprKind::Block(blk, _), _, _) => {
|
||||||
|
self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Suggest wrapping the block in square brackets instead of curly braces
|
||||||
|
/// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
|
||||||
|
pub(crate) fn suggest_block_to_brackets(
|
||||||
|
&self,
|
||||||
|
diag: &mut Diagnostic,
|
||||||
|
blk: &hir::Block<'_>,
|
||||||
|
blk_ty: Ty<'tcx>,
|
||||||
|
expected_ty: Ty<'tcx>,
|
||||||
|
) {
|
||||||
|
if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() {
|
||||||
|
if self.can_coerce(blk_ty, *elem_ty)
|
||||||
|
&& blk.stmts.is_empty()
|
||||||
|
&& blk.rules == hir::BlockCheckMode::DefaultBlock
|
||||||
|
{
|
||||||
|
let source_map = self.tcx.sess.source_map();
|
||||||
|
if let Ok(snippet) = source_map.span_to_snippet(blk.span) {
|
||||||
|
if snippet.starts_with('{') && snippet.ends_with('}') {
|
||||||
|
diag.multipart_suggestion_verbose(
|
||||||
|
"to create an array, use square brackets instead of curly braces",
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
blk.span
|
||||||
|
.shrink_to_lo()
|
||||||
|
.with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)),
|
||||||
|
"[".to_string(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
blk.span
|
||||||
|
.shrink_to_hi()
|
||||||
|
.with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)),
|
||||||
|
"]".to_string(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn is_loop(&self, id: hir::HirId) -> bool {
|
fn is_loop(&self, id: hir::HirId) -> bool {
|
||||||
let node = self.tcx.hir().get(id);
|
let node = self.tcx.hir().get(id);
|
||||||
matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
|
matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
const A: [&str; 1] = { "hello" };
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
|
||||||
|
const B: &[u32] = &{ 1 };
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
|
||||||
|
const C: &&[u32; 1] = &&{ 1 };
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,38 @@
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/brackets-to-braces-single-element.rs:1:24
|
||||||
|
|
|
||||||
|
LL | const A: [&str; 1] = { "hello" };
|
||||||
|
| ^^^^^^^ expected array `[&'static str; 1]`, found `&str`
|
||||||
|
|
|
||||||
|
help: to create an array, use square brackets instead of curly braces
|
||||||
|
|
|
||||||
|
LL | const A: [&str; 1] = [ "hello" ];
|
||||||
|
| ~ ~
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/brackets-to-braces-single-element.rs:4:19
|
||||||
|
|
|
||||||
|
LL | const B: &[u32] = &{ 1 };
|
||||||
|
| ^^^^^^ expected slice `[u32]`, found integer
|
||||||
|
|
|
||||||
|
= note: expected reference `&'static [u32]`
|
||||||
|
found reference `&{integer}`
|
||||||
|
help: to create an array, use square brackets instead of curly braces
|
||||||
|
|
|
||||||
|
LL | const B: &[u32] = &[ 1 ];
|
||||||
|
| ~ ~
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/brackets-to-braces-single-element.rs:7:27
|
||||||
|
|
|
||||||
|
LL | const C: &&[u32; 1] = &&{ 1 };
|
||||||
|
| ^ expected array `[u32; 1]`, found integer
|
||||||
|
|
|
||||||
|
help: to create an array, use square brackets instead of curly braces
|
||||||
|
|
|
||||||
|
LL | const C: &&[u32; 1] = &&[ 1 ];
|
||||||
|
| ~ ~
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
|
@ -1,15 +1,16 @@
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
||||||
const FOO: [u8; 3] = { //~ ERROR this code is interpreted as a block expression
|
const FOO: [u8; 3] = {
|
||||||
|
//~^ ERROR this is a block expression, not an array
|
||||||
1, 2, 3
|
1, 2, 3
|
||||||
};
|
};
|
||||||
|
|
||||||
const BAR: [&str; 3] = {"one", "two", "three"};
|
const BAR: [&str; 3] = {"one", "two", "three"};
|
||||||
//~^ ERROR this code is interpreted as a block expression
|
//~^ ERROR this is a block expression, not an array
|
||||||
|
|
||||||
fn foo() {
|
fn foo() {
|
||||||
{1, 2, 3};
|
{1, 2, 3};
|
||||||
//~^ ERROR this code is interpreted as a block expression
|
//~^ ERROR this is a block expression, not an array
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bar() {
|
fn bar() {
|
||||||
|
|
|
@ -1,46 +1,45 @@
|
||||||
error: this code is interpreted as a block expression, not an array
|
error: this is a block expression, not an array
|
||||||
--> $DIR/issue-87830-try-brackets-for-arrays.rs:3:22
|
--> $DIR/issue-87830-try-brackets-for-arrays.rs:3:22
|
||||||
|
|
|
|
||||||
LL | const FOO: [u8; 3] = {
|
LL | const FOO: [u8; 3] = {
|
||||||
| ______________________^
|
| ______________________^
|
||||||
|
LL | |
|
||||||
LL | | 1, 2, 3
|
LL | | 1, 2, 3
|
||||||
LL | | };
|
LL | | };
|
||||||
| |_^
|
| |_^
|
||||||
|
|
|
|
||||||
= note: to define an array, one would use square brackets instead of curly braces
|
help: to make an array, use square brackets instead of curly braces
|
||||||
help: try using [] instead of {}
|
|
||||||
|
|
|
|
||||||
LL ~ const FOO: [u8; 3] = [
|
LL ~ const FOO: [u8; 3] = [
|
||||||
|
LL |
|
||||||
LL | 1, 2, 3
|
LL | 1, 2, 3
|
||||||
LL ~ ];
|
LL ~ ];
|
||||||
|
|
|
|
||||||
|
|
||||||
error: this code is interpreted as a block expression, not an array
|
error: this is a block expression, not an array
|
||||||
--> $DIR/issue-87830-try-brackets-for-arrays.rs:7:24
|
--> $DIR/issue-87830-try-brackets-for-arrays.rs:8:24
|
||||||
|
|
|
|
||||||
LL | const BAR: [&str; 3] = {"one", "two", "three"};
|
LL | const BAR: [&str; 3] = {"one", "two", "three"};
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: to define an array, one would use square brackets instead of curly braces
|
help: to make an array, use square brackets instead of curly braces
|
||||||
help: try using [] instead of {}
|
|
||||||
|
|
|
|
||||||
LL | const BAR: [&str; 3] = ["one", "two", "three"];
|
LL | const BAR: [&str; 3] = ["one", "two", "three"];
|
||||||
| ~ ~
|
| ~ ~
|
||||||
|
|
||||||
error: this code is interpreted as a block expression, not an array
|
error: this is a block expression, not an array
|
||||||
--> $DIR/issue-87830-try-brackets-for-arrays.rs:11:5
|
--> $DIR/issue-87830-try-brackets-for-arrays.rs:12:5
|
||||||
|
|
|
|
||||||
LL | {1, 2, 3};
|
LL | {1, 2, 3};
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: to define an array, one would use square brackets instead of curly braces
|
help: to make an array, use square brackets instead of curly braces
|
||||||
help: try using [] instead of {}
|
|
||||||
|
|
|
|
||||||
LL | [1, 2, 3];
|
LL | [1, 2, 3];
|
||||||
| ~ ~
|
| ~ ~
|
||||||
|
|
||||||
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,`
|
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,`
|
||||||
--> $DIR/issue-87830-try-brackets-for-arrays.rs:16:6
|
--> $DIR/issue-87830-try-brackets-for-arrays.rs:17:6
|
||||||
|
|
|
|
||||||
LL | 1, 2, 3
|
LL | 1, 2, 3
|
||||||
| ^ expected one of `.`, `;`, `?`, `}`, or an operator
|
| ^ expected one of `.`, `;`, `?`, `}`, or an operator
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue