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