Skip to main content

rustc_parse/parser/expr/
diagnostics.rs

1use rustc_ast::util::parser::AssocOp;
2use rustc_ast::{BinOpKind, Expr, ExprKind, token};
3use rustc_errors::{Applicability, Diag, PResult};
4use rustc_span::{Span, Spanned, respan, sym};
5
6use crate::parser::Parser;
7use crate::{diagnostics, exp};
8
9impl<'a> Parser<'a> {
10    /// Recover from alphabetic logic operators `and` and `or` as found in e.g., Python and PHP.
11    pub(super) fn recover_from_alpha_logic_op(&self) -> Option<Spanned<AssocOp>> {
12        if self.may_recover()
13            && let Some(ident) = self.token.non_raw_ident()
14        {
15            let (op, sub): (_, fn(_) -> _) = match ident.name {
16                sym::and => (BinOpKind::And, diagnostics::InvalidLogicalOperatorSub::Conjunction),
17                sym::or => (BinOpKind::Or, diagnostics::InvalidLogicalOperatorSub::Disjunction),
18                _ => return None,
19            };
20
21            self.dcx().emit_err(diagnostics::InvalidLogicalOperator {
22                span: self.token.span,
23                incorrect: ident.name,
24                sub: sub(self.token.span),
25            });
26
27            Some(respan(self.token.span, AssocOp::Binary(op)))
28        } else {
29            None
30        }
31    }
32
33    /// Reject `...` being used as an expression operator.
34    pub(super) fn reject_dotdotdot_expr_op(&self) {
35        if self.token == token::DotDotDot {
36            self.dcx().emit_err(diagnostics::DotDotDotExprOp { span: self.token.span });
37        }
38    }
39
40    /// Reject `<-` being used as an expression operator.
41    pub(super) fn reject_larrow_expr_op(&self) {
42        if self.token == token::LArrow {
43            self.dcx().emit_err(diagnostics::LArrowExprOp { span: self.token.span });
44        }
45    }
46
47    /// Recover from strict equality operators `===` and `!==` as found in e.g., JS and PHP.
48    pub(super) fn recover_from_strict_eq_op(&mut self, op: Spanned<AssocOp>) {
49        if let AssocOp::Binary(bop @ BinOpKind::Eq | bop @ BinOpKind::Ne) = op.node
50            && self.token == token::Eq
51            && self.prev_token.span.hi() == self.token.span.lo()
52        {
53            let sp = op.span.to(self.token.span);
54            let sugg = bop.as_str().into();
55            let invalid = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}=", sugg))
    })format!("{sugg}=");
56            self.dcx().emit_err(diagnostics::InvalidComparisonOperator {
57                span: sp,
58                invalid: invalid.clone(),
59                sub: diagnostics::InvalidComparisonOperatorSub::Correctable {
60                    span: sp,
61                    invalid,
62                    correct: sugg,
63                },
64            });
65            self.bump();
66        }
67    }
68
69    /// Recover from inequality operator `<>` ("diamond") as found in e.g., PHP.
70    pub(super) fn recover_from_diamond_ne_op(&mut self) {
71        if let (token::Lt, token::Gt) = (self.prev_token.kind, self.token.kind)
72            && self.prev_token.span.hi() == self.token.span.lo()
73        {
74            let sp = self.prev_token.span.to(self.token.span);
75            self.dcx().emit_err(diagnostics::InvalidComparisonOperator {
76                span: sp,
77                invalid: "<>".into(),
78                sub: diagnostics::InvalidComparisonOperatorSub::Correctable {
79                    span: sp,
80                    invalid: "<>".into(),
81                    correct: "!=".into(),
82                },
83            });
84            self.bump();
85        }
86    }
87
88    /// Recover from comparison operator `<=>` ("spaceship") as found in e.g., C++.
89    pub(super) fn recover_from_spaceship_cmp_op(&mut self) {
90        if let (token::Le, token::Gt) = (self.prev_token.kind, self.token.kind)
91            && self.prev_token.span.hi() == self.token.span.lo()
92        {
93            let sp = self.prev_token.span.to(self.token.span);
94            self.dcx().emit_err(diagnostics::InvalidComparisonOperator {
95                span: sp,
96                invalid: "<=>".into(),
97                sub: diagnostics::InvalidComparisonOperatorSub::Spaceship(sp),
98            });
99            self.bump();
100        }
101    }
102
103    /// Recover from postfix increment operator `++` as found in many C-style languages.
104    pub(super) fn recover_from_postfix_inc_op(
105        &mut self,
106        lhs: &Expr,
107        starts_stmt: bool,
108    ) -> PResult<'a, ()> {
109        if let (token::Plus, token::Plus) = (self.prev_token.kind, self.token.kind)
110            && self.prev_token.span.hi() == self.token.span.lo()
111        {
112            let op_span = self.prev_token.span.to(self.token.span);
113            self.bump(); // eat the second `+`
114            Err(self.report_inc_dec_op(lhs, starts_stmt, IncOrDec::Inc, UnaryFixity::Post, op_span))
115        } else {
116            Ok(())
117        }
118    }
119
120    /// Recover from postfix decrement operator `--` as found in many C-style languages.
121    pub(super) fn recover_from_postfix_dec_op(
122        &mut self,
123        lhs: &Expr,
124        starts_stmt: bool,
125    ) -> PResult<'a, ()> {
126        if let (token::Minus, token::Minus) = (self.prev_token.kind, self.token.kind)
127            && self.prev_token.span.hi() == self.token.span.lo()
128            && !self.look_ahead(1, |tok| tok.can_begin_expr())
129        {
130            let op_span = self.prev_token.span.to(self.token.span);
131            self.bump(); // eat the second `-`
132            Err(self.report_inc_dec_op(lhs, starts_stmt, IncOrDec::Dec, UnaryFixity::Post, op_span))
133        } else {
134            Ok(())
135        }
136    }
137
138    /// Report increment operator `++` & decrement operator `--` as found in many C-style languages.
139    pub(super) fn report_inc_dec_op(
140        &mut self,
141        base: &Expr,
142        starts_stmt: bool,
143        op: IncOrDec,
144        fixity: UnaryFixity,
145        op_span: Span,
146    ) -> Diag<'a> {
147        // FIXME: Don't return an error diag, emit the diag here *and* return a new expr of the form
148        //        `$base += 1` / `$base -= 1` (taking `base: Expr` by value) for *proper* recovery.
149        //        (Just emitting the diag would be insufficient since callers would most likely just
150        //        use `$base` as the recovered AST node which would lead to annoying follow-up diags
151        //        like "variable doesn't need to be mutable" getting emitted in some cases.)
152
153        let mut err = {
154            let fixity = match fixity {
155                UnaryFixity::Pre => "prefix",
156                UnaryFixity::Post => "postfix",
157            };
158            let op = match op {
159                IncOrDec::Inc => "increment",
160                IncOrDec::Dec => "decrement",
161            };
162            self.dcx()
163                .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"))
164                .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"))
165        };
166
167        let op = match op {
168            IncOrDec::Inc => "+= 1",
169            IncOrDec::Dec => "-= 1",
170        };
171        let (pre_span, post_span) = match fixity {
172            UnaryFixity::Pre => (op_span, base.span.shrink_to_hi()),
173            UnaryFixity::Post => (base.span.shrink_to_lo(), op_span),
174        };
175
176        if starts_stmt {
177            let mut patches = Vec::new();
178            if !pre_span.is_empty() {
179                patches.push((pre_span, String::new()));
180            }
181            patches.push((post_span, ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!(" {0}", op))
    })format!(" {op}")));
182            err.multipart_suggestion(
183                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("use `{0}` instead", op))
    })format!("use `{op}` instead"),
184                patches,
185                Applicability::MachineApplicable,
186            );
187        } else {
188            let Ok(base_src) = self.span_to_snippet(base.span) else {
189                err.help(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("use `{0}` instead", op))
    })format!("use `{op}` instead"));
190                return err;
191            };
192            match fixity {
193                UnaryFixity::Pre => {
194                    err.multipart_suggestion(
195                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("use `{0}` instead", op))
    })format!("use `{op}` instead"),
196                        ::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} }}"))],
197                        Applicability::MachineApplicable,
198                    );
199                }
200                UnaryFixity::Post => {
201                    // won't suggest since we can not handle the precedences
202                    // for example: `a + b++` has been parsed (a + b)++ and we can not suggest here
203                    if !#[allow(non_exhaustive_omitted_patterns)] match base.kind {
    ExprKind::Binary(..) => true,
    _ => false,
}matches!(base.kind, ExprKind::Binary(..)) {
204                        let tmp_var = if base_src.trim() == "tmp" { "tmp_" } else { "tmp" };
205                        err.multipart_suggestion(
206                            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("use `{0}` instead", op))
    })format!("use `{op}` instead"),
207                            ::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![
208                                (pre_span, format!("{{ let {tmp_var} = ")),
209                                (post_span, format!("; {base_src} {op}; {tmp_var} }}")),
210                            ],
211                            Applicability::HasPlaceholders,
212                        );
213                    }
214                }
215            }
216        }
217        err
218    }
219
220    /// Recover from array expressions as found in C like `{0, 1, 2, 3}`.
221    pub(super) fn recover_from_c_array(&mut self, lo: Span) -> Option<Box<Expr>> {
222        if !self.may_recover()
223            || self.token.kind != token::OpenBrace
224            || self.look_ahead(1, |t| !#[allow(non_exhaustive_omitted_patterns)] match t.kind {
    token::Literal(_) => true,
    _ => false,
}matches!(t.kind, token::Literal(_)))
225            || self.look_ahead(2, |t| t != &token::Comma)
226            || self.look_ahead(3, |t| !t.can_begin_expr())
227        {
228            return None;
229        }
230
231        let mut snapshot = self.create_snapshot_for_diagnostic();
232        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)) {
233            Ok(arr) => {
234                let guar = self.dcx().emit_err(diagnostics::ArrayBracketsInsteadOfBraces {
235                    span: arr.span,
236                    sub: diagnostics::ArrayBracketsInsteadOfBracesSugg {
237                        left: lo,
238                        right: snapshot.prev_token.span,
239                    },
240                });
241
242                self.restore_snapshot(snapshot);
243                Some(self.mk_expr_err(arr.span, guar))
244            }
245            Err(e) => {
246                e.cancel();
247                None
248            }
249        }
250    }
251}
252
253#[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)]
254pub(super) enum IncOrDec {
255    Inc,
256    Dec,
257}
258
259#[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)]
260pub(super) enum UnaryFixity {
261    Pre,
262    Post,
263}