The @:op(a.b)
feature is described here: https://haxe.io/releases/3.3.0/
I have May<T>
abstract which is used for null safety. Here is the simplified version of it:
package;
import haxe.macro.Expr;
abstract May<T>(Null<T>) from(Null<T>){
// convert T, Null<T> or May<T> to May<T>
// accepts Null<T because of 'from(Null<T>)'
// accepts T because Null<T> has 'from(T)'
// we need to accept May<T> to avoid returning May<May<T>> from resolve() if field is already a May
public static inline function from<T>(t:May<T>):May<T> return t;
public inline function exist():Bool return this != null;
public inline function unwrap():T return exist() ? unsafeUnwrap() : throw 'unwrap null';
public inline function unwrapOr(defaultValue:T):T return exist() ? unsafeUnwrap() : defaultValue;
public inline function unsafeUnwrap():T return this;
// return same field from underlying type but as May<FieldType>
@:op(a.b) static macro function resolve<T>(ethis:ExprOf<May<T>>, name:String):Expr {
return macro {
var me = $ethis;
var result = May.from(me.exist() ? me.unsafeUnwrap().$name : null);
result;
}
}
}
Note the resolve()
function. It's the new feature that I want to add to my actual May
abstract. It allows to safely get fields from May
and call unwrap()
only once. For example:
may.exist() ? may.unwrap().someField : defaultValue
becomes
may.someField.unwrapOr(defaultValue)
That's very handy and works good. But the completion does not work. It only gives fields from May
: unwrap()
, exist()
etc., but no fields from the underlying class.
I've decided to add @:forward
metadata for completion:
#if display @:forward #end
This makes the compiler see all fields during completion. It's better than nothing, but fields have an incorrect type: T
instead of May<T>
, so I do not get completion for May
fields.
I understand why the compiler can't know all possible fields when using @:op(a.b)
, but maybe there is some more clever trick that will help?