Repository: HigherOrderCO/Bend
File: src/term/mod.rs (Rust)

Code before:
          let can_subst;

          if let RulePat::Num(MatchNum::Succ(Some(nam))) = rule {
            can_subst = nam != from
          } else {
            can_subst = true
          };

          if can_subst {
            term.subst(from, to);
          }
        }
      }
      Term::App { fun: fst, arg: snd }
      | Term::Sup { fst, snd }
      | Term::Tup { fst, snd }
      | Term::Opx { fst, snd, .. } => {
        fst.subst(from, to);
        snd.subst(from, to);
      }
      Term::Ref { .. } | Term::Num { .. } | Term::Era => (),
    }
  }

  /// Collects all the free variables that a term has
  pub fn free_vars(&self, free_vars: &mut IndexMap<Name, u64>) {
    match self {
      Term::Lam { nam: Some(nam), bod } => {
        let mut new_scope = IndexMap::new();
        bod.free_vars(&mut new_scope);
        new_scope.remove(nam);

        free_vars.extend(new_scope);
      }
      Term::Lam { nam: None, bod } => bod.free_vars(free_vars),
      Term::Var { nam } => *free_vars.entry(nam.clone()).or_default() += 1,
      Term::Chn { bod, .. } => bod.free_vars(free_vars),
      Term::Lnk { .. } => {}
      Term::Let { pat: LetPat::Var(nam), val, nxt } => {
        val.free_vars(free_vars);

        let mut new_scope = IndexMap::new();
        nxt.free_vars(&mut new_scope);

        new_scope.remove(nam);

        free_vars.extend(new_scope);
      }
      Term::Let { pat: LetPat::Tup(fst, snd), val, nxt } | Term::Dup { fst, snd, val, nxt, .. } => {
        val.free_vars(free_vars);


Review comment (suggestion):
You should make this function return the map, and not receive it as a mutable reference. That's how it's used anyway, with this argument always being set by this function.
You can make the recursive part a subfunction of a wrapper that simply allocates the map, calls the recursive function and then returns it

Code after:
        for (rule, term) in arms {
          let can_subst;

          if let RulePat::Num(MatchNum::Succ(Some(nam))) = rule {
            can_subst = nam != from
          } else {
            can_subst = true
          };

          if can_subst {
            term.subst(from, to);
          }
        }
      }
      Term::App { fun: fst, arg: snd }
      | Term::Sup { fst, snd, .. }
      | Term::Tup { fst, snd }
      | Term::Opx { fst, snd, .. } => {
        fst.subst(from, to);
        snd.subst(from, to);
      }
      Term::Ref { .. } | Term::Num { .. } | Term::Era => (),
    }
  }

  /// Collects all the free variables that a term has
  /// and the number of times each var is used
  pub fn free_vars(&self) -> IndexMap<Name, u64> {
    fn go(term: &Term, free_vars: &mut IndexMap<Name, u64>) {
      match term {
        Term::Lam { nam: Some(nam), bod } => {
          let mut new_scope = IndexMap::new();
          go(bod, &mut new_scope);
          new_scope.remove(nam);

          free_vars.extend(new_scope);
        }
        Term::Lam { nam: None, bod } => go(bod, free_vars),
        Term::Var { nam } => *free_vars.entry(nam.clone()).or_default() += 1,
        Term::Chn { bod, .. } => go(bod, free_vars),
        Term::Lnk { .. } => {}
        Term::Let { pat: LetPat::Var(nam), val, nxt } => {
          go(val, free_vars);

          let mut new_scope = IndexMap::new();
          go(nxt, &mut new_scope);

          new_scope.remove(nam);

          free_vars.extend(new_scope);
        }