HaxeCheckstyle / haxeparser

A Haxe parser for Haxe
61 stars 23 forks source link

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

Open arotter opened 2 years ago

arotter commented 2 years ago

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) ```haxe { 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: '', min: 34, max: 45 } }, { name: 'bar', meta: [ { name: ':optional', params: [], pos: { file: '', 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: '', min: 48, max: 56 } }, { name: 'baz', meta: [ { name: ':optional', params: [], pos: { file: '', 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: '', min: 59, max: 69 } } ], __enum__: 'haxe.macro.ComplexType', toString: [Function: $estr] } } ```
TestB gets parsed as (code snippet under fold) ```haxe { name: 'TestB', doc: '', meta: [], params: [], flags: [], data: { _hx_index: 2, fields: [ { name: 'foo', doc: '', meta: [], access: [], pos: { file: '', 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: '', 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: '', min: 123, max: 132 } } ], access: [], pos: { file: '', 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.