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(Debug)]
540pub struct ConstArg {
541    pub kind: ConstArgKind,
542    pub span: Span,
543}
544
545#[derive(Debug)]
546pub enum ConstArgKind {
547    Lit(usize),
548    Path(Path),
549    Infer,
550}
551
552#[derive(Debug)]
553pub struct Indices {
554    pub indices: Vec<RefineArg>,
555    pub span: Span,
556}
557
558#[derive(Debug)]
559pub enum RefineArg {
560    /// `@n` or `#n`, the span corresponds to the span of the identifier plus the binder token (`@` or `#`)
561    Bind(Ident, BindKind, Span, NodeId),
562    Expr(Expr),
563    Abs(RefineParams, Expr, Span, NodeId),
564}
565
566#[derive(Debug, Clone, Copy)]
567pub enum BindKind {
568    At,
569    Pound,
570}
571
572/// A boolean-like enum used to mark whether some code should be trusted.
573#[derive(Debug, Eq, PartialEq, Copy, Clone)]
574pub enum Trusted {
575    Yes,
576    No,
577}
578
579impl Trusted {
580    pub fn to_bool(self) -> bool {
581        match self {
582            Trusted::Yes => true,
583            Trusted::No => false,
584        }
585    }
586}
587
588impl From<bool> for Trusted {
589    fn from(value: bool) -> Self {
590        if value { Trusted::Yes } else { Trusted::No }
591    }
592}
593
594/// A boolean-like enum used to mark whether a piece of code is ignored.
595#[derive(Debug, Eq, PartialEq, Copy, Clone)]
596pub enum Ignored {
597    Yes,
598    No,
599}
600
601impl Ignored {
602    pub fn to_bool(self) -> bool {
603        match self {
604            Ignored::Yes => true,
605            Ignored::No => false,
606        }
607    }
608}
609
610impl From<bool> for Ignored {
611    fn from(value: bool) -> Self {
612        if value { Ignored::Yes } else { Ignored::No }
613    }
614}
615
616/// An attribute attaches metadata to an item.
617///
618/// Note that some of these attributes correspond to a Rust attribute, but some don't. For example,
619/// when annotating a function, a `#[flux::trusted]` is mapped to [`Attr::Trusted`] because it
620/// corresponds to metadata associated to the function, however, a `#[flux::spec(...)]` doesn't
621/// map to any [`Attr`] because that's considered to be part of the *refined syntax* of the item.
622///
623/// Note that these attributes can also originate from detached specs.
624#[derive(Debug)]
625pub enum Attr {
626    /// A `#[trusted(...)]` attribute
627    Trusted(Trusted),
628    /// A `#[trusted_impl(...)]` attribute
629    TrustedImpl(Trusted),
630    /// A `#[ignore(...)]` attribute
631    Ignore(Ignored),
632    /// A `#[proven_externally]` attribute
633    ProvenExternally(Span),
634    /// A `#[should_fail]` attribute
635    ShouldFail,
636    /// A `#[qualifiers(...)]` attribute
637    Qualifiers(Vec<Ident>),
638    /// A `#[reveal(...)]` attribute
639    Reveal(Vec<Ident>),
640    /// A `#[opts(...)]` attribute
641    InferOpts(PartialInferOpts),
642    /// A `#[no_panic]` attribute
643    NoPanic,
644    /// A `#[assume_parametric(...)]` attribute
645    AssumeParametric(Vec<Ident>),
646}
647
648#[derive(Debug)]
649pub struct Path {
650    pub segments: Vec<PathSegment>,
651    pub refine: Vec<RefineArg>,
652    pub node_id: NodeId,
653    pub span: Span,
654}
655
656impl Path {
657    pub fn last(&self) -> &PathSegment {
658        self.segments
659            .last()
660            .expect("path must have at least one segment")
661    }
662}
663
664#[derive(Debug)]
665pub struct PathSegment {
666    pub ident: Ident,
667    pub args: Vec<GenericArg>,
668    pub node_id: NodeId,
669}
670
671#[derive(Debug)]
672pub struct GenericArg {
673    pub kind: GenericArgKind,
674    pub node_id: NodeId,
675}
676
677#[derive(Debug)]
678pub enum GenericArgKind {
679    Type(Ty),
680    Constraint(Ident, Ty),
681}
682
683#[derive(Debug)]
684pub struct FieldExpr {
685    pub ident: Ident,
686    pub expr: RefineArg,
687    pub span: Span,
688    pub node_id: NodeId,
689}
690
691#[derive(Debug)]
692pub struct Spread {
693    pub expr: Expr,
694    pub span: Span,
695    pub node_id: NodeId,
696}
697
698#[derive(Debug)]
699pub enum ConstructorArg {
700    FieldExpr(FieldExpr),
701    Spread(Spread),
702}
703
704#[derive(Debug)]
705pub struct Expr {
706    pub kind: ExprKind,
707    pub node_id: NodeId,
708    pub span: Span,
709}
710
711#[derive(Debug)]
712pub enum QuantKind {
713    Forall,
714    Exists,
715}
716
717#[derive(Debug)]
718pub enum ExprKind {
719    Path(ExprPath),
720    Dot(Box<Expr>, Ident),
721    Literal(Lit),
722    BinaryOp(BinOp, Box<[Expr; 2]>),
723    UnaryOp(UnOp, Box<Expr>),
724    Call(Box<Expr>, Vec<Expr>),
725    /// A UIF representing a PrimOp expression, e.g. `[<<](x, y)`
726    PrimUIF(BinOp),
727    /// `<qself as path>::name`
728    AssocReft(Box<Ty>, Path, Ident),
729    IfThenElse(Box<[Expr; 3]>),
730    Constructor(Option<ExprPath>, Vec<ConstructorArg>),
731    BoundedQuant(QuantKind, RefineParam, Range<usize>, Box<Expr>),
732    Block(Vec<LetDecl>, Box<Expr>),
733    /// Set expression `#{ e1, e2, ..., en }`
734    SetLiteral(Vec<Expr>),
735    /// Tuple expression `(e1, e2, ..., en)`
736    Tuple(Vec<Expr>),
737}
738
739#[derive(Debug)]
740pub struct LetDecl {
741    pub param: RefineParam,
742    pub init: Expr,
743}
744
745/// A [`Path`] but for refinement expressions
746#[derive(Debug, Clone)]
747pub struct ExprPath {
748    pub segments: Vec<ExprPathSegment>,
749    pub node_id: NodeId,
750    pub span: Span,
751}
752
753#[derive(Debug, Clone)]
754pub struct ExprPathSegment {
755    pub ident: Ident,
756    pub node_id: NodeId,
757}
758#[derive(Copy, Clone, Hash, Eq, PartialEq)]
759pub enum BinOp {
760    Iff,
761    Imp,
762    Or,
763    And,
764    Eq,
765    Ne,
766    Gt,
767    Ge,
768    Lt,
769    Le,
770    Add,
771    Sub,
772    Mul,
773    Div,
774    Mod,
775    BitOr,
776    BitXor,
777    BitAnd,
778    BitShl,
779    BitShr,
780}
781
782impl fmt::Debug for BinOp {
783    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
784        match self {
785            BinOp::Iff => write!(f, "<=>"),
786            BinOp::Imp => write!(f, "=>"),
787            BinOp::Or => write!(f, "||"),
788            BinOp::And => write!(f, "&&"),
789            BinOp::Eq => write!(f, "=="),
790            BinOp::Ne => write!(f, "!="),
791            BinOp::Lt => write!(f, "<"),
792            BinOp::Le => write!(f, "<="),
793            BinOp::Gt => write!(f, ">"),
794            BinOp::Ge => write!(f, ">="),
795            BinOp::Add => write!(f, "+"),
796            BinOp::Sub => write!(f, "-"),
797            BinOp::Mod => write!(f, "mod"),
798            BinOp::Mul => write!(f, "*"),
799            BinOp::Div => write!(f, "/"),
800            BinOp::BitOr => write!(f, "|"),
801            BinOp::BitXor => write!(f, "^"),
802            BinOp::BitAnd => write!(f, "&"),
803            BinOp::BitShl => write!(f, "<<"),
804            BinOp::BitShr => write!(f, ">>"),
805        }
806    }
807}
808
809impl rustc_errors::IntoDiagArg for BinOp {
810    fn into_diag_arg(self, _path: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
811        rustc_errors::DiagArgValue::Str(Cow::Owned(format!("{self:?}")))
812    }
813}
814
815#[derive(Copy, Clone)]
816pub enum UnOp {
817    Not,
818    Neg,
819}
820
821impl fmt::Debug for UnOp {
822    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
823        match self {
824            Self::Not => write!(f, "!"),
825            Self::Neg => write!(f, "-"),
826        }
827    }
828}
829
830impl BindKind {
831    pub fn token_str(&self) -> &'static str {
832        match self {
833            BindKind::At => "@",
834            BindKind::Pound => "#",
835        }
836    }
837}
838
839/// A punctuated sequence of values of type `T` separated by punctuation of type `P`
840pub struct Punctuated<T, P> {
841    inner: Vec<(T, P)>,
842    last: Option<Box<T>>,
843}
844
845impl<T, P> From<Vec<(T, P)>> for Punctuated<T, P> {
846    fn from(inner: Vec<(T, P)>) -> Self {
847        Self { inner, last: None }
848    }
849}
850
851impl<T, P> Punctuated<T, P> {
852    pub fn len(&self) -> usize {
853        self.inner.len() + self.last.is_some() as usize
854    }
855
856    /// Determines whether this punctuated sequence is empty, meaning it
857    /// contains no syntax tree nodes or punctuation.
858    pub fn is_empty(&self) -> bool {
859        self.inner.len() == 0 && self.last.is_none()
860    }
861
862    /// Appends a syntax tree node onto the end of this punctuated sequence. The
863    /// sequence must already have a trailing punctuation, or be empty.
864    ///
865    /// # Panics
866    ///
867    /// Panics if the sequence is nonempty and does not already have a trailing
868    /// punctuation.
869    pub fn push_value(&mut self, value: T) {
870        assert!(
871            self.empty_or_trailing(),
872            "Punctuated::push_value: cannot push value if Punctuated is missing trailing punctuation",
873        );
874
875        self.last = Some(Box::new(value));
876    }
877
878    /// Returns true if either this `Punctuated` is empty, or it has a trailing
879    /// punctuation.
880    ///
881    /// Equivalent to `punctuated.is_empty() || punctuated.trailing_punct()`.
882    pub fn empty_or_trailing(&self) -> bool {
883        self.last.is_none()
884    }
885
886    /// Determines whether this punctuated sequence ends with a trailing
887    /// punctuation.
888    pub fn trailing_punct(&self) -> bool {
889        self.last.is_none() && !self.is_empty()
890    }
891
892    pub fn into_values(self) -> Vec<T> {
893        let mut v: Vec<T> = self.inner.into_iter().map(|(v, _)| v).collect();
894        if let Some(last) = self.last {
895            v.push(*last);
896        }
897        v
898    }
899}
900
901impl Expr {
902    /// Collects all free variables in an expression.
903    /// A free variable is an `ExprKind::Path` with a single identifier segment.
904    pub fn free_vars(&self) -> FxHashSet<Ident> {
905        struct FreeVarsVisitor {
906            vars: FxHashSet<Ident>,
907        }
908
909        impl visit::Visitor for FreeVarsVisitor {
910            fn visit_expr(&mut self, expr: &Expr) {
911                if let ExprKind::Path(path) = &expr.kind {
912                    // Only collect paths with a single segment
913                    if let [segment] = path.segments.as_slice() {
914                        self.vars.insert(segment.ident);
915                    }
916                }
917                // Continue visiting child expressions
918                visit::walk_expr(self, expr);
919            }
920        }
921
922        let mut visitor = FreeVarsVisitor { vars: FxHashSet::default() };
923        visitor.visit_expr(self);
924        visitor.vars
925    }
926}