Skip to content

Commit 8f493fd

Browse files
authored
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
2 parents 94b1960 + 91ac9cf commit 8f493fd

8 files changed

+222
-91
lines changed

compiler/rustc_parse/src/parser/expr.rs

+7-11
Original file line numberDiff line numberDiff line change
@@ -1919,17 +1919,13 @@ impl<'a> Parser<'a> {
19191919
match snapshot.parse_array_or_repeat_expr(attrs, token::Brace) {
19201920
Ok(arr) => {
19211921
let hi = snapshot.prev_token.span;
1922-
self.struct_span_err(
1923-
arr.span,
1924-
"this code is interpreted as a block expression, not an array",
1925-
)
1926-
.multipart_suggestion(
1927-
"try using [] instead of {}",
1928-
vec![(lo, "[".to_owned()), (hi, "]".to_owned())],
1929-
Applicability::MaybeIncorrect,
1930-
)
1931-
.note("to define an array, one would use square brackets instead of curly braces")
1932-
.emit();
1922+
self.struct_span_err(arr.span, "this is a block expression, not an array")
1923+
.multipart_suggestion(
1924+
"to make an array, use square brackets instead of curly braces",
1925+
vec![(lo, "[".to_owned()), (hi, "]".to_owned())],
1926+
Applicability::MaybeIncorrect,
1927+
)
1928+
.emit();
19331929

19341930
self.restore_snapshot(snapshot);
19351931
Some(self.mk_expr_err(arr.span))

compiler/rustc_typeck/src/check/demand.rs

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3939
self.suggest_no_capture_closure(err, expected, expr_ty);
4040
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
4141
self.suggest_missing_parentheses(err, expr);
42+
self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected);
4243
self.note_need_for_fn_pointer(err, expected, expr_ty);
4344
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
4445
self.report_closure_inferred_return_type(err, expected);

compiler/rustc_typeck/src/check/fn_ctxt/checks.rs

+80-65
Original file line numberDiff line numberDiff line change
@@ -774,57 +774,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
774774
let prev_diverges = self.diverges.get();
775775
let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false };
776776

777-
let (ctxt, ()) =
778-
self.with_breakable_ctxt(blk.hir_id, ctxt, || {
779-
for (pos, s) in blk.stmts.iter().enumerate() {
780-
self.check_stmt(s, blk.stmts.len() - 1 == pos);
781-
}
777+
let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || {
778+
for (pos, s) in blk.stmts.iter().enumerate() {
779+
self.check_stmt(s, blk.stmts.len() - 1 == pos);
780+
}
782781

783-
// check the tail expression **without** holding the
784-
// `enclosing_breakables` lock below.
785-
let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected));
786-
787-
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
788-
let ctxt = enclosing_breakables.find_breakable(blk.hir_id);
789-
let coerce = ctxt.coerce.as_mut().unwrap();
790-
if let Some(tail_expr_ty) = tail_expr_ty {
791-
let tail_expr = tail_expr.unwrap();
792-
let span = self.get_expr_coercion_span(tail_expr);
793-
let cause =
794-
self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
795-
coerce.coerce(self, &cause, tail_expr, tail_expr_ty);
796-
} else {
797-
// Subtle: if there is no explicit tail expression,
798-
// that is typically equivalent to a tail expression
799-
// of `()` -- except if the block diverges. In that
800-
// case, there is no value supplied from the tail
801-
// expression (assuming there are no other breaks,
802-
// this implies that the type of the block will be
803-
// `!`).
804-
//
805-
// #41425 -- label the implicit `()` as being the
806-
// "found type" here, rather than the "expected type".
807-
if !self.diverges.get().is_always() {
808-
// #50009 -- Do not point at the entire fn block span, point at the return type
809-
// span, as it is the cause of the requirement, and
810-
// `consider_hint_about_removing_semicolon` will point at the last expression
811-
// if it were a relevant part of the error. This improves usability in editors
812-
// that highlight errors inline.
813-
let mut sp = blk.span;
814-
let mut fn_span = None;
815-
if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) {
816-
let ret_sp = decl.output.span();
817-
if let Some(block_sp) = self.parent_item_span(blk.hir_id) {
818-
// HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the
819-
// output would otherwise be incorrect and even misleading. Make sure
820-
// the span we're aiming at correspond to a `fn` body.
821-
if block_sp == blk.span {
822-
sp = ret_sp;
823-
fn_span = Some(ident.span);
824-
}
782+
// check the tail expression **without** holding the
783+
// `enclosing_breakables` lock below.
784+
let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected));
785+
786+
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
787+
let ctxt = enclosing_breakables.find_breakable(blk.hir_id);
788+
let coerce = ctxt.coerce.as_mut().unwrap();
789+
if let Some(tail_expr_ty) = tail_expr_ty {
790+
let tail_expr = tail_expr.unwrap();
791+
let span = self.get_expr_coercion_span(tail_expr);
792+
let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
793+
let ty_for_diagnostic = coerce.merged_ty();
794+
// We use coerce_inner here because we want to augment the error
795+
// suggesting to wrap the block in square brackets if it might've
796+
// been mistaken array syntax
797+
coerce.coerce_inner(
798+
self,
799+
&cause,
800+
Some(tail_expr),
801+
tail_expr_ty,
802+
Some(&mut |diag: &mut Diagnostic| {
803+
self.suggest_block_to_brackets(diag, blk, tail_expr_ty, ty_for_diagnostic);
804+
}),
805+
false,
806+
);
807+
} else {
808+
// Subtle: if there is no explicit tail expression,
809+
// that is typically equivalent to a tail expression
810+
// of `()` -- except if the block diverges. In that
811+
// case, there is no value supplied from the tail
812+
// expression (assuming there are no other breaks,
813+
// this implies that the type of the block will be
814+
// `!`).
815+
//
816+
// #41425 -- label the implicit `()` as being the
817+
// "found type" here, rather than the "expected type".
818+
if !self.diverges.get().is_always() {
819+
// #50009 -- Do not point at the entire fn block span, point at the return type
820+
// span, as it is the cause of the requirement, and
821+
// `consider_hint_about_removing_semicolon` will point at the last expression
822+
// if it were a relevant part of the error. This improves usability in editors
823+
// that highlight errors inline.
824+
let mut sp = blk.span;
825+
let mut fn_span = None;
826+
if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) {
827+
let ret_sp = decl.output.span();
828+
if let Some(block_sp) = self.parent_item_span(blk.hir_id) {
829+
// HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the
830+
// output would otherwise be incorrect and even misleading. Make sure
831+
// the span we're aiming at correspond to a `fn` body.
832+
if block_sp == blk.span {
833+
sp = ret_sp;
834+
fn_span = Some(ident.span);
825835
}
826836
}
827-
coerce.coerce_forced_unit(
837+
}
838+
coerce.coerce_forced_unit(
828839
self,
829840
&self.misc(sp),
830841
&mut |err| {
@@ -837,21 +848,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
837848
// Our block must be a `assign desugar local; assignment`
838849
if let Some(hir::Node::Block(hir::Block {
839850
stmts:
840-
[hir::Stmt {
841-
kind:
842-
hir::StmtKind::Local(hir::Local {
843-
source: hir::LocalSource::AssignDesugar(_),
844-
..
845-
}),
846-
..
847-
}, hir::Stmt {
848-
kind:
849-
hir::StmtKind::Expr(hir::Expr {
850-
kind: hir::ExprKind::Assign(..),
851-
..
852-
}),
853-
..
854-
}],
851+
[
852+
hir::Stmt {
853+
kind:
854+
hir::StmtKind::Local(hir::Local {
855+
source:
856+
hir::LocalSource::AssignDesugar(_),
857+
..
858+
}),
859+
..
860+
},
861+
hir::Stmt {
862+
kind:
863+
hir::StmtKind::Expr(hir::Expr {
864+
kind: hir::ExprKind::Assign(..),
865+
..
866+
}),
867+
..
868+
},
869+
],
855870
..
856871
})) = self.tcx.hir().find(blk.hir_id)
857872
{
@@ -871,9 +886,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
871886
},
872887
false,
873888
);
874-
}
875889
}
876-
});
890+
}
891+
});
877892

