///|
/// Returns whether any immediate expression child of `expr` satisfies `visit`.
///
/// Immediate children are the expression-valued edges represented directly by
/// the expression node: operands, arguments, literal elements and properties,
/// template substitutions, destructuring RHS expressions, and a class's
/// superclass. Function bodies, parameter lists, static blocks, and class
/// member containers are not edges of this traversal. In particular,
/// `ClassExpr` emits only its optional superclass; consumers with class-specific
/// policies must handle class members locally.
///
/// The node itself is not passed to `visit`, and this function never recurses.
/// Children are visited left-to-right and stop at the first `true` result.
pub fn expr_immediate_children_any(expr : Expr, visit : (Expr) -> Bool) -> Bool {
  match expr {
    Binary(_, left, right, _)
    | Comma(left, right, _)
    | ComputedMember(left, right, _)
    | OptionalComputedMember(left, right, _)
    | ChainComputedMember(left, right, _)
    | SuperComputedAssign(left, right, _) => visit(left) || visit(right)
    ComputedAssign(obj, key, value, _) =>
      visit(obj) || visit(key) || visit(value)
    Unary(_, inner, _)
    | Grouping(inner, _)
    | SpreadExpr(inner, _)
    | AwaitExpr(inner, _)
    | Member(inner, _, _)
    | OptionalMember(inner, _, _)
    | ChainMember(inner, _, _)
    | PrivateMember(inner, _, _)
    | Assign(_, inner, _)
    | UpdateExpr(_, inner, _, _)
    | SuperComputedMember(inner, _)
    | SuperMemberAssign(_, inner, _) => visit(inner)
    MemberAssign(obj, _, value, _)
    | PrivateMemberAssign(obj, _, value, _)
    | CompoundAssign(_, obj, value, _)
    | WebCompatCallAssign(obj, value, _) => visit(obj) || visit(value)
    Ternary(condition, then_expr, else_expr, _) =>
      visit(condition) || visit(then_expr) || visit(else_expr)
    Call(callee, args, _)
    | NewExpr(callee, args, _)
    | OptionalCall(callee, args, _) =>
      if visit(callee) {
        true
      } else {
        for arg in args {
          if visit(arg) {
            break true
          }
        } nobreak {
          false
        }
      }
    SuperCall(args, _) =>
      for arg in args {
        if visit(arg) {
          break true
        }
      } nobreak {
        false
      }
    ArrayLit(elements, _) =>
      for element in elements {
        if visit(element) {
          break true
        }
      } nobreak {
        false
      }
    ObjectLit(properties, _) =>
      for property in properties {
        if visit(property.key) || visit(property.value) {
          break true
        }
      } nobreak {
        false
      }
    TemplateLit(_, expressions, _) =>
      for expression in expressions {
        if visit(expression) {
          break true
        }
      } nobreak {
        false
      }
    TaggedTemplate(tag, _, expressions, _) =>
      if visit(tag) {
        true
      } else {
        for expression in expressions {
          if visit(expression) {
            break true
          }
        } nobreak {
          false
        }
      }
    DestructureAssign(_, value, _) => visit(value)
    YieldExpr(argument, _, _) =>
      match argument {
        Some(argument) => visit(argument)
        None => false
      }
    ClassExpr(_, superclass, _, _, _) =>
      match superclass {
        Some(superclass) => visit(superclass)
        None => false
      }
    NumberLit(_, _, _)
    | StringLit(_, _, _, _)
    | BoolLit(_, _)
    | NullLit(_)
    | UndefinedLit(_)
    | ArrayHole(_)
    | Ident(_, _)
    | ThisExpr(_)
    | RegexLit(_, _, _)
    | PrivateIdent(_, _)
    | FuncExpr(_, _, _, _, _)
    | FuncExprExt(_, _, _, _, _, _)
    | ArrowFunc(_, _, _, _)
    | ArrowFuncExt(_, _, _, _, _)
    | GeneratorExpr(_, _, _, _, _)
    | GeneratorExprExt(_, _, _, _, _, _)
    | AsyncFuncExpr(_, _, _, _, _)
    | AsyncFuncExprExt(_, _, _, _, _, _)
    | AsyncArrowFunc(_, _, _, _)
    | AsyncArrowFuncExt(_, _, _, _, _)
    | AsyncGeneratorExpr(_, _, _, _, _)
    | AsyncGeneratorExprExt(_, _, _, _, _, _)
    | SuperMember(_, _)
    | NewTargetExpr(_) => false
  }
}

///|
/// Returns whether any immediate pattern child of `pattern` satisfies
/// `visit_pattern` or any embedded expression child satisfies `visit_expr`.
///
/// Immediate pattern children are nested patterns, array/rest elements,
/// object/rest values, and assignment targets. Embedded expression edges are
/// object computed keys and defaults, pattern defaults, and assignment-target
/// expressions. Children are visited in source order, left-to-right, and stop
/// at the first `true` result. This function never recurses and allocates no
/// child collection.
pub fn pattern_immediate_children_any(
  pattern : Pattern,
  visit_pattern : (Pattern) -> Bool,
  visit_expr : (Expr) -> Bool,
) -> Bool {
  match pattern {
    IdentPat(_) => false
    DefaultPat(inner, default_expr) =>
      visit_pattern(inner) || visit_expr(default_expr)
    ArrayPat(elements, rest) => {
      let elements_result = for element in elements {
        match element {
          Some(element) => if visit_pattern(element) { break true }
          None => ()
        }
      } nobreak {
        false
      }
      if elements_result {
        true
      } else {
        match rest {
          Some(rest) => visit_pattern(rest)
          None => false
        }
      }
    }
    ObjectPat(properties, rest) => {
      let properties_result = for property in properties {
        if (match property.computed_key {
            Some(key) => visit_expr(key)
            None => false
          }) {
          break true
        }
        if visit_pattern(property.value) {
          break true
        }
        if (match property.default_val {
            Some(default_expr) => visit_expr(default_expr)
            None => false
          }) {
          break true
        }
      } nobreak {
        false
      }
      if properties_result {
        true
      } else {
        match rest {
          Some(rest) => visit_pattern(rest)
          None => false
        }
      }
    }
    AssignTarget(expr) => visit_expr(expr)
  }
}