mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 23:04:33 +00:00
Rollup merge of #106620 - estebank:issue-82051, r=davidtwco
Detect struct literal needing parentheses Fix #82051.
This commit is contained in:
commit
7ee3fd2edc
@ -2,6 +2,10 @@ parse_struct_literal_body_without_path =
|
|||||||
struct literal body without path
|
struct literal body without path
|
||||||
.suggestion = you might have forgotten to add the struct literal inside the block
|
.suggestion = you might have forgotten to add the struct literal inside the block
|
||||||
|
|
||||||
|
parse_struct_literal_needing_parens =
|
||||||
|
invalid struct literal
|
||||||
|
.suggestion = you might need to surround the struct literal in parentheses
|
||||||
|
|
||||||
parse_maybe_report_ambiguous_plus =
|
parse_maybe_report_ambiguous_plus =
|
||||||
ambiguous `+` in a type
|
ambiguous `+` in a type
|
||||||
.suggestion = use parentheses to disambiguate
|
.suggestion = use parentheses to disambiguate
|
||||||
|
@ -970,6 +970,24 @@ pub(crate) struct StructLiteralBodyWithoutPathSugg {
|
|||||||
pub after: Span,
|
pub after: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(parse_struct_literal_needing_parens)]
|
||||||
|
pub(crate) struct StructLiteralNeedingParens {
|
||||||
|
#[primary_span]
|
||||||
|
pub span: Span,
|
||||||
|
#[subdiagnostic]
|
||||||
|
pub sugg: StructLiteralNeedingParensSugg,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subdiagnostic)]
|
||||||
|
#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
|
||||||
|
pub(crate) struct StructLiteralNeedingParensSugg {
|
||||||
|
#[suggestion_part(code = "(")]
|
||||||
|
pub before: Span,
|
||||||
|
#[suggestion_part(code = ")")]
|
||||||
|
pub after: Span,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(parse_unmatched_angle_brackets)]
|
#[diag(parse_unmatched_angle_brackets)]
|
||||||
pub(crate) struct UnmatchedAngleBrackets {
|
pub(crate) struct UnmatchedAngleBrackets {
|
||||||
|
@ -12,9 +12,10 @@ use crate::errors::{
|
|||||||
IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, ParenthesesInForHead,
|
IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, ParenthesesInForHead,
|
||||||
ParenthesesInForHeadSugg, PatternMethodParamWithoutBody, QuestionMarkInType,
|
ParenthesesInForHeadSugg, PatternMethodParamWithoutBody, QuestionMarkInType,
|
||||||
QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath,
|
QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath,
|
||||||
StructLiteralBodyWithoutPathSugg, SuggEscapeToUseAsIdentifier, SuggRemoveComma,
|
StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, StructLiteralNeedingParensSugg,
|
||||||
UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
|
SuggEscapeToUseAsIdentifier, SuggRemoveComma, UnexpectedConstInGenericParam,
|
||||||
UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead,
|
UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets,
|
||||||
|
UseEqInstead,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::lexer::UnmatchedBrace;
|
use crate::lexer::UnmatchedBrace;
|
||||||
@ -623,12 +624,15 @@ impl<'a> Parser<'a> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
lo: Span,
|
lo: Span,
|
||||||
s: BlockCheckMode,
|
s: BlockCheckMode,
|
||||||
|
maybe_struct_name: token::Token,
|
||||||
|
can_be_struct_literal: bool,
|
||||||
) -> Option<PResult<'a, P<Block>>> {
|
) -> Option<PResult<'a, P<Block>>> {
|
||||||
if self.token.is_ident() && self.look_ahead(1, |t| t == &token::Colon) {
|
if self.token.is_ident() && self.look_ahead(1, |t| t == &token::Colon) {
|
||||||
// We might be having a struct literal where people forgot to include the path:
|
// We might be having a struct literal where people forgot to include the path:
|
||||||
// fn foo() -> Foo {
|
// fn foo() -> Foo {
|
||||||
// field: value,
|
// field: value,
|
||||||
// }
|
// }
|
||||||
|
info!(?maybe_struct_name, ?self.token);
|
||||||
let mut snapshot = self.create_snapshot_for_diagnostic();
|
let mut snapshot = self.create_snapshot_for_diagnostic();
|
||||||
let path = Path {
|
let path = Path {
|
||||||
segments: ThinVec::new(),
|
segments: ThinVec::new(),
|
||||||
@ -648,13 +652,6 @@ impl<'a> Parser<'a> {
|
|||||||
// field: value,
|
// field: value,
|
||||||
// } }
|
// } }
|
||||||
err.delay_as_bug();
|
err.delay_as_bug();
|
||||||
self.sess.emit_err(StructLiteralBodyWithoutPath {
|
|
||||||
span: expr.span,
|
|
||||||
sugg: StructLiteralBodyWithoutPathSugg {
|
|
||||||
before: expr.span.shrink_to_lo(),
|
|
||||||
after: expr.span.shrink_to_hi(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
self.restore_snapshot(snapshot);
|
self.restore_snapshot(snapshot);
|
||||||
let mut tail = self.mk_block(
|
let mut tail = self.mk_block(
|
||||||
vec![self.mk_stmt_err(expr.span)],
|
vec![self.mk_stmt_err(expr.span)],
|
||||||
@ -662,8 +659,26 @@ impl<'a> Parser<'a> {
|
|||||||
lo.to(self.prev_token.span),
|
lo.to(self.prev_token.span),
|
||||||
);
|
);
|
||||||
tail.could_be_bare_literal = true;
|
tail.could_be_bare_literal = true;
|
||||||
|
if maybe_struct_name.is_ident() && can_be_struct_literal {
|
||||||
|
// Account for `if Example { a: one(), }.is_pos() {}`.
|
||||||
|
Err(self.sess.create_err(StructLiteralNeedingParens {
|
||||||
|
span: maybe_struct_name.span.to(expr.span),
|
||||||
|
sugg: StructLiteralNeedingParensSugg {
|
||||||
|
before: maybe_struct_name.span.shrink_to_lo(),
|
||||||
|
after: expr.span.shrink_to_hi(),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
self.sess.emit_err(StructLiteralBodyWithoutPath {
|
||||||
|
span: expr.span,
|
||||||
|
sugg: StructLiteralBodyWithoutPathSugg {
|
||||||
|
before: expr.span.shrink_to_lo(),
|
||||||
|
after: expr.span.shrink_to_hi(),
|
||||||
|
},
|
||||||
|
});
|
||||||
Ok(tail)
|
Ok(tail)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
(Err(err), Ok(tail)) => {
|
(Err(err), Ok(tail)) => {
|
||||||
// We have a block tail that contains a somehow valid type ascription expr.
|
// We have a block tail that contains a somehow valid type ascription expr.
|
||||||
err.cancel();
|
err.cancel();
|
||||||
|
@ -2039,7 +2039,7 @@ impl<'a> Parser<'a> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let (attrs, blk) = self.parse_block_common(lo, blk_mode)?;
|
let (attrs, blk) = self.parse_block_common(lo, blk_mode, true)?;
|
||||||
Ok(self.mk_expr_with_attrs(blk.span, ExprKind::Block(blk, opt_label), attrs))
|
Ok(self.mk_expr_with_attrs(blk.span, ExprKind::Block(blk, opt_label), attrs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2214,7 +2214,8 @@ impl<'a> Parser<'a> {
|
|||||||
*sig_hi = self.prev_token.span;
|
*sig_hi = self.prev_token.span;
|
||||||
(AttrVec::new(), None)
|
(AttrVec::new(), None)
|
||||||
} else if self.check(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() {
|
} else if self.check(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() {
|
||||||
self.parse_inner_attrs_and_block().map(|(attrs, body)| (attrs, Some(body)))?
|
self.parse_block_common(self.token.span, BlockCheckMode::Default, false)
|
||||||
|
.map(|(attrs, body)| (attrs, Some(body)))?
|
||||||
} else if self.token.kind == token::Eq {
|
} else if self.token.kind == token::Eq {
|
||||||
// Recover `fn foo() = $expr;`.
|
// Recover `fn foo() = $expr;`.
|
||||||
self.bump(); // `=`
|
self.bump(); // `=`
|
||||||
|
@ -498,7 +498,7 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
/// Parses a block. Inner attributes are allowed.
|
/// Parses a block. Inner attributes are allowed.
|
||||||
pub(super) fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (AttrVec, P<Block>)> {
|
pub(super) fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (AttrVec, P<Block>)> {
|
||||||
self.parse_block_common(self.token.span, BlockCheckMode::Default)
|
self.parse_block_common(self.token.span, BlockCheckMode::Default, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses a block. Inner attributes are allowed.
|
/// Parses a block. Inner attributes are allowed.
|
||||||
@ -506,16 +506,23 @@ impl<'a> Parser<'a> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
lo: Span,
|
lo: Span,
|
||||||
blk_mode: BlockCheckMode,
|
blk_mode: BlockCheckMode,
|
||||||
|
can_be_struct_literal: bool,
|
||||||
) -> PResult<'a, (AttrVec, P<Block>)> {
|
) -> PResult<'a, (AttrVec, P<Block>)> {
|
||||||
maybe_whole!(self, NtBlock, |x| (AttrVec::new(), x));
|
maybe_whole!(self, NtBlock, |x| (AttrVec::new(), x));
|
||||||
|
|
||||||
|
let maybe_ident = self.prev_token.clone();
|
||||||
self.maybe_recover_unexpected_block_label();
|
self.maybe_recover_unexpected_block_label();
|
||||||
if !self.eat(&token::OpenDelim(Delimiter::Brace)) {
|
if !self.eat(&token::OpenDelim(Delimiter::Brace)) {
|
||||||
return self.error_block_no_opening_brace();
|
return self.error_block_no_opening_brace();
|
||||||
}
|
}
|
||||||
|
|
||||||
let attrs = self.parse_inner_attributes()?;
|
let attrs = self.parse_inner_attributes()?;
|
||||||
let tail = match self.maybe_suggest_struct_literal(lo, blk_mode) {
|
let tail = match self.maybe_suggest_struct_literal(
|
||||||
|
lo,
|
||||||
|
blk_mode,
|
||||||
|
maybe_ident,
|
||||||
|
can_be_struct_literal,
|
||||||
|
) {
|
||||||
Some(tail) => tail?,
|
Some(tail) => tail?,
|
||||||
None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?,
|
None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?,
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
pub struct Example { a: i32 }
|
||||||
|
|
||||||
|
impl Example {
|
||||||
|
fn is_pos(&self) -> bool { self.a > 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn one() -> i32 { 1 }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if Example { a: one(), }.is_pos() { //~ ERROR invalid struct literal
|
||||||
|
println!("Positive!");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
error: invalid struct literal
|
||||||
|
--> $DIR/method-call-on-struct-literal-in-if-condition.rs:10:8
|
||||||
|
|
|
||||||
|
LL | if Example { a: one(), }.is_pos() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: you might need to surround the struct literal in parentheses
|
||||||
|
|
|
||||||
|
LL | if (Example { a: one(), }).is_pos() {
|
||||||
|
| + +
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
Loading…
Reference in New Issue
Block a user