1use rustc_ast::util::parser::AssocOp;
2use rustc_ast::{BinOpKind, Expr, ExprKind, token};
3use rustc_ast_pretty::pprust;
4use rustc_errors::{Applicability, Diag, PResult};
5use rustc_span::{Span, Spanned, respan, sym};
6
7use crate::parser::Parser;
8use crate::{diagnostics, exp};
9
10impl<'a> Parser<'a> {
11 pub(super) fn recover_from_bin_op_after_complete_stmt_expr(&self, lhs: &Expr) -> bool {
13 use BinOpKind::*;
14
15 let Some(op) = AssocOp::from_token(&self.token) else { return false };
18
19 if op_can_continue_stmt_expr_unambiguously(op) {
25 self.dcx().emit_err(diagnostics::FoundExprWouldBeStmt {
30 span: self.token.span,
31 token: pprust::token_to_string(&self.token),
32 suggestion: diagnostics::ExprParenthesesNeeded::surrounding(lhs.span),
33 });
34 return true;
35 }
36
37 if let AssocOp::Binary(Add | And | BitOr | Mul | Or | Sub) = op {
46 let sp = self.psess.source_map().start_point(self.token.span);
47 self.psess
48 .complete_stmt_exprs_before_bin_op_lookalike
49 .borrow_mut()
50 .insert(sp, lhs.span);
51 }
52
53 false
54 }
55
56 pub(super) fn recover_from_alpha_logic_op(&self) -> Option<Spanned<AssocOp>> {
58 if self.may_recover()
59 && let Some(ident) = self.token.non_raw_ident()
60 {
61 let (op, sub): (_, fn(_) -> _) = match ident.name {
62 sym::and => (BinOpKind::And, diagnostics::InvalidLogicalOperatorSub::Conjunction),
63 sym::or => (BinOpKind::Or, diagnostics::InvalidLogicalOperatorSub::Disjunction),
64 _ => return None,
65 };
66
67 self.dcx().emit_err(diagnostics::InvalidLogicalOperator {
68 span: self.token.span,
69 incorrect: ident.name,
70 sub: sub(self.token.span),
71 });
72
73 Some(respan(self.token.span, AssocOp::Binary(op)))
74 } else {
75 None
76 }
77 }
78
79 pub(super) fn reject_dotdotdot_expr_op(&self) {
81 if self.token == token::DotDotDot {
82 self.dcx().emit_err(diagnostics::DotDotDotExprOp { span: self.token.span });
83 }
84 }
85
86 pub(super) fn reject_larrow_expr_op(&self) {
88 if self.token == token::LArrow {
89 self.dcx().emit_err(diagnostics::LArrowExprOp { span: self.token.span });
90 }
91 }
92
93 pub(super) fn recover_from_strict_eq_op(&mut self, op: Spanned<AssocOp>) {
95 if let AssocOp::Binary(bop @ BinOpKind::Eq | bop @ BinOpKind::Ne) = op.node
96 && self.token == token::Eq
97 && self.prev_token.span.hi() == self.token.span.lo()
98 {
99 let sp = op.span.to(self.token.span);
100 let sugg = bop.as_str().into();
101 let invalid = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}=", sugg))
})format!("{sugg}=");
102 self.dcx().emit_err(diagnostics::InvalidComparisonOperator {
103 span: sp,
104 invalid: invalid.clone(),
105 sub: diagnostics::InvalidComparisonOperatorSub::Correctable {
106 span: sp,
107 invalid,
108 correct: sugg,
109 },
110 });
111 self.bump();
112 }
113 }
114
115 pub(super) fn recover_from_diamond_ne_op(&mut self) {
117 if let (token::Lt, token::Gt) = (self.prev_token.kind, self.token.kind)
118 && self.prev_token.span.hi() == self.token.span.lo()
119 {
120 let sp = self.prev_token.span.to(self.token.span);
121 self.dcx().emit_err(diagnostics::InvalidComparisonOperator {
122 span: sp,
123 invalid: "<>".into(),
124 sub: diagnostics::InvalidComparisonOperatorSub::Correctable {
125 span: sp,
126 invalid: "<>".into(),
127 correct: "!=".into(),
128 },
129 });
130 self.bump();
131 }
132 }
133
134 pub(super) fn recover_from_spaceship_cmp_op(&mut self) {
136 if let (token::Le, token::Gt) = (self.prev_token.kind, self.token.kind)
137 && self.prev_token.span.hi() == self.token.span.lo()
138 {
139 let sp = self.prev_token.span.to(self.token.span);
140 self.dcx().emit_err(diagnostics::InvalidComparisonOperator {
141 span: sp,
142 invalid: "<=>".into(),
143 sub: diagnostics::InvalidComparisonOperatorSub::Spaceship(sp),
144 });
145 self.bump();
146 }
147 }
148
149 pub(super) fn recover_from_postfix_inc_op(
151 &mut self,
152 lhs: &Expr,
153 starts_stmt: bool,
154 ) -> PResult<'a, ()> {
155 if let (token::Plus, token::Plus) = (self.prev_token.kind, self.token.kind)
156 && self.prev_token.span.hi() == self.token.span.lo()
157 {
158 let op_span = self.prev_token.span.to(self.token.span);
159 self.bump(); Err(self.report_inc_dec_op(lhs, starts_stmt, IncOrDec::Inc, UnaryFixity::Post, op_span))
161 } else {
162 Ok(())
163 }
164 }
165
166 pub(super) fn recover_from_postfix_dec_op(
168 &mut self,
169 lhs: &Expr,
170 starts_stmt: bool,
171 ) -> PResult<'a, ()> {
172 if let (token::Minus, token::Minus) = (self.prev_token.kind, self.token.kind)
173 && self.prev_token.span.hi() == self.token.span.lo()
174 && !self.look_ahead(1, |tok| tok.can_begin_expr())
175 {
176 let op_span = self.prev_token.span.to(self.token.span);
177 self.bump(); Err(self.report_inc_dec_op(lhs, starts_stmt, IncOrDec::Dec, UnaryFixity::Post, op_span))
179 } else {
180 Ok(())
181 }
182 }
183
184 pub(super) fn report_inc_dec_op(
186 &mut self,
187 base: &Expr,
188 starts_stmt: bool,
189 op: IncOrDec,
190 fixity: UnaryFixity,
191 op_span: Span,
192 ) -> Diag<'a> {
193 let mut err = {
200 let fixity = match fixity {
201 UnaryFixity::Pre => "prefix",
202 UnaryFixity::Post => "postfix",
203 };
204 let op = match op {
205 IncOrDec::Inc => "increment",
206 IncOrDec::Dec => "decrement",
207 };
208 self.dcx()
209 .struct_span_err(op_span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Rust has no {0} {1} operator",
fixity, op))
})format!("Rust has no {fixity} {op} operator"))
210 .with_span_label(op_span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("not a valid {0} operator", fixity))
})format!("not a valid {fixity} operator"))
211 };
212
213 let op = match op {
214 IncOrDec::Inc => "+= 1",
215 IncOrDec::Dec => "-= 1",
216 };
217 let (pre_span, post_span) = match fixity {
218 UnaryFixity::Pre => (op_span, base.span.shrink_to_hi()),
219 UnaryFixity::Post => (base.span.shrink_to_lo(), op_span),
220 };
221
222 if starts_stmt {
223 let mut patches = Vec::new();
224 if !pre_span.is_empty() {
225 patches.push((pre_span, String::new()));
226 }
227 patches.push((post_span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0}", op))
})format!(" {op}")));
228 err.multipart_suggestion(
229 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("use `{0}` instead", op))
})format!("use `{op}` instead"),
230 patches,
231 Applicability::MachineApplicable,
232 );
233 } else {
234 let Ok(base_src) = self.span_to_snippet(base.span) else {
235 err.help(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("use `{0}` instead", op))
})format!("use `{op}` instead"));
236 return err;
237 };
238 match fixity {
239 UnaryFixity::Pre => {
240 err.multipart_suggestion(
241 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("use `{0}` instead", op))
})format!("use `{op}` instead"),
242 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(pre_span, "{ ".into()),
(post_span,
::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0}; {1} }}", op,
base_src))
}))]))vec![(pre_span, "{ ".into()), (post_span, format!(" {op}; {base_src} }}"))],
243 Applicability::MachineApplicable,
244 );
245 }
246 UnaryFixity::Post => {
247 if !#[allow(non_exhaustive_omitted_patterns)] match base.kind {
ExprKind::Binary(..) => true,
_ => false,
}matches!(base.kind, ExprKind::Binary(..)) {
250 let tmp_var = if base_src.trim() == "tmp" { "tmp_" } else { "tmp" };
251 err.multipart_suggestion(
252 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("use `{0}` instead", op))
})format!("use `{op}` instead"),
253 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(pre_span,
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{{ let {0} = ", tmp_var))
})),
(post_span,
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("; {0} {1}; {2} }}",
base_src, op, tmp_var))
}))]))vec![
254 (pre_span, format!("{{ let {tmp_var} = ")),
255 (post_span, format!("; {base_src} {op}; {tmp_var} }}")),
256 ],
257 Applicability::HasPlaceholders,
258 );
259 }
260 }
261 }
262 }
263 err
264 }
265
266 pub(super) fn recover_from_c_array(&mut self, lo: Span) -> Option<Box<Expr>> {
268 if !self.may_recover()
269 || self.token.kind != token::OpenBrace
270 || self.look_ahead(1, |t| !#[allow(non_exhaustive_omitted_patterns)] match t.kind {
token::Literal(_) => true,
_ => false,
}matches!(t.kind, token::Literal(_)))
271 || self.look_ahead(2, |t| t != &token::Comma)
272 || self.look_ahead(3, |t| !t.can_begin_expr())
273 {
274 return None;
275 }
276
277 let mut snapshot = self.create_snapshot_for_diagnostic();
278 match snapshot.parse_expr_array_or_repeat(crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::CloseBrace,
token_type: crate::parser::token_type::TokenType::CloseBrace,
}exp!(CloseBrace)) {
279 Ok(arr) => {
280 let guar = self.dcx().emit_err(diagnostics::ArrayBracketsInsteadOfBraces {
281 span: arr.span,
282 sub: diagnostics::ArrayBracketsInsteadOfBracesSugg {
283 left: lo,
284 right: snapshot.prev_token.span,
285 },
286 });
287
288 self.restore_snapshot(snapshot);
289 Some(self.mk_expr_err(arr.span, guar))
290 }
291 Err(e) => {
292 e.cancel();
293 None
294 }
295 }
296 }
297}
298
299pub(in crate::parser) fn op_can_continue_stmt_expr_unambiguously(op: AssocOp) -> bool {
302 use BinOpKind::*;
307 match op {
308 AssocOp::Assign | AssocOp::AssignOp(_) | AssocOp::Cast => true,
309 AssocOp::Binary(bin_op) => match bin_op {
310 | BitXor | Div | Ge | Gt | Le | Rem | Shr => true,
311 | Add | And | BitAnd | BitOr | Eq | Ne | Lt | Mul | Or | Shl | Sub => false, },
322 AssocOp::Range(_) => false, }
324}
325
326#[derive(#[automatically_derived]
impl ::core::marker::Copy for IncOrDec { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for IncOrDec { }
#[automatically_derived]
impl ::core::clone::Clone for IncOrDec {
#[inline]
fn clone(&self) -> Self { *self }
}Clone)]
327pub(super) enum IncOrDec {
328 Inc,
329 Dec,
330}
331
332#[derive(#[automatically_derived]
impl ::core::marker::Copy for UnaryFixity { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for UnaryFixity { }
#[automatically_derived]
impl ::core::clone::Clone for UnaryFixity {
#[inline]
fn clone(&self) -> Self { *self }
}Clone)]
333pub(super) enum UnaryFixity {
334 Pre,
335 Post,
336}