1
Fork 0

Rollup merge of #95293 - compiler-errors:braces, r=davidtwco

suggest wrapping single-expr blocks in square brackets

Suggests a fix in cases like:

```diff
- const A: [i32; 1] = { 1 };

+ const A: [i32; 1] = [ 1 ];
                      ^   ^
```

Also edit the message for the same suggestion in the parser (e.g. `{ 1, 2 }`).

Fixes #95289
This commit is contained in:
Matthias Krüger 2022-04-01 06:59:42 +02:00 committed by GitHub
commit 8f493fd46a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 221 additions and 90 deletions

View file

@ -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))

View file

@ -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);

View file

@ -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

View file

@ -766,6 +766,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(..), .. }))

View file

@ -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() {}

View file

@ -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`.

View file

@ -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() {

View file

@ -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