private function generateRewriteChildrenMethod()

in src/__Private/codegen/CodegenSyntax.hack [479:560]


  private function generateRewriteChildrenMethod(
    Schema\TAST $syntax,
  ): CodegenMethod {
    $cg = $this->getCodegenFactory();

    $fields = Vec\map($syntax['fields'], $field ==> $field['field_name']);

    return $cg
      ->codegenMethod('rewriteChildren<Tret as ?Node>')
      ->setIsOverride()
      ->addParameter('(function(Node, vec<Node>): Tret) $rewriter')
      ->addParameter('vec<Node> $parents = vec[]')
      ->setReturnType('this')
      ->setBody(
        $cg
          ->codegenHackBuilder()
          ->addLine('$parents[] = $this;')
          ->addLines(
            Vec\map(
              $fields,
              $field ==> {
                $spec = $this->getTypeSpecForField($syntax, $field);
                if (!$spec['nullable']) {
                  return Str\format(
                    '$%s = $rewriter($this->_%s, $parents);',
                    $field,
                    $field,
                  );
                }
                return Str\format(
                  '$%s = $this->_%s === null '.
                  '? null : $rewriter($this->_%s, $parents);',
                  $field,
                  $field,
                  $field,
                );
              },
            ),
          )
          ->addLine('if (')
          ->indent()
          ->addLines(
            Vec\map(
              $fields,
              $field ==> Str\format('$%s === $this->_%s &&', $field, $field),
            )
              |> (
                (vec<string> $lines) ==> {
                  $idx = C\last_keyx($lines);
                  $lines[$idx] = Str\strip_suffix($lines[$idx], ' &&');
                  return $lines;
                }
              )($$),
          )
          ->unindent()
          ->addLine(') {')
          ->indent()
          ->addLine('return $this;')
          ->unindent()
          ->addLine('}')
          ->add('return ')
          ->addMultilineCall(
            'new static',
            Vec\map(
              $fields,
              $field ==> {
                $type = $this->getTypeSpecForField($syntax, $field)
                  |> $$['nullable'] ? '?'.$$['class'] : $$['class'];
                $enforceable = Str\format('$%s as %s', $field, $type);
                $not_enforceable = Str\format(
                  '/* HH_FIXME[4110] %s may not be enforceable */ $%s',
                  $type,
                  $field,
                );
                return $type
                  |> Str\contains($$, '<') ? $not_enforceable : $enforceable;
              },
            ),
          )
          ->getCode(),
      );
  }