Skip to content

Commit 7ee3fd2

Browse files
authored
Rollup merge of #106620 - estebank:issue-82051, r=davidtwco
Detect struct literal needing parentheses Fix #82051.
2 parents ef4046e + 5311938 commit 7ee3fd2

File tree

8 files changed

+86
-15
lines changed

8 files changed

+86
-15
lines changed

compiler/rustc_error_messages/locales/en-US/parse.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ parse_struct_literal_body_without_path =
22
struct literal body without path
33
.suggestion = you might have forgotten to add the struct literal inside the block
44
5+
parse_struct_literal_needing_parens =
6+
invalid struct literal
7+
.suggestion = you might need to surround the struct literal in parentheses
8+
59
parse_maybe_report_ambiguous_plus =
610
ambiguous `+` in a type
711
.suggestion = use parentheses to disambiguate

compiler/rustc_parse/src/errors.rs

+18
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,24 @@ pub(crate) struct StructLiteralBodyWithoutPathSugg {
970970
pub after: Span,
971971
}
972972

973+
#[derive(Diagnostic)]
974+
#[diag(parse_struct_literal_needing_parens)]
975+
pub(crate) struct StructLiteralNeedingParens {
976+
#[primary_span]
977+
pub span: Span,
978+
#[subdiagnostic]
979+
pub sugg: StructLiteralNeedingParensSugg,
980+
}
981+
982+
#[derive(Subdiagnostic)]
983+
#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
984+
pub(crate) struct StructLiteralNeedingParensSugg {
985+
#[suggestion_part(code = "(")]
986+
pub before: Span,
987+
#[suggestion_part(code = ")")]
988+
pub after: Span,
989+
}
990+
973991
#[derive(Diagnostic)]
974992
#[diag(parse_unmatched_angle_brackets)]
975993
pub(crate) struct UnmatchedAngleBrackets {

compiler/rustc_parse/src/parser/diagnostics.rs

+26-11
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ use crate::errors::{
1212
IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, ParenthesesInForHead,
1313
ParenthesesInForHeadSugg, PatternMethodParamWithoutBody, QuestionMarkInType,
1414
QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath,
15-
StructLiteralBodyWithoutPathSugg, SuggEscapeToUseAsIdentifier, SuggRemoveComma,
16-
UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
17-
UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead,
15+
StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, StructLiteralNeedingParensSugg,
16+
SuggEscapeToUseAsIdentifier, SuggRemoveComma, UnexpectedConstInGenericParam,
17+
UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets,
18+
UseEqInstead,
1819
};
1920

2021
use crate::lexer::UnmatchedBrace;
@@ -623,12 +624,15 @@ impl<'a> Parser<'a> {
623624
&mut self,
624625
lo: Span,
625626
s: BlockCheckMode,
627+
maybe_struct_name: token::Token,
628+
can_be_struct_literal: bool,
626629
) -> Option<PResult<'a, P<Block>>> {
627630
if self.token.is_ident() && self.look_ahead(1, |t| t == &token::Colon) {
628631
// We might be having a struct literal where people forgot to include the path:
629632
// fn foo() -> Foo {
630633
// field: value,
631634
// }
635+
info!(?maybe_struct_name, ?self.token);
632636
let mut snapshot = self.create_snapshot_for_diagnostic();
633637
let path = Path {
634638
segments: ThinVec::new(),
@@ -648,21 +652,32 @@ impl<'a> Parser<'a> {
648652
// field: value,
649653
// } }
650654
err.delay_as_bug();
651-
self.sess.emit_err(StructLiteralBodyWithoutPath {
652-
span: expr.span,
653-
sugg: StructLiteralBodyWithoutPathSugg {
654-
before: expr.span.shrink_to_lo(),
655-
after: expr.span.shrink_to_hi(),
656-
},
657-
});
658655
self.restore_snapshot(snapshot);
659656
let mut tail = self.mk_block(
660657
vec![self.mk_stmt_err(expr.span)],
661658
s,
662659
lo.to(self.prev_token.span),
663660
);
664661
tail.could_be_bare_literal = true;
665-
Ok(tail)
662+
if maybe_struct_name.is_ident() && can_be_struct_literal {
663+
// Account for `if Example { a: one(), }.is_pos() {}`.
664+
Err(self.sess.create_err(StructLiteralNeedingParens {
665+
span: maybe_struct_name.span.to(expr.span),
666+
sugg: StructLiteralNeedingParensSugg {
667+
before: maybe_struct_name.span.shrink_to_lo(),
668+
after: expr.span.shrink_to_hi(),
669+
},
670+
}))
671+
} else {
672+
self.sess.emit_err(StructLiteralBodyWithoutPath {
673+
span: expr.span,
674+
sugg: StructLiteralBodyWithoutPathSugg {
675+
before: expr.span.shrink_to_lo(),
676+
after: expr.span.shrink_to_hi(),
677+
},
678+
});
679+
Ok(tail)
680+
}
666681
}
667682
(Err(err), Ok(tail)) => {
668683
// We have a block tail that contains a somehow valid type ascription expr.

compiler/rustc_parse/src/parser/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2039,7 +2039,7 @@ impl<'a> Parser<'a> {
20392039
});
20402040
}
20412041

2042-
let (attrs, blk) = self.parse_block_common(lo, blk_mode)?;
2042+
let (attrs, blk) = self.parse_block_common(lo, blk_mode, true)?;
20432043
Ok(self.mk_expr_with_attrs(blk.span, ExprKind::Block(blk, opt_label), attrs))
20442044
}
20452045

compiler/rustc_parse/src/parser/item.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2214,7 +2214,8 @@ impl<'a> Parser<'a> {
22142214
*sig_hi = self.prev_token.span;
22152215
(AttrVec::new(), None)
22162216
} else if self.check(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() {
2217-
self.parse_inner_attrs_and_block().map(|(attrs, body)| (attrs, Some(body)))?
2217+
self.parse_block_common(self.token.span, BlockCheckMode::Default, false)
2218+
.map(|(attrs, body)| (attrs, Some(body)))?
22182219
} else if self.token.kind == token::Eq {
22192220
// Recover `fn foo() = $expr;`.
22202221
self.bump(); // `=`

compiler/rustc_parse/src/parser/stmt.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -498,24 +498,31 @@ impl<'a> Parser<'a> {
498498

499499
/// Parses a block. Inner attributes are allowed.
500500
pub(super) fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (AttrVec, P<Block>)> {
501-
self.parse_block_common(self.token.span, BlockCheckMode::Default)
501+
self.parse_block_common(self.token.span, BlockCheckMode::Default, true)
502502
}
503503

504504
/// Parses a block. Inner attributes are allowed.
505505
pub(super) fn parse_block_common(
506506
&mut self,
507507
lo: Span,
508508
blk_mode: BlockCheckMode,
509+
can_be_struct_literal: bool,
509510
) -> PResult<'a, (AttrVec, P<Block>)> {
510511
maybe_whole!(self, NtBlock, |x| (AttrVec::new(), x));
511512

513+
let maybe_ident = self.prev_token.clone();
512514
self.maybe_recover_unexpected_block_label();
513515
if !self.eat(&token::OpenDelim(Delimiter::Brace)) {
514516
return self.error_block_no_opening_brace();
515517
}
516518

517519
let attrs = self.parse_inner_attributes()?;
518-
let tail = match self.maybe_suggest_struct_literal(lo, blk_mode) {
520+
let tail = match self.maybe_suggest_struct_literal(
521+
lo,
522+
blk_mode,
523+
maybe_ident,
524+
can_be_struct_literal,
525+
) {
519526
Some(tail) => tail?,
520527
None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?,
521528
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
pub struct Example { a: i32 }
2+
3+
impl Example {
4+
fn is_pos(&self) -> bool { self.a > 0 }
5+
}
6+
7+
fn one() -> i32 { 1 }
8+
9+
fn main() {
10+
if Example { a: one(), }.is_pos() { //~ ERROR invalid struct literal
11+
println!("Positive!");
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: invalid struct literal
2+
--> $DIR/method-call-on-struct-literal-in-if-condition.rs:10:8
3+
|
4+
LL | if Example { a: one(), }.is_pos() {
5+
| ^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
help: you might need to surround the struct literal in parentheses
8+
|
9+
LL | if (Example { a: one(), }).is_pos() {
10+
| + +
11+
12+
error: aborting due to previous error
13+

0 commit comments

Comments
 (0)