flux_syntax/
surface.rs

1pub mod visit;
2use std::{borrow::Cow, fmt, ops::Range};
3
4use flux_config::PartialInferOpts;
5pub use rustc_ast::{
6    Mutability,
7    token::{Lit, LitKind},
8};
9use rustc_hash::FxHashSet;
10pub use rustc_span::{Span, symbol::Ident};
11use rustc_span::{Symbol, symbol::sym};
12
13use crate::surface::visit::Visitor;
14
15/// A [`NodeId`] is a unique identifier we assign to some AST nodes to be able to attach information
16/// to them. For example, to assign a resolution to a [`Path`]. The [`NodeId`] is unique within a crate.
17#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
18pub struct NodeId(pub(super) usize);
19
20impl NodeId {
21    pub fn as_usize(&self) -> usize {
22        self.0
23    }
24}
25
26#[derive(Debug)]
27pub struct SortDecl {
28    pub name: Ident,
29    pub sort_vars: Vec<Ident>,
30}
31
32#[derive(Debug)]
33pub enum FluxItem {
34    Qualifier(Qualifier),
35    FuncDef(SpecFunc),
36    SortDecl(SortDecl),
37    PrimOpProp(PrimOpProp),
38}
39
40impl FluxItem {
41    pub fn name(&self) -> Ident {
42        match self {
43            FluxItem::Qualifier(qualifier) => qualifier.name,
44            FluxItem::FuncDef(spec_func) => spec_func.name,
45            FluxItem::SortDecl(sort_decl) => sort_decl.name,
46            FluxItem::PrimOpProp(primop_prop) => primop_prop.name,
47        }
48    }
49}
50
51#[derive(Debug)]
52pub struct Qualifier {
53    pub name: Ident,
54    pub params: RefineParams,
55    pub expr: Expr,
56    pub span: Span,
57    pub kind: QualifierKind,
58}
59
60#[derive(Debug)]
61pub enum QualifierKind {
62    Global,
63    Local,
64    Hint,
65}
66
67/// A global function definition. It can be either an uninterpreted function or a *syntactic abstraction*,
68/// i.e., a function with a body.
69#[derive(Debug)]
70pub struct SpecFunc {
71    pub name: Ident,
72    pub sort_vars: Vec<Ident>,
73    pub params: RefineParams,
74    pub output: Sort,
75    /// Body of the function. If not present this definition corresponds to an uninterpreted function.
76    pub body: Option<Expr>,
77    /// Is this function "hidden" i.e. to be considered
78    /// as uninterpreted by default (only makes sense if `body` is_some ...)
79    /// as otherwise it is *always* uninterpreted.
80    pub hide: bool,
81}
82
83/// A (currently global) *primop property*; see tests/tests/pos/surface/
84#[derive(Debug)]
85pub struct PrimOpProp {
86    /// The name of the property
87    pub name: Ident,
88    /// The binop it is attached to
89    pub op: BinOp,
90    /// The sort _at_ which the primop is defined,
91    /// The binders for the inputs of the primop; the output sort is always `Bool`
92    pub params: RefineParams,
93    /// The actual definition of the property
94    pub body: Expr,
95    pub span: Span,
96}
97
98#[derive(Debug)]
99pub struct Generics {
100    pub params: Vec<GenericParam>,
101    pub predicates: Option<Vec<WhereBoundPredicate>>,
102    pub span: Span,
103}
104
105#[derive(Debug)]
106pub struct GenericParam {
107    pub name: Ident,
108    pub node_id: NodeId,
109}
110
111#[derive(Debug)]
112pub struct TyAlias {
113    pub ident: Ident,
114    pub generics: Generics,
115    pub params: RefineParams,
116    pub index: Option<RefineParam>,
117    pub ty: Ty,
118    pub node_id: NodeId,
119    pub span: Span,
120}
121
122pub struct Item {
123    pub attrs: Vec<Attr>,
124    pub kind: ItemKind,
125    pub node_id: NodeId,
126}
127
128pub enum ItemKind {
129    Fn(Option<FnSig>),
130    Struct(StructDef),
131    Enum(EnumDef),
132    Trait(Trait),
133    Impl(Impl),
134    Const(ConstantInfo),
135    TyAlias(Box<TyAlias>),
136    Static(StaticInfo),
137    /// Modules can't be refined but we collect attributes for them, e.g., `#[trusted]`
138    /// This kind is also used for the crate root, for which we also collect attributes.
139    Mod,
140}
141
142pub struct TraitItemFn {
143    pub attrs: Vec<Attr>,
144    pub sig: Option<FnSig>,
145    pub node_id: NodeId,
146}
147
148pub struct ImplItemFn {
149    pub attrs: Vec<Attr>,
150    pub sig: Option<FnSig>,
151    pub node_id: NodeId,
152}
153
154#[derive(Debug)]
155pub struct DetachedSpecs {
156    pub items: Vec<DetachedItem>,
157}
158
159#[derive(Debug)]
160pub struct DetachedTraitImpl {
161    pub trait_: ExprPath,
162    pub items: Vec<DetachedItem<FnSig>>,
163    pub refts: Vec<ImplAssocReft>,
164    pub span: Span,
165}
166
167#[derive(Debug, Default)]
168pub struct DetachedTrait {
169    pub items: Vec<DetachedItem<FnSig>>,
170    pub refts: Vec<TraitAssocReft>,
171}
172
173#[derive(Debug)]
174pub struct DetachedInherentImpl {
175    pub items: Vec<DetachedItem<FnSig>>,
176    pub span: Span,
177}
178
179impl DetachedInherentImpl {
180    pub fn extend(&mut self, other: DetachedInherentImpl) {
181        self.items.extend(other.items);
182    }
183}
184
185#[derive(Debug)]
186pub struct DetachedItem<K = DetachedItemKind> {
187    pub attrs: Vec<Attr>,
188    pub path: ExprPath,
189    pub kind: K,
190    pub node_id: NodeId,
191}
192
193impl<K> DetachedItem<K> {
194    pub fn map_kind<R>(self, f: impl FnOnce(K) -> R) -> DetachedItem<R> {
195        DetachedItem {
196            attrs: self.attrs,
197            path: self.path,
198            kind: f(self.kind),
199            node_id: self.node_id,
200        }
201    }
202}
203
204impl DetachedItem<DetachedItemKind> {
205    pub fn span(&self) -> Span {
206        match &self.kind {
207            DetachedItemKind::InherentImpl(impl_) => impl_.span,
208            DetachedItemKind::TraitImpl(trait_impl) => trait_impl.span,
209            _ => self.path.span,
210        }
211    }
212}
213
214#[derive(Debug)]
215pub enum DetachedItemKind {
216    FnSig(FnSig),
217    Mod(DetachedSpecs),
218    Struct(StructDef),
219    Enum(EnumDef),
220    InherentImpl(DetachedInherentImpl),
221    TraitImpl(DetachedTraitImpl),
222    Trait(DetachedTrait),
223    Static(StaticInfo),
224}
225
226#[derive(Debug)]
227pub struct ConstantInfo {
228    pub expr: Option<Expr>,
229}
230
231#[derive(Debug)]
232pub struct StaticInfo {
233    pub ty: Ty,
234}
235
236#[derive(Debug)]
237pub struct StructDef {
238    pub generics: Option<Generics>,
239    pub refined_by: Option<RefineParams>,
240    pub fields: Vec<Option<Ty>>,
241    pub opaque: bool,
242    pub invariants: Vec<Expr>,
243}
244
245#[derive(Debug)]
246pub struct EnumDef {
247    pub generics: Option<Generics>,
248    pub refined_by: Option<RefineParams>,
249    pub variants: Vec<Option<VariantDef>>,
250    pub invariants: Vec<Expr>,
251    pub reflected: bool,
252}
253
254#[derive(Debug)]
255pub struct VariantDef {
256    pub ident: Option<Ident>,
257    pub fields: Vec<Ty>,
258    pub ret: Option<VariantRet>,
259    pub node_id: NodeId,
260    pub span: Span,
261}
262
263#[derive(Debug)]
264pub struct VariantRet {
265    pub path: Path,
266    /// Binders are not allowed at this position, but we parse this as a list of indices
267    /// for better error reporting.
268    pub indices: Indices,
269}
270
271pub type RefineParams = Vec<RefineParam>;
272
273#[derive(Debug)]
274pub struct RefineParam {
275    pub ident: Ident,
276    pub sort: Sort,
277    pub mode: Option<ParamMode>,
278    pub span: Span,
279    pub node_id: NodeId,
280}
281
282#[derive(Clone, Copy, Debug, PartialEq, Eq)]
283pub enum ParamMode {
284    Horn,
285    Hindley,
286}
287
288#[derive(Debug)]
289pub enum Sort {
290    /// A _base_ sort, e.g., `int` or `bool`.
291    Base(BaseSort),
292    /// A _function_ sort of the form `(bi,...) -> bo` where `bi..` and `bo`
293    /// are all base sorts.
294    Func { inputs: Vec<BaseSort>, output: BaseSort },
295    /// A sort that needs to be inferred.
296    Infer,
297}
298
299#[derive(Debug)]
300pub enum BaseSort {
301    /// a bitvector sort, e.g., bitvec<32>
302    BitVec(u32),
303    SortOf(Box<Ty>, Path),
304    Path(SortPath),
305    /// a tuple sort, e.g., (int, bool)
306    Tuple(Vec<BaseSort>),
307}
308
309/// A [`Path`] but for sorts.
310#[derive(Debug)]
311pub struct SortPath {
312    /// The segments in the path
313    pub segments: Vec<Ident>,
314    /// The sort arguments, i.e., the list `[int, bool]` in `Map<int, bool>`.
315    pub args: Vec<BaseSort>,
316    pub node_id: NodeId,
317}
318
319#[derive(Debug)]
320pub struct Impl {
321    pub generics: Option<Generics>,
322    pub assoc_refinements: Vec<ImplAssocReft>,
323}
324
325#[derive(Debug)]
326pub struct ImplAssocReft {
327    pub name: Ident,
328    pub params: RefineParams,
329    pub output: BaseSort,
330    pub body: Expr,
331    pub span: Span,
332}
333
334#[derive(Debug)]
335pub struct Trait {
336    pub generics: Option<Generics>,
337    pub assoc_refinements: Vec<TraitAssocReft>,
338}
339
340#[derive(Debug)]
341pub struct TraitAssocReft {
342    pub name: Ident,
343    pub params: RefineParams,
344    pub output: BaseSort,
345    pub body: Option<Expr>,
346    pub span: Span,
347    pub final_: bool,
348}
349
350#[derive(Debug)]
351pub struct FnSig {
352    pub asyncness: Async,
353    pub ident: Option<Ident>,
354    pub generics: Generics,
355    pub params: RefineParams,
356    /// example: `requires n > 0`
357    pub requires: Vec<Requires>,
358    /// example: `i32<@n>`
359    pub inputs: Vec<FnInput>,
360    pub output: FnOutput,
361    /// source span
362    pub span: Span,
363    pub node_id: NodeId,
364    pub no_panic: Option<Expr>,
365}
366
367#[derive(Debug)]
368pub struct Requires {
369    /// Optional list of universally quantified parameters
370    pub params: RefineParams,
371    pub pred: Expr,
372}
373
374#[derive(Debug)]
375pub struct FnOutput {
376    /// example `i32{v:v >= 0}`
377    pub returns: FnRetTy,
378    /// example: `*x: i32{v. v = n+1}` or just `x > 10`
379    pub ensures: Vec<Ensures>,
380    pub node_id: NodeId,
381}
382
383#[derive(Debug)]
384pub enum Ensures {
385    /// A type constraint on a location
386    Type(Ident, Ty, NodeId),
387    /// A predicate that needs to hold
388    Pred(Expr),
389}
390
391#[derive(Debug)]
392pub enum FnRetTy {
393    Default(Span),
394    Ty(Box<Ty>),
395}
396
397#[derive(Debug, Copy, Clone)]
398pub enum Async {
399    Yes { node_id: NodeId, span: Span },
400    No,
401}
402
403#[derive(Debug)]
404pub struct WhereBoundPredicate {
405    pub span: Span,
406    pub bounded_ty: Ty,
407    pub bounds: GenericBounds,
408}
409
410pub type GenericBounds = Vec<TraitRef>;
411
412#[derive(Debug)]
413pub struct TraitRef {
414    pub path: Path,
415    pub node_id: NodeId,
416}
417
418impl TraitRef {
419    fn is_fn_trait_name(name: Symbol) -> bool {
420        name == sym::FnOnce || name == sym::FnMut || name == sym::Fn
421    }
422
423    pub fn as_fn_trait_ref(&self) -> Option<(&GenericArg, &GenericArg)> {
424        if let [segment] = self.path.segments.as_slice()
425            && Self::is_fn_trait_name(segment.ident.name)
426            && let [in_arg, out_arg] = segment.args.as_slice()
427        {
428            return Some((in_arg, out_arg));
429        }
430        None
431    }
432}
433
434#[derive(Debug)]
435pub enum FnInput {
436    /// example `a: i32{a > 0}`
437    Constr(Ident, Path, Expr, NodeId),
438    /// example `v: &strg i32`
439    StrgRef(Ident, Ty, NodeId),
440    /// A type with an optional binder, e.g, `i32`, `x: i32` or `x: i32{v: v > 0}`.
441    /// The binder has a different meaning depending on the type.
442    Ty(Option<Ident>, Ty, NodeId),
443}
444
445#[derive(Debug)]
446pub struct Ty {
447    pub kind: TyKind,
448    pub node_id: NodeId,
449    pub span: Span,
450}
451
452#[derive(Debug)]
453pub enum TyKind {
454    /// ty
455    Base(BaseTy),
456    /// `B[r]`
457    Indexed {
458        bty: BaseTy,
459        indices: Indices,
460    },
461    /// B{v: r}
462    Exists {
463        bind: Ident,
464        bty: BaseTy,
465        pred: Expr,
466    },
467    GeneralExists {
468        params: RefineParams,
469        ty: Box<Ty>,
470        pred: Option<Expr>,
471    },
472    /// Mutable or shared reference
473    Ref(Mutability, Box<Ty>),
474    /// Constrained type: an exists without binder
475    Constr(Expr, Box<Ty>),
476    Tuple(Vec<Ty>),
477    Array(Box<Ty>, ConstArg),
478    /// The `NodeId` is used to resolve the type to a corresponding `OpaqueTy`
479    ImplTrait(NodeId, GenericBounds),
480    Hole,
481}
482
483impl Ty {
484    pub fn is_refined(&self) -> bool {
485        struct IsRefinedVisitor {
486            is_refined: bool,
487        }
488        let mut vis = IsRefinedVisitor { is_refined: false };
489        impl visit::Visitor for IsRefinedVisitor {
490            fn visit_ty(&mut self, ty: &Ty) {
491                match &ty.kind {
492                    TyKind::Tuple(_)
493                    | TyKind::Ref(..)
494                    | TyKind::Array(..)
495                    | TyKind::ImplTrait(..)
496                    | TyKind::Hole
497                    | TyKind::Base(_) => {
498                        visit::walk_ty(self, ty);
499                    }
500                    TyKind::Indexed { .. }
501                    | TyKind::Exists { .. }
502                    | TyKind::GeneralExists { .. }
503                    | TyKind::Constr(..) => {
504                        self.is_refined = true;
505                    }
506                }
507            }
508        }
509        vis.visit_ty(self);
510        vis.is_refined
511    }
512
513    pub fn is_potential_const_arg(&self) -> Option<&Path> {
514        if let TyKind::Base(bty) = &self.kind
515            && let BaseTyKind::Path(None, path) = &bty.kind
516            && let [segment] = &path.segments[..]
517            && segment.args.is_empty()
518        {
519            Some(path)
520        } else {
521            None
522        }
523    }
524}
525#[derive(Debug)]
526pub struct BaseTy {
527    pub kind: BaseTyKind,
528    pub span: Span,
529}
530
531#[derive(Debug)]
532pub enum BaseTyKind {
533    Path(Option<Box<Ty>>, Path),
534    Slice(Box<Ty>),
535    /// Raw pointer (*const T or *mut T), optionally with refinement on pointer value
536    Ptr(Mutability, Box<Ty>),
537}
538
539#[derive(PartialEq, Eq, Clone, Debug, Copy)]
540pub struct ConstArg {
541    pub kind: ConstArgKind,
542    pub span: Span,
543}
544
545#[derive(PartialEq, Eq, Clone, Debug, Copy)]
546pub enum ConstArgKind {
547    Lit(usize),
548    Infer,
549}
550
551#[derive(Debug)]
552pub struct Indices {
553    pub indices: Vec<RefineArg>,
554    pub span: Span,
555}
556
557#[derive(Debug)]
558pub enum RefineArg {
559    /// `@n` or `#n`, the span corresponds to the span of the identifier plus the binder token (`@` or `#`)
560    Bind(Ident, BindKind, Span, NodeId),
561    Expr(Expr),
562    Abs(RefineParams, Expr, Span, NodeId),
563}
564
565#[derive(Debug, Clone, Copy)]
566pub enum BindKind {
567    At,
568    Pound,
569}
570
571/// A boolean-like enum used to mark whether some code should be trusted.
572#[derive(Debug, Eq, PartialEq, Copy, Clone)]
573pub enum Trusted {
574    Yes,
575    No,
576}
577
578impl Trusted {
579    pub fn to_bool(self) -> bool {
580        match self {
581            Trusted::Yes => true,
582            Trusted::No => false,
583        }
584    }
585}
586
587impl From<bool> for Trusted {
588    fn from(value: bool) -> Self {
589        if value { Trusted::Yes } else { Trusted::No }
590    }
591}
592
593/// A boolean-like enum used to mark whether a piece of code is ignored.
594#[derive(Debug, Eq, PartialEq, Copy, Clone)]
595pub enum Ignored {
596    Yes,
597    No,
598}
599
600impl Ignored {
601    pub fn to_bool(self) -> bool {
602        match self {
603            Ignored::Yes => true,
604            Ignored::No => false,
605        }
606    }
607}
608
609impl From<bool> for Ignored {
610    fn from(value: bool) -> Self {
611        if value { Ignored::Yes } else { Ignored::No }
612    }
613}
614
615/// An attribute attaches metadata to an item.
616///
617/// Note that some of these attributes correspond to a Rust attribute, but some don't. For example,
618/// when annotating a function, a `#[flux::trusted]` is mapped to [`Attr::Trusted`] because it
619/// corresponds to metadata associated to the function, however, a `#[flux::spec(...)]` doesn't
620/// map to any [`Attr`] because that's considered to be part of the *refined syntax* of the item.
621///
622/// Note that these attributes can also originate from detached specs.
623#[derive(Debug)]
624pub enum Attr {
625    /// A `#[trusted(...)]` attribute
626    Trusted(Trusted),
627    /// A `#[trusted_impl(...)]` attribute
628    TrustedImpl(Trusted),
629    /// A `#[ignore(...)]` attribute
630    Ignore(Ignored),
631    /// A `#[proven_externally]` attribute
632    ProvenExternally(Span),
633    /// A `#[should_fail]` attribute
634    ShouldFail,
635    /// A `#[qualifiers(...)]` attribute
636    Qualifiers(Vec<Ident>),
637    /// A `#[reveal(...)]` attribute
638    Reveal(Vec<Ident>),
639    /// A `#[opts(...)]` attribute
640    InferOpts(PartialInferOpts),
641    /// A `#[no_panic]` attribute
642    NoPanic,
643}
644
645#[derive(Debug)]
646pub struct Path {
647    pub segments: Vec<PathSegment>,
648    pub refine: Vec<RefineArg>,
649    pub node_id: NodeId,
650    pub span: Span,
651}
652
653impl Path {
654    pub fn last(&self) -> &PathSegment {
655        self.segments
656            .last()
657            .expect("path must have at least one segment")
658    }
659}
660
661#[derive(Debug)]
662pub struct PathSegment {
663    pub ident: Ident,
664    pub args: Vec<GenericArg>,
665    pub node_id: NodeId,
666}
667
668#[derive(Debug)]
669pub struct GenericArg {
670    pub kind: GenericArgKind,
671    pub node_id: NodeId,
672}
673
674#[derive(Debug)]
675pub enum GenericArgKind {
676    Type(Ty),
677    Constraint(Ident, Ty),
678}
679
680#[derive(Debug)]
681pub struct FieldExpr {
682    pub ident: Ident,
683    pub expr: Expr,
684    pub span: Span,
685    pub node_id: NodeId,
686}
687
688#[derive(Debug)]
689pub struct Spread {
690    pub expr: Expr,
691    pub span: Span,
692    pub node_id: NodeId,
693}
694
695#[derive(Debug)]
696pub enum ConstructorArg {
697    FieldExpr(FieldExpr),
698    Spread(Spread),
699}
700
701#[derive(Debug)]
702pub struct Expr {
703    pub kind: ExprKind,
704    pub node_id: NodeId,
705    pub span: Span,
706}
707
708#[derive(Debug)]
709pub enum QuantKind {
710    Forall,
711    Exists,
712}
713
714#[derive(Debug)]
715pub enum ExprKind {
716    Path(ExprPath),
717    Dot(Box<Expr>, Ident),
718    Literal(Lit),
719    BinaryOp(BinOp, Box<[Expr; 2]>),
720    UnaryOp(UnOp, Box<Expr>),
721    Call(Box<Expr>, Vec<Expr>),
722    /// A UIF representing a PrimOp expression, e.g. `[<<](x, y)`
723    PrimUIF(BinOp),
724    /// `<qself as path>::name`
725    AssocReft(Box<Ty>, Path, Ident),
726    IfThenElse(Box<[Expr; 3]>),
727    Constructor(Option<ExprPath>, Vec<ConstructorArg>),
728    BoundedQuant(QuantKind, RefineParam, Range<usize>, Box<Expr>),
729    Block(Vec<LetDecl>, Box<Expr>),
730    /// Set expression `#{ e1, e2, ..., en }`
731    SetLiteral(Vec<Expr>),
732    /// Tuple expression `(e1, e2, ..., en)`
733    Tuple(Vec<Expr>),
734}
735
736#[derive(Debug)]
737pub struct LetDecl {
738    pub param: RefineParam,
739    pub init: Expr,
740}
741
742/// A [`Path`] but for refinement expressions
743#[derive(Debug, Clone)]
744pub struct ExprPath {
745    pub segments: Vec<ExprPathSegment>,
746    pub node_id: NodeId,
747    pub span: Span,
748}
749
750#[derive(Debug, Clone)]
751pub struct ExprPathSegment {
752    pub ident: Ident,
753    pub node_id: NodeId,
754}
755#[derive(Copy, Clone, Hash, Eq, PartialEq)]
756pub enum BinOp {
757    Iff,
758    Imp,
759    Or,
760    And,
761    Eq,
762    Ne,
763    Gt,
764    Ge,
765    Lt,
766    Le,
767    Add,
768    Sub,
769    Mul,
770    Div,
771    Mod,
772    BitOr,
773    BitXor,
774    BitAnd,
775    BitShl,
776    BitShr,
777}
778
779impl fmt::Debug for BinOp {
780    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
781        match self {
782            BinOp::Iff => write!(f, "<=>"),
783            BinOp::Imp => write!(f, "=>"),
784            BinOp::Or => write!(f, "||"),
785            BinOp::And => write!(f, "&&"),
786            BinOp::Eq => write!(f, "=="),
787            BinOp::Ne => write!(f, "!="),
788            BinOp::Lt => write!(f, "<"),
789            BinOp::Le => write!(f, "<="),
790            BinOp::Gt => write!(f, ">"),
791            BinOp::Ge => write!(f, ">="),
792            BinOp::Add => write!(f, "+"),
793            BinOp::Sub => write!(f, "-"),
794            BinOp::Mod => write!(f, "mod"),
795            BinOp::Mul => write!(f, "*"),
796            BinOp::Div => write!(f, "/"),
797            BinOp::BitOr => write!(f, "|"),
798            BinOp::BitXor => write!(f, "^"),
799            BinOp::BitAnd => write!(f, "&"),
800            BinOp::BitShl => write!(f, "<<"),
801            BinOp::BitShr => write!(f, ">>"),
802        }
803    }
804}
805
806impl rustc_errors::IntoDiagArg for BinOp {
807    fn into_diag_arg(self, _path: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
808        rustc_errors::DiagArgValue::Str(Cow::Owned(format!("{self:?}")))
809    }
810}
811
812#[derive(Copy, Clone)]
813pub enum UnOp {
814    Not,
815    Neg,
816}
817
818impl fmt::Debug for UnOp {
819    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
820        match self {
821            Self::Not => write!(f, "!"),
822            Self::Neg => write!(f, "-"),
823        }
824    }
825}
826
827impl BindKind {
828    pub fn token_str(&self) -> &'static str {
829        match self {
830            BindKind::At => "@",
831            BindKind::Pound => "#",
832        }
833    }
834}
835
836/// A punctuated sequence of values of type `T` separated by punctuation of type `P`
837pub struct Punctuated<T, P> {
838    inner: Vec<(T, P)>,
839    last: Option<Box<T>>,
840}
841
842impl<T, P> From<Vec<(T, P)>> for Punctuated<T, P> {
843    fn from(inner: Vec<(T, P)>) -> Self {
844        Self { inner, last: None }
845    }
846}
847
848impl<T, P> Punctuated<T, P> {
849    pub fn len(&self) -> usize {
850        self.inner.len() + self.last.is_some() as usize
851    }
852
853    /// Determines whether this punctuated sequence is empty, meaning it
854    /// contains no syntax tree nodes or punctuation.
855    pub fn is_empty(&self) -> bool {
856        self.inner.len() == 0 && self.last.is_none()
857    }
858
859    /// Appends a syntax tree node onto the end of this punctuated sequence. The
860    /// sequence must already have a trailing punctuation, or be empty.
861    ///
862    /// # Panics
863    ///
864    /// Panics if the sequence is nonempty and does not already have a trailing
865    /// punctuation.
866    pub fn push_value(&mut self, value: T) {
867        assert!(
868            self.empty_or_trailing(),
869            "Punctuated::push_value: cannot push value if Punctuated is missing trailing punctuation",
870        );
871
872        self.last = Some(Box::new(value));
873    }
874
875    /// Returns true if either this `Punctuated` is empty, or it has a trailing
876    /// punctuation.
877    ///
878    /// Equivalent to `punctuated.is_empty() || punctuated.trailing_punct()`.
879    pub fn empty_or_trailing(&self) -> bool {
880        self.last.is_none()
881    }
882
883    /// Determines whether this punctuated sequence ends with a trailing
884    /// punctuation.
885    pub fn trailing_punct(&self) -> bool {
886        self.last.is_none() && !self.is_empty()
887    }
888
889    pub fn into_values(self) -> Vec<T> {
890        let mut v: Vec<T> = self.inner.into_iter().map(|(v, _)| v).collect();
891        if let Some(last) = self.last {
892            v.push(*last);
893        }
894        v
895    }
896}
897
898impl Expr {
899    /// Collects all free variables in an expression.
900    /// A free variable is an `ExprKind::Path` with a single identifier segment.
901    pub fn free_vars(&self) -> FxHashSet<Ident> {
902        struct FreeVarsVisitor {
903            vars: FxHashSet<Ident>,
904        }
905
906        impl visit::Visitor for FreeVarsVisitor {
907            fn visit_expr(&mut self, expr: &Expr) {
908                if let ExprKind::Path(path) = &expr.kind {
909                    // Only collect paths with a single segment
910                    if let [segment] = path.segments.as_slice() {
911                        self.vars.insert(segment.ident);
912                    }
913                }
914                // Continue visiting child expressions
915                visit::walk_expr(self, expr);
916            }
917        }
918
919        let mut visitor = FreeVarsVisitor { vars: FxHashSet::default() };
920        visitor.visit_expr(self);
921        visitor.vars
922    }
923}