private function generateFieldMethods()

in src/__Private/codegen/CodegenSyntax.hack [211:322]


  private function generateFieldMethods(
    Schema\TAST $syntax,
    string $underscored,
  ): Traversable<CodegenMethod> {
    $spec = $this->getTypeSpecForField($syntax, $underscored);
    $upper_camel = StrP\upper_camel($underscored);
    $types = $spec['possibleTypes'];
    $type = $spec['nullable'] ? ('?'.$spec['class']) : $spec['class'];
    // AttributeAsAttributeSpecTrait declares some abstract methods
    // we need to <<__Override>> them in these classes.
    $needs_override_for_attribute_methods = (
      $syntax['kind_name'] === 'ParameterDeclaration' ||
      $syntax['kind_name'] === 'ClassishDeclaration'
    ) &&
      $upper_camel === 'Attribute';

    $patch_in_override = (CodegenMethod $method): CodegenMethod ==> {
      if ($needs_override_for_attribute_methods) {
        $method->addEmptyUserAttribute('__Override');
      }
      return $method;
    };

    $cg = $this->getCodegenFactory();
    yield $cg
      ->codegenMethodf('get%sUNTYPED', $upper_camel)
      ->setReturnType('?Node')
      ->setBodyf('return $this->_%s;', $underscored)
      |> $patch_in_override($$);

    yield $cg
      ->codegenMethodf('with%s', $upper_camel)
      ->setReturnType('this')
      ->addParameterf('%s $value', $type)
      ->setBody(
        $cg
          ->codegenHackBuilder()
          ->startIfBlockf('$value === $this->_%s', $underscored)
          ->addReturnf('$this')
          ->endIfBlock()
          ->add('return new ')
          ->addMultilineCall(
            'static',
            Vec\map(
              $syntax['fields'],
              $inner ==> $inner['field_name'] === $underscored
                ? '$value'
                : '$this->_'.$inner['field_name'],
            ),
          )
          ->getCode(),
      );

    if ($spec['nullable']) {
      yield $cg
        ->codegenMethodf('has%s', $upper_camel)
        ->setReturnType('bool')
        ->setBodyf('return $this->_%s !== null;', $underscored)
        |> $patch_in_override($$);
    } else {
      yield $cg
        ->codegenMethodf('has%s', $upper_camel)
        ->setReturnType('bool')
        ->setBody('return true;');
    }

    if (!$spec['nullable']) {
      $get = $cg
        ->codegenMethodf('get%s', $upper_camel)
        ->setDocBlock('@return '.Str\join($types, ' | '))
        ->setReturnType($type);
      if ($spec['class'] === 'Node') {
        yield $get->setBodyf('return $this->_%s;', $underscored);
      } else {
        yield $get->setBodyf(
          'return TypeAssert\instance_of(%s::class, $this->_%s);',
          $type |> Str\split($$, '<') |> C\firstx($$),
          $underscored,
        );
      }
      // For backwards compatibility: always offer getFoox, in case it was
      // nullable in a previous version
      yield $cg
        ->codegenMethodf('get%sx', $upper_camel)
        ->setDocBlock('@return '.Str\join($types, ' | '))
        ->setReturnType($type)
        ->setBodyf('return $this->get%s();', $upper_camel);
      return;
    }

    yield $cg
      ->codegenMethodf('get%s', $upper_camel)
      ->setDocBlock(
        Vec\map($types, $type ==> $type === 'Missing' ? 'null' : $type)
          |> Str\join($$, ' | ')
          |> '@return '.$$,
      )
      ->setReturnType($type)
      ->setBodyf('return $this->_%s;', $underscored)
      |> $patch_in_override($$);

    yield $cg
      ->codegenMethodf('get%sx', $upper_camel)
      ->setDocBlock(
        Vec\filter($types, $type ==> $type !== 'Missing')
          |> Str\join($$, ' | ')
          |> '@return '.$$,
      )
      ->setReturnType($spec['class'])
      ->setBodyf('return TypeAssert\\not_null($this->get%s());', $upper_camel)
      |> $patch_in_override($$);
  }