878893
if ctxt.may_break {
879894
// If we can break from the block, then the block's exit is always reachable

compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs

+71
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,77 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
766766
}
767767
}
768768

769+
/// Given an expression type mismatch, peel any `&` expressions until we get to
770+
/// a block expression, and then suggest replacing the braces with square braces
771+
/// if it was possibly mistaken array syntax.
772+
pub(crate) fn suggest_block_to_brackets_peeling_refs(
773+
&self,
774+
diag: &mut Diagnostic,
775+
mut expr: &hir::Expr<'_>,
776+
mut expr_ty: Ty<'tcx>,
777+
mut expected_ty: Ty<'tcx>,
778+
) {
779+
loop {
780+
match (&expr.kind, expr_ty.kind(), expected_ty.kind()) {
781+
(
782+
hir::ExprKind::AddrOf(_, _, inner_expr),
783+
ty::Ref(_, inner_expr_ty, _),
784+
ty::Ref(_, inner_expected_ty, _),
785+
) => {
786+
expr = *inner_expr;
787+
expr_ty = *inner_expr_ty;
788+
expected_ty = *inner_expected_ty;
789+
}
790+
(hir::ExprKind::Block(blk, _), _, _) => {
791+
self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty);
792+
break;
793+
}
794+
_ => break,
795+
}
796+
}
797+
}
798+
799+
/// Suggest wrapping the block in square brackets instead of curly braces
800+
/// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
801+
pub(crate) fn suggest_block_to_brackets(
802+
&self,
803+
diag: &mut Diagnostic,
804+
blk: &hir::Block<'_>,
805+
blk_ty: Ty<'tcx>,
806+
expected_ty: Ty<'tcx>,
807+
) {
808+
if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() {
809+
if self.can_coerce(blk_ty, *elem_ty)
810+
&& blk.stmts.is_empty()
811+
&& blk.rules == hir::BlockCheckMode::DefaultBlock
812+
{
813+
let source_map = self.tcx.sess.source_map();
814+
if let Ok(snippet) = source_map.span_to_snippet(blk.span) {
815+
if snippet.starts_with('{') && snippet.ends_with('}') {
816+
diag.multipart_suggestion_verbose(
817+
"to create an array, use square brackets instead of curly braces",
818+
vec![
819+
(
820+
blk.span
821+
.shrink_to_lo()
822+
.with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)),
823+
"[".to_string(),
824+
),
825+
(
826+
blk.span
827+
.shrink_to_hi()
828+
.with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)),
829+
"]".to_string(),
830+
),
831+
],
832+
Applicability::MachineApplicable,
833+
);
834+
}
835+
}
836+
}
837+
}
838+
}
839+
769840
fn is_loop(&self, id: hir::HirId) -> bool {
770841
let node = self.tcx.hir().get(id);
771842
matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const A: [&str; 1] = { "hello" };
2+
//~^ ERROR mismatched types
3+
4+
const B: &[u32] = &{ 1 };
5+
//~^ ERROR mismatched types
6+
7+
const C: &&[u32; 1] = &&{ 1 };
8+
//~^ ERROR mismatched types
9+
10+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/brackets-to-braces-single-element.rs:1:24
3+
|
4+
LL | const A: [&str; 1] = { "hello" };
5+
| ^^^^^^^ expected array `[&'static str; 1]`, found `&str`
6+
|
7+
help: to create an array, use square brackets instead of curly braces
8+
|
9+
LL | const A: [&str; 1] = [ "hello" ];
10+
| ~ ~
11+
12+
error[E0308]: mismatched types
13+
--> $DIR/brackets-to-braces-single-element.rs:4:19
14+
|
15+
LL | const B: &[u32] = &{ 1 };
16+
| ^^^^^^ expected slice `[u32]`, found integer
17+
|
18+
= note: expected reference `&'static [u32]`
19+
found reference `&{integer}`
20+
help: to create an array, use square brackets instead of curly braces
21+
|
22+
LL | const B: &[u32] = &[ 1 ];
23+
| ~ ~
24+
25+
error[E0308]: mismatched types
26+
--> $DIR/brackets-to-braces-single-element.rs:7:27
27+
|
28+
LL | const C: &&[u32; 1] = &&{ 1 };
29+
| ^ expected array `[u32; 1]`, found integer
30+
|
31+
help: to create an array, use square brackets instead of curly braces
32+
|
33+
LL | const C: &&[u32; 1] = &&[ 1 ];
34+
| ~ ~
35+
36+
error: aborting due to 3 previous errors
37+
38+
For more information about this error, try `rustc --explain E0308`.

src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
fn main() {}
22

3-
const FOO: [u8; 3] = { //~ ERROR this code is interpreted as a block expression
3+
const FOO: [u8; 3] = {
4+
//~^ ERROR this is a block expression, not an array
45
1, 2, 3
56
};
67

78
const BAR: [&str; 3] = {"one", "two", "three"};
8-
//~^ ERROR this code is interpreted as a block expression
9+
//~^ ERROR this is a block expression, not an array
910

1011
fn foo() {
1112
{1, 2, 3};
12-
//~^ ERROR this code is interpreted as a block expression
13+
//~^ ERROR this is a block expression, not an array
1314
}
1415

1516
fn bar() {

0 commit comments

Comments
 (0)