Skip to main content

hir_def/
hir.rs

1//! This module describes hir-level representation of expressions.
2//!
3//! This representation is:
4//!
5//! 1. Identity-based. Each expression has an `id`, so we can distinguish
6//!    between different `1` in `1 + 1`.
7//! 2. Independent of syntax. Though syntactic provenance information can be
8//!    attached separately via id-based side map.
9//! 3. Unresolved. Paths are stored as sequences of names, and not as defs the
10//!    names refer to.
11//! 4. Desugared. There's no `if let`.
12//!
13//! See also a neighboring [`body`] module.
14//!
15//! [`body`]: crate::expr_store::body
16
17pub mod format_args;
18pub mod generics;
19pub mod type_ref;
20
21use std::{fmt, mem};
22
23use hir_expand::{MacroDefId, name::Name};
24use intern::Symbol;
25use la_arena::{Idx, RawIdx};
26use rustc_apfloat::ieee::{Double, Half, Quad, Single};
27use syntax::ast;
28use thin_vec::ThinVec;
29use type_ref::TypeRefId;
30
31use crate::{
32    BlockId,
33    builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
34    expr_store::{
35        HygieneId,
36        path::{GenericArgs, Path},
37    },
38    type_ref::{Mutability, Rawness},
39};
40
41pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
42
43pub type BindingId = Idx<Binding>;
44
45pub type ExprId = Idx<Expr>;
46
47pub type PatId = Idx<Pat>;
48
49#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
50pub enum ExprOrPatId {
51    ExprId(ExprId),
52    PatId(PatId),
53}
54
55impl ExprOrPatId {
56    pub fn as_expr(self) -> Option<ExprId> {
57        match self {
58            Self::ExprId(v) => Some(v),
59            _ => None,
60        }
61    }
62
63    pub fn is_expr(&self) -> bool {
64        matches!(self, Self::ExprId(_))
65    }
66
67    pub fn as_pat(self) -> Option<PatId> {
68        match self {
69            Self::PatId(v) => Some(v),
70            _ => None,
71        }
72    }
73
74    pub fn is_pat(&self) -> bool {
75        matches!(self, Self::PatId(_))
76    }
77}
78
79#[derive(Copy, Clone, Hash, PartialEq, Eq, salsa::SalsaValue)]
80pub struct ExprOrPatIdPacked(u32);
81
82const _: () = assert!(mem::size_of::<ExprOrPatIdPacked>() == mem::size_of::<u32>());
83
84impl ExprOrPatIdPacked {
85    const PAT_BIT: u32 = 1 << (u32::BITS - 1);
86    const INDEX_MASK: u32 = !Self::PAT_BIT;
87
88    pub fn unpack(self) -> ExprOrPatId {
89        match self.is_expr() {
90            true => ExprOrPatId::ExprId(ExprId::from_raw(RawIdx::from_u32(self.0))),
91            false => {
92                ExprOrPatId::PatId(PatId::from_raw(RawIdx::from_u32(self.0 & Self::INDEX_MASK)))
93            }
94        }
95    }
96
97    #[inline]
98    pub fn as_expr(self) -> Option<ExprId> {
99        self.is_expr().then(|| ExprId::from_raw(RawIdx::from_u32(self.0)))
100    }
101
102    #[inline]
103    pub fn is_expr(&self) -> bool {
104        self.0 & Self::PAT_BIT == 0
105    }
106
107    #[inline]
108    pub fn as_pat(self) -> Option<PatId> {
109        self.is_pat().then(|| PatId::from_raw(RawIdx::from_u32(self.0 & Self::INDEX_MASK)))
110    }
111
112    #[inline]
113    pub fn is_pat(&self) -> bool {
114        self.0 & Self::PAT_BIT != 0
115    }
116}
117
118impl fmt::Debug for ExprOrPatIdPacked {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        match self.unpack() {
121            ExprOrPatId::ExprId(id) => f.debug_tuple("ExprId").field(&id).finish(),
122            ExprOrPatId::PatId(id) => f.debug_tuple("PatId").field(&id).finish(),
123        }
124    }
125}
126
127impl From<ExprId> for ExprOrPatIdPacked {
128    fn from(value: ExprId) -> Self {
129        let value = value.into_raw().into_u32();
130        // virtually impossible to have IDs that high
131        debug_assert_eq!(value & Self::PAT_BIT, 0);
132        Self(value)
133    }
134}
135
136impl From<PatId> for ExprOrPatId {
137    fn from(value: PatId) -> Self {
138        ExprOrPatId::PatId(value)
139    }
140}
141impl From<ExprId> for ExprOrPatId {
142    fn from(value: ExprId) -> Self {
143        ExprOrPatId::ExprId(value)
144    }
145}
146
147impl From<PatId> for ExprOrPatIdPacked {
148    fn from(value: PatId) -> Self {
149        let value = value.into_raw().into_u32();
150        // virtually impossible to have IDs that high
151        debug_assert_eq!(value & Self::PAT_BIT, 0);
152        Self(value | Self::PAT_BIT)
153    }
154}
155
156#[derive(Debug, Clone, Eq, PartialEq)]
157pub struct Label {
158    pub name: Name,
159}
160pub type LabelId = Idx<Label>;
161
162// We leave float values as a string to avoid double rounding.
163// For PartialEq, string comparison should work, as ordering is not important
164// https://github.com/rust-lang/rust-analyzer/issues/12380#issuecomment-1137284360
165#[derive(Debug, Clone, Eq, PartialEq)]
166pub struct FloatTypeWrapper(Symbol);
167
168// FIXME(#17451): Use builtin types once stabilised.
169impl FloatTypeWrapper {
170    pub fn new(sym: Symbol) -> Self {
171        Self(sym)
172    }
173
174    pub fn to_f128(&self) -> Quad {
175        self.0.as_str().parse().unwrap_or_default()
176    }
177
178    pub fn to_f64(&self) -> Double {
179        self.0.as_str().parse().unwrap_or_default()
180    }
181
182    pub fn to_f32(&self) -> Single {
183        self.0.as_str().parse().unwrap_or_default()
184    }
185
186    pub fn to_f16(&self) -> Half {
187        self.0.as_str().parse().unwrap_or_default()
188    }
189}
190
191impl fmt::Display for FloatTypeWrapper {
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        f.write_str(self.0.as_str())
194    }
195}
196
197#[derive(Debug, Clone, Eq, PartialEq)]
198pub enum Literal {
199    String(Symbol),
200    ByteString(Box<[u8]>),
201    CString(Box<[u8]>),
202    Char(char),
203    Bool(bool),
204    Int(i128, Option<BuiltinInt>),
205    Uint(u128, Option<BuiltinUint>),
206    // Here we are using a wrapper around float because float primitives do not implement Eq, so they
207    // could not be used directly here, to understand how the wrapper works go to definition of
208    // FloatTypeWrapper
209    Float(FloatTypeWrapper, Option<BuiltinFloat>),
210}
211
212#[derive(Debug, Clone, Eq, PartialEq)]
213/// Used in range patterns.
214pub enum LiteralOrConst {
215    Literal(Literal),
216    Const(PatId),
217}
218
219impl Literal {
220    pub fn negate(self) -> Option<Self> {
221        if let Literal::Int(i, k) = self { Some(Literal::Int(-i, k)) } else { None }
222    }
223}
224
225impl From<ast::LiteralKind> for Literal {
226    fn from(ast_lit_kind: ast::LiteralKind) -> Self {
227        use ast::LiteralKind;
228        match ast_lit_kind {
229            LiteralKind::IntNumber(lit) => {
230                if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
231                    Literal::Float(
232                        FloatTypeWrapper::new(Symbol::intern(&lit.value_string())),
233                        builtin,
234                    )
235                } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
236                    Literal::Uint(lit.value().unwrap_or(0), builtin)
237                } else {
238                    let builtin = lit.suffix().and_then(BuiltinInt::from_suffix);
239                    Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
240                }
241            }
242            LiteralKind::FloatNumber(lit) => {
243                let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
244                Literal::Float(FloatTypeWrapper::new(Symbol::intern(&lit.value_string())), ty)
245            }
246            LiteralKind::ByteString(bs) => {
247                let text = bs.value().map_or_else(|_| Default::default(), Box::from);
248                Literal::ByteString(text)
249            }
250            LiteralKind::String(s) => {
251                let text = s.value().map_or_else(|_| Symbol::empty(), |it| Symbol::intern(&it));
252                Literal::String(text)
253            }
254            LiteralKind::CString(s) => {
255                let text = s.value().map_or_else(|_| Default::default(), Box::from);
256                Literal::CString(text)
257            }
258            LiteralKind::Byte(b) => {
259                Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
260            }
261            LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
262            LiteralKind::Bool(val) => Literal::Bool(val),
263        }
264    }
265}
266
267#[derive(Debug, Clone, Eq, PartialEq, Copy)]
268pub enum RecordSpread {
269    None,
270    FieldDefaults,
271    Expr(ExprId),
272}
273
274#[derive(Debug, Clone, Copy, Eq, PartialEq)]
275pub enum Unsafe {
276    Yes,
277    No,
278}
279
280#[derive(Debug, Clone, Eq, PartialEq)]
281pub enum Expr {
282    /// This is produced if the syntax tree does not have a required expression piece.
283    Missing,
284    Path(Path),
285    If {
286        condition: ExprId,
287        then_branch: ExprId,
288        else_branch: Option<ExprId>,
289    },
290    Let {
291        pat: PatId,
292        expr: ExprId,
293    },
294    Block {
295        id: Option<BlockId>,
296        statements: Box<[Statement]>,
297        tail: Option<ExprId>,
298        label: Option<LabelId>,
299        unsafe_: Unsafe,
300    },
301    Const(ExprId),
302    Loop {
303        body: ExprId,
304        label: Option<LabelId>,
305        source: LoopSource,
306    },
307    Call {
308        callee: ExprId,
309        args: Box<[ExprId]>,
310    },
311    MethodCall {
312        receiver: ExprId,
313        method_name: Name,
314        args: Box<[ExprId]>,
315        generic_args: Option<Box<GenericArgs>>,
316    },
317    Match {
318        expr: ExprId,
319        arms: Box<[MatchArm]>,
320    },
321    Continue {
322        label: Option<LabelId>,
323    },
324    Break {
325        expr: Option<ExprId>,
326        label: Option<LabelId>,
327    },
328    Return {
329        expr: Option<ExprId>,
330    },
331    Become {
332        expr: ExprId,
333    },
334    Yield {
335        expr: Option<ExprId>,
336    },
337    Yeet {
338        expr: Option<ExprId>,
339    },
340    RecordLit {
341        path: Path,
342        fields: ThinVec<RecordLitField>,
343        spread: RecordSpread,
344    },
345    Field {
346        expr: ExprId,
347        name: Name,
348    },
349    Await {
350        expr: ExprId,
351    },
352    Cast {
353        expr: ExprId,
354        type_ref: TypeRefId,
355    },
356    Ref {
357        expr: ExprId,
358        rawness: Rawness,
359        mutability: Mutability,
360    },
361    UnaryOp {
362        expr: ExprId,
363        op: UnaryOp,
364    },
365    /// `op` cannot be bare `=` (but can be `op=`), these are lowered to `Assignment` instead.
366    BinaryOp {
367        lhs: ExprId,
368        rhs: ExprId,
369        op: Option<BinaryOp>,
370    },
371    // Assignments need a special treatment because of destructuring assignment.
372    Assignment {
373        target: PatId,
374        value: ExprId,
375    },
376    Index {
377        base: ExprId,
378        index: ExprId,
379    },
380    Closure {
381        args: Box<[PatId]>,
382        arg_types: Box<[Option<TypeRefId>]>,
383        ret_type: Option<TypeRefId>,
384        body: ExprId,
385        closure_kind: ClosureKind,
386        capture_by: CaptureBy,
387    },
388    Tuple {
389        exprs: Box<[ExprId]>,
390    },
391    Array(Array),
392    Literal(Literal),
393    Underscore,
394    OffsetOf(OffsetOf),
395    InlineAsm(InlineAsm),
396    IncludeBytes,
397}
398
399#[cfg(target_pointer_width = "64")]
400const _: () = assert!(std::mem::size_of::<Expr>() == 48);
401
402impl Expr {
403    pub fn precedence(&self) -> ast::prec::ExprPrecedence {
404        use ast::prec::ExprPrecedence;
405
406        match self {
407            Expr::Array(_)
408            | Expr::InlineAsm(_)
409            | Expr::Block { .. }
410            | Expr::Const(_)
411            | Expr::If { .. }
412            | Expr::Literal(_)
413            | Expr::Loop { .. }
414            | Expr::Match { .. }
415            | Expr::Missing
416            | Expr::Path(_)
417            | Expr::RecordLit { .. }
418            | Expr::Tuple { .. }
419            | Expr::OffsetOf(_)
420            | Expr::Underscore
421            | Expr::IncludeBytes => ExprPrecedence::Unambiguous,
422
423            Expr::Await { .. }
424            | Expr::Call { .. }
425            | Expr::Field { .. }
426            | Expr::Index { .. }
427            | Expr::MethodCall { .. } => ExprPrecedence::Postfix,
428
429            Expr::Let { .. } | Expr::UnaryOp { .. } | Expr::Ref { .. } => ExprPrecedence::Prefix,
430
431            Expr::Cast { .. } => ExprPrecedence::Cast,
432
433            Expr::BinaryOp { op, .. } => match op {
434                None => ExprPrecedence::Unambiguous,
435                Some(BinaryOp::LogicOp(LogicOp::Or)) => ExprPrecedence::LOr,
436                Some(BinaryOp::LogicOp(LogicOp::And)) => ExprPrecedence::LAnd,
437                Some(BinaryOp::CmpOp(_)) => ExprPrecedence::Compare,
438                Some(BinaryOp::Assignment { .. }) => ExprPrecedence::Assign,
439                Some(BinaryOp::ArithOp(arith_op)) => match arith_op {
440                    ArithOp::Add | ArithOp::Sub => ExprPrecedence::Sum,
441                    ArithOp::Mul | ArithOp::Div | ArithOp::Rem => ExprPrecedence::Product,
442                    ArithOp::Shl | ArithOp::Shr => ExprPrecedence::Shift,
443                    ArithOp::BitXor => ExprPrecedence::BitXor,
444                    ArithOp::BitOr => ExprPrecedence::BitOr,
445                    ArithOp::BitAnd => ExprPrecedence::BitAnd,
446                },
447            },
448
449            Expr::Assignment { .. } => ExprPrecedence::Assign,
450
451            Expr::Become { .. }
452            | Expr::Break { .. }
453            | Expr::Closure { .. }
454            | Expr::Return { .. }
455            | Expr::Yeet { .. }
456            | Expr::Yield { .. } => ExprPrecedence::Jump,
457
458            Expr::Continue { .. } => ExprPrecedence::Unambiguous,
459        }
460    }
461}
462
463/// The loop type that yielded an `Expr::Loop`.
464#[derive(Debug, Clone, Copy, PartialEq, Eq)]
465pub enum LoopSource {
466    /// A `loop { .. }` loop.
467    Loop,
468    /// A `while _ { .. }` loop.
469    While,
470    /// A `for _ in _ { .. }` loop.
471    ForLoop,
472}
473
474#[derive(Debug, Clone, PartialEq, Eq)]
475pub struct OffsetOf {
476    pub container: TypeRefId,
477    pub fields: Box<[Name]>,
478}
479
480#[derive(Debug, Clone, PartialEq, Eq)]
481pub struct InlineAsm {
482    pub operands: Box<[(Option<Name>, AsmOperand)]>,
483    pub options: AsmOptions,
484    pub kind: InlineAsmKind,
485}
486
487#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
488pub enum InlineAsmKind {
489    /// `asm!()`.
490    Asm,
491    /// `global_asm!()`.
492    GlobalAsm,
493    /// `naked_asm!()`.
494    NakedAsm,
495}
496
497#[derive(Clone, Copy, PartialEq, Eq, Hash)]
498pub struct AsmOptions(u16);
499bitflags::bitflags! {
500    impl AsmOptions: u16 {
501        const PURE            = 1 << 0;
502        const NOMEM           = 1 << 1;
503        const READONLY        = 1 << 2;
504        const PRESERVES_FLAGS = 1 << 3;
505        const NORETURN        = 1 << 4;
506        const NOSTACK         = 1 << 5;
507        const ATT_SYNTAX      = 1 << 6;
508        const RAW             = 1 << 7;
509        const MAY_UNWIND      = 1 << 8;
510    }
511}
512
513impl AsmOptions {
514    pub const COUNT: usize = Self::all().bits().count_ones() as usize;
515
516    pub const GLOBAL_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW);
517    pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW).union(Self::NORETURN);
518
519    pub fn human_readable_names(&self) -> Vec<&'static str> {
520        let mut options = vec![];
521
522        if self.contains(AsmOptions::PURE) {
523            options.push("pure");
524        }
525        if self.contains(AsmOptions::NOMEM) {
526            options.push("nomem");
527        }
528        if self.contains(AsmOptions::READONLY) {
529            options.push("readonly");
530        }
531        if self.contains(AsmOptions::PRESERVES_FLAGS) {
532            options.push("preserves_flags");
533        }
534        if self.contains(AsmOptions::NORETURN) {
535            options.push("noreturn");
536        }
537        if self.contains(AsmOptions::NOSTACK) {
538            options.push("nostack");
539        }
540        if self.contains(AsmOptions::ATT_SYNTAX) {
541            options.push("att_syntax");
542        }
543        if self.contains(AsmOptions::RAW) {
544            options.push("raw");
545        }
546        if self.contains(AsmOptions::MAY_UNWIND) {
547            options.push("may_unwind");
548        }
549
550        options
551    }
552}
553
554impl std::fmt::Debug for AsmOptions {
555    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
556        bitflags::parser::to_writer(self, f)
557    }
558}
559
560#[derive(Clone, Debug, Eq, PartialEq, Hash)]
561pub enum AsmOperand {
562    In {
563        reg: InlineAsmRegOrRegClass,
564        expr: ExprId,
565    },
566    Out {
567        reg: InlineAsmRegOrRegClass,
568        expr: Option<ExprId>,
569        late: bool,
570    },
571    InOut {
572        reg: InlineAsmRegOrRegClass,
573        expr: ExprId,
574        late: bool,
575    },
576    SplitInOut {
577        reg: InlineAsmRegOrRegClass,
578        in_expr: ExprId,
579        out_expr: Option<ExprId>,
580        late: bool,
581    },
582    Label(ExprId),
583    Const(ExprId),
584    Sym(Path),
585}
586
587impl AsmOperand {
588    pub fn reg(&self) -> Option<&InlineAsmRegOrRegClass> {
589        match self {
590            Self::In { reg, .. }
591            | Self::Out { reg, .. }
592            | Self::InOut { reg, .. }
593            | Self::SplitInOut { reg, .. } => Some(reg),
594            Self::Const { .. } | Self::Sym { .. } | Self::Label { .. } => None,
595        }
596    }
597
598    pub fn is_clobber(&self) -> bool {
599        matches!(self, AsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(_), late: _, expr: None })
600    }
601}
602
603#[derive(Clone, Debug, Eq, PartialEq, Hash)]
604pub enum InlineAsmRegOrRegClass {
605    Reg(Symbol),
606    RegClass(Symbol),
607}
608
609#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
610pub enum CoroutineKind {
611    Async,
612    Gen,
613    AsyncGen,
614}
615
616#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
617pub enum ClosureKind {
618    Closure,
619    OldCoroutine(Movability),
620    Coroutine { kind: CoroutineKind, source: CoroutineSource },
621    CoroutineClosure(CoroutineKind),
622}
623
624/// In the case of a coroutine created as part of an async/gen construct,
625/// which kind of async/gen construct caused it to be created?
626///
627/// This helps error messages but is also used to drive coercions in
628/// type-checking (see #60424).
629#[derive(Clone, PartialEq, Eq, Hash, Debug, Copy)]
630pub enum CoroutineSource {
631    /// An explicit `async`/`gen` block written by the user.
632    Block,
633
634    /// An explicit `async`/`gen` closure written by the user.
635    Closure,
636
637    /// The `async`/`gen` block generated as the body of an async/gen function.
638    Fn,
639}
640
641#[derive(Debug, Clone, Copy, PartialEq, Eq)]
642pub enum CaptureBy {
643    /// `move |x| y + x`.
644    Value,
645    /// `move` keyword was not specified.
646    Ref,
647}
648
649#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
650pub enum Movability {
651    Static,
652    Movable,
653}
654
655#[derive(Debug, Clone, Eq, PartialEq)]
656pub enum Array {
657    ElementList { elements: Box<[ExprId]> },
658    Repeat { initializer: ExprId, repeat: ExprId },
659}
660
661#[derive(Debug, Clone, Eq, PartialEq)]
662pub struct MatchArm {
663    pub pat: PatId,
664    pub guard: Option<ExprId>,
665    pub expr: ExprId,
666}
667
668#[derive(Debug, Clone, Eq, PartialEq)]
669pub struct RecordLitField {
670    pub name: Name,
671    pub expr: ExprId,
672}
673
674#[derive(Debug, Clone, Eq, PartialEq)]
675pub enum Statement {
676    Let {
677        pat: PatId,
678        type_ref: Option<TypeRefId>,
679        initializer: Option<ExprId>,
680        else_branch: Option<ExprId>,
681    },
682    Expr {
683        expr: ExprId,
684        has_semi: bool,
685    },
686    Item(Item),
687}
688
689#[derive(Debug, Clone, PartialEq, Eq)]
690pub enum Item {
691    MacroDef(Box<MacroDefId>),
692    Other,
693}
694
695/// Explicit binding annotations given in the HIR for a binding. Note
696/// that this is not the final binding *mode* that we infer after type
697/// inference.
698#[derive(Clone, PartialEq, Eq, Debug, Copy)]
699pub enum BindingAnnotation {
700    /// No binding annotation given: this means that the final binding mode
701    /// will depend on whether we have skipped through a `&` reference
702    /// when matching. For example, the `x` in `Some(x)` will have binding
703    /// mode `None`; if you do `let Some(x) = &Some(22)`, it will
704    /// ultimately be inferred to be by-reference.
705    Unannotated,
706
707    /// Annotated with `mut x` -- could be either ref or not, similar to `None`.
708    Mutable,
709
710    /// Annotated as `ref`, like `ref x`
711    Ref,
712
713    /// Annotated as `ref mut x`.
714    RefMut,
715}
716
717impl BindingAnnotation {
718    pub fn new(is_mutable: bool, is_ref: bool) -> Self {
719        match (is_mutable, is_ref) {
720            (true, true) => BindingAnnotation::RefMut,
721            (false, true) => BindingAnnotation::Ref,
722            (true, false) => BindingAnnotation::Mutable,
723            (false, false) => BindingAnnotation::Unannotated,
724        }
725    }
726}
727
728#[derive(Debug, Clone, Eq, PartialEq)]
729pub enum BindingProblems {
730    /// <https://doc.rust-lang.org/stable/error_codes/E0416.html>
731    BoundMoreThanOnce,
732    /// <https://doc.rust-lang.org/stable/error_codes/E0409.html>
733    BoundInconsistently,
734    /// <https://doc.rust-lang.org/stable/error_codes/E0408.html>
735    NotBoundAcrossAll,
736}
737
738#[derive(Debug, Clone, Eq, PartialEq)]
739pub struct Binding {
740    pub name: Name,
741    pub mode: BindingAnnotation,
742    pub problems: Option<BindingProblems>,
743    /// Note that this may not be the direct `SyntaxContextId` of the binding's expansion, because transparent
744    /// expansions are attributed to their parent expansion (recursively).
745    pub hygiene: HygieneId,
746}
747
748#[derive(Debug, Clone, Eq, PartialEq)]
749pub struct RecordFieldPat {
750    pub name: Name,
751    pub pat: PatId,
752}
753
754/// Close relative to rustc's hir::PatKind
755#[derive(Debug, Clone, Eq, PartialEq)]
756pub enum Pat {
757    Missing,
758    /// A rest pattern. Not valid outside special context.
759    Rest,
760    Wild,
761    Tuple {
762        args: Box<[PatId]>,
763        ellipsis: Option<u32>,
764    },
765    Or(Box<[PatId]>),
766    Record {
767        path: Path,
768        args: Box<[RecordFieldPat]>,
769        ellipsis: bool,
770    },
771    Range {
772        start: Option<ExprId>,
773        end: Option<ExprId>,
774        range_type: RangeOp,
775    },
776    Slice {
777        prefix: Box<[PatId]>,
778        slice: Option<PatId>,
779        suffix: Box<[PatId]>,
780    },
781    Path(Path),
782    Lit(ExprId),
783    Bind {
784        id: BindingId,
785        subpat: Option<PatId>,
786    },
787    TupleStruct {
788        path: Path,
789        args: Box<[PatId]>,
790        ellipsis: Option<u32>,
791    },
792    Ref {
793        pat: PatId,
794        mutability: Mutability,
795    },
796    Box {
797        inner: PatId,
798    },
799    Deref {
800        inner: PatId,
801    },
802    NotNull,
803    /// An expression inside a pattern. That can only occur inside assignments.
804    ///
805    /// E.g. in `(a, *b) = (1, &mut 2)`, `*b` is an expression.
806    Expr(ExprId),
807}
808
809impl Pat {
810    pub fn walk_child_pats(&self, mut f: impl FnMut(PatId)) {
811        match self {
812            Pat::Range { .. }
813            | Pat::Lit(..)
814            | Pat::Path(..)
815            | Pat::Wild
816            | Pat::Missing
817            | Pat::Rest
818            | Pat::Expr(_)
819            | Pat::NotNull => {}
820            Pat::Bind { subpat, .. } => {
821                subpat.iter().copied().for_each(f);
822            }
823            Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
824                args.iter().copied().for_each(f);
825            }
826            Pat::Ref { pat, .. } => f(*pat),
827            Pat::Slice { prefix, slice, suffix } => {
828                let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
829                total_iter.copied().for_each(f);
830            }
831            Pat::Record { args, .. } => {
832                args.iter().map(|f| f.pat).for_each(f);
833            }
834            Pat::Box { inner } | Pat::Deref { inner } => f(*inner),
835        }
836    }
837}