Skip to content

Inconsistent parsing of optional fields in typedefs when using class notation #65

@arotter

Description

@arotter

Description

I have a test file with two typedefs, one using object notation and one using class notation. When parsing that file the resulting data is inconsistent in retaining information about optional fields depending on which notation was used. Object notation works fine, class notation is misbehaving.

package test;

typedef TestA = {
	foo:String,
	?bar:Int,
	?baz:Bool
}

typedef TestB = {
	var foo:String;
	var ?bar:Int;
	@:optional var baz:Bool;
}

Current result

TestA gets parsed as (code snippet under fold)
{
  name: 'TestA',
  doc: '',
  meta: [],
  params: [],
  flags: [],
  data: {
    _hx_index: 2,
    fields: [
      {
        name: 'foo',
        meta: [],
        access: [],
        doc: null,
        kind: {
          _hx_index: 0,
          t: {
            _hx_index: 0,
            p: { pack: [], name: 'String', params: [], sub: null },
            __enum__: 'haxe.macro.ComplexType',
            toString: [Function: $estr]
          },
          e: null,
          __enum__: 'haxe.macro.FieldType',
          toString: [Function: $estr]
        },
        pos: { file: '<null>', min: 34, max: 45 }
      },
      {
        name: 'bar',
        meta: [
          {
            name: ':optional',
            params: [],
            pos: { file: '<null>', min: 48, max: 51 }
          }
        ],
        access: [],
        doc: null,
        kind: {
          _hx_index: 0,
          t: {
            _hx_index: 0,
            p: {
              pack: [],
              name: 'Null',
              sub: null,
              params: [
                {
                  _hx_index: 0,
                  t: {
                    _hx_index: 0,
                    p: { pack: [], name: 'Int', params: [], sub: null },
                    __enum__: 'haxe.macro.ComplexType',
                    toString: [Function: $estr]
                  },
                  __enum__: 'haxe.macro.TypeParam',
                  toString: [Function: $estr]
                }
              ]
            },
            __enum__: 'haxe.macro.ComplexType',
            toString: [Function: $estr]
          },
          e: null,
          __enum__: 'haxe.macro.FieldType',
          toString: [Function: $estr]
        },
        pos: { file: '<null>', min: 48, max: 56 }
      },
      {
        name: 'baz',
        meta: [
          {
            name: ':optional',
            params: [],
            pos: { file: '<null>', min: 59, max: 62 }
          }
        ],
        access: [],
        doc: null,
        kind: {
          _hx_index: 0,
          t: {
            _hx_index: 0,
            p: {
              pack: [],
              name: 'Null',
              sub: null,
              params: [
                {
                  _hx_index: 0,
                  t: {
                    _hx_index: 0,
                    p: { pack: [], name: 'Bool', params: [], sub: null },
                    __enum__: 'haxe.macro.ComplexType',
                    toString: [Function: $estr]
                  },
                  __enum__: 'haxe.macro.TypeParam',
                  toString: [Function: $estr]
                }
              ]
            },
            __enum__: 'haxe.macro.ComplexType',
            toString: [Function: $estr]
          },
          e: null,
          __enum__: 'haxe.macro.FieldType',
          toString: [Function: $estr]
        },
        pos: { file: '<null>', min: 59, max: 69 }
      }
    ],
    __enum__: 'haxe.macro.ComplexType',
    toString: [Function: $estr]
  }
}
TestB gets parsed as (code snippet under fold)
{
  name: 'TestB',
  doc: '',
  meta: [],
  params: [],
  flags: [],
  data: {
    _hx_index: 2,
    fields: [
      {
        name: 'foo',
        doc: '',
        meta: [],
        access: [],
        pos: { file: '<null>', min: 90, max: 105 },
        kind: {
          _hx_index: 0,
          t: {
            _hx_index: 0,
            p: { pack: [], name: 'String', params: [], sub: null },
            __enum__: 'haxe.macro.ComplexType',
            toString: [Function: $estr]
          },
          e: null,
          __enum__: 'haxe.macro.FieldType',
          toString: [Function: $estr]
        }
      },
      {
        name: 'bar',
        doc: '',
        meta: [],
        access: [],
        pos: { file: '<null>', min: 107, max: 120 },
        kind: {
          _hx_index: 0,
          t: {
            _hx_index: 0,
            p: { pack: [], name: 'Int', params: [], sub: null },
            __enum__: 'haxe.macro.ComplexType',
            toString: [Function: $estr]
          },
          e: null,
          __enum__: 'haxe.macro.FieldType',
          toString: [Function: $estr]
        }
      },
      {
        name: 'baz',
        doc: '',
        meta: [
          {
            name: ':optional',
            params: [],
            pos: { file: '<null>', min: 123, max: 132 }
          }
        ],
        access: [],
        pos: { file: '<null>', min: 133, max: 146 },
        kind: {
          _hx_index: 0,
          t: {
            _hx_index: 0,
            p: { pack: [], name: 'Bool', params: [], sub: null },
            __enum__: 'haxe.macro.ComplexType',
            toString: [Function: $estr]
          },
          e: null,
          __enum__: 'haxe.macro.FieldType',
          toString: [Function: $estr]
        }
      }
    ],
    __enum__: 'haxe.macro.ComplexType',
    toString: [Function: $estr]
  }
}

The parse data for TestA contains the expected :optional metadata for the optional fields, and their respective types come down to Null<X> which was a bit surprising for me at that moment but is still fine.

However, the parse data for TestB::bar does not contain any information about it being optional (no typing as Null<Int>, no :optional entry in meta), which afaics makes it impossible to know whether that field was optional or not. Additionally, TestB::baz is not the same type as TestA::baz (Bool vs Null<Bool>) despite carrying the :optional metadata.

Using the ? to indicate optional fields should be valid in class notation according to the documentation.

Expected result

Information about fields being optional should be retained regardless of notation used. Also, the data should follow a consistent pattern: if optional fields are typed as Null<X> in one notation then they should also be typed as such when using the other notation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions