Compare commits

...

26 Commits

Author SHA1 Message Date
Juraj Kirchheim
bc9f7460c2 Better, I guess. 2021-03-14 14:25:36 +01:00
Juraj Kirchheim
043df0f47d Try a more deterministic approach to build cache. 2021-03-14 14:11:35 +01:00
Juraj Kirchheim
7e2dfad607 Make TypeMap work exacly with anons. 2021-03-14 13:25:20 +01:00
Juraj Kirchheim
94d02c91a8 Haxe 3 support. 2021-03-14 12:59:38 +01:00
Juraj Kirchheim
99a1fc3fa6 Exact type to string conversion. 2021-03-14 12:58:10 +01:00
Juraj Kirchheim
a383f7692b Argh ... 2021-02-25 11:37:04 +01:00
Juraj Kirchheim
5e5781e8bc Faster intersection for haxe4. 2021-02-25 11:36:26 +01:00
Juraj Kirchheim
63ce1853f5 Release 0.22.0 2021-02-22 11:46:09 +01:00
Juraj Kirchheim
f3ddaa6496 Merge pull request #33 from haxetink/deduce_common_type
Deduce common base type from a list of types
2020-12-23 09:32:31 +01:00
Kevin Leung
f46e49ce66 Fix haxe3 2020-12-22 17:15:28 +08:00
Kevin Leung
007c73d58e Deduce common base type from a list of types 2020-12-22 16:56:45 +08:00
Juraj Kirchheim
8c5903833c Merge pull request #32 from haxetink/isAbstract
haxe 4.2+ fix
2020-12-20 09:21:31 +01:00
Dan Korostelev
59d7407d1b haxe 4.2+ fix 2020-12-19 22:58:31 +01:00
Juraj Kirchheim
0680220a77 Release 0.21.1 2020-08-20 13:09:07 +02:00
Juraj Kirchheim
acaedc170a Fix issue with build field retrieval. 2020-08-20 13:08:28 +02:00
Juraj Kirchheim
8b9dbba624 Release 0.21.0 2020-08-20 12:47:53 +02:00
Juraj Kirchheim
f9348d4a46 Release 0.20.2 2020-08-20 12:46:40 +02:00
Juraj Kirchheim
6f4e6b9227 Workaround https://github.com/HaxeFoundation/haxe/issues/9853 2020-08-20 12:45:33 +02:00
Juraj Kirchheim
59135d5cea Release 0.20.1 2020-08-12 23:14:38 +02:00
Juraj Kirchheim
4accf55b41 Avoid messing with typing order. 2020-08-12 23:10:43 +02:00
Juraj Kirchheim
fd3b01ef0f Release 0.20.0 2020-08-12 14:25:07 +02:00
Juraj Kirchheim
5825c2b617 Add TypedExpr helpers. 2020-08-12 14:22:00 +02:00
Juraj Kirchheim
328a8476a5 Release 0.19.3 2020-06-26 15:26:51 +02:00
Juraj Kirchheim
1589665652 Fix haxe 3. 2020-06-26 15:24:00 +02:00
Juraj Kirchheim
08f15a6739 Release 0.19.2 2020-06-26 15:09:56 +02:00
Juraj Kirchheim
b5e992b820 Fix issues in ComplexType -> Type conversion for functions. 2020-06-26 15:09:21 +02:00
13 changed files with 369 additions and 171 deletions

View File

@@ -9,8 +9,8 @@
"contributors": [
"back2dos"
],
"version": "0.19.1",
"releasenote": "More fine grained control over type normalization in BuildCache.",
"version": "0.22.0",
"releasenote": "Support Haxe 4.2",
"tags": [
"tink",
"macro",

View File

@@ -8,6 +8,8 @@ using StringTools;
typedef Positions = tink.macro.Positions;
typedef ExprTools = haxe.macro.ExprTools;
typedef TypedExprTools = haxe.macro.TypedExprTools;
typedef TypedExprs = tink.macro.TypedExprs;
typedef Exprs = tink.macro.Exprs;
typedef Functions = tink.macro.Functions;
typedef Metadatas = tink.macro.Metadatas;
@@ -28,11 +30,11 @@ typedef ClassBuilder = tink.macro.ClassBuilder;
typedef TypeResolution = Ref<Either<String, TypeDefinition>>;
class MacroApi {
static var MAIN_CANDIDATES = ['-main', '-x', '--run'];
static public function getMainClass():Option<String> {
var args = Sys.args();
for (c in MAIN_CANDIDATES)
switch args.indexOf(c) {
case -1:
@@ -42,19 +44,26 @@ class MacroApi {
return None;
}
@:persistent static var idCounter = 0;
@:persistent static var idCounter = 0;
@:noUsing static public inline function tempName(?prefix:String = 'tmp'):String
return '__tink_' + prefix + Std.string(idCounter++);
static public function pos()
static public function pos()
return haxe.macro.Context.currentPos();
static public var completionPoint(default, null):Option<{
var file(default, never):String;
var content(default, never):Null<String>;
var pos(default, never):Int;
}>;
static public function getBuildFields():Option<Array<haxe.macro.Expr.Field>>
return switch completionPoint {
case Some(v) if (v.content != null && (v.content.charAt(v.pos - 1) == '@' || (v.content.charAt(v.pos - 1) == ':' && v.content.charAt(v.pos - 2) == '@'))): None;
default: Some(haxe.macro.Context.getBuildFields());
}
static public var args(default, null):Iterable<String>;
static var initialized = initArgs();
@@ -63,7 +72,7 @@ class MacroApi {
args = sysArgs;
completionPoint = switch sysArgs.indexOf('--display') {
case -1: None;
case sysArgs[_ + 1] => arg:
case sysArgs[_ + 1] => arg:
if (arg.startsWith('{"jsonrpc":')) {
var payload:{
jsonrpc:String,
@@ -71,12 +80,15 @@ class MacroApi {
params:{
file:String,
offset:Int,
contents:String,
}
} = haxe.Json.parse(arg);
switch payload {
case { jsonrpc: '2.0', method: 'display/completion' }:
Some({
file: payload.params.file,
content: payload.params.contents,
pos: payload.params.offset,
});
default: None;
@@ -108,26 +120,26 @@ class MacroApi {
@:forward
abstract ObjectField(F) to F {
static var QUOTED = "@$__hx__";
static var QUOTED = "@$__hx__";
inline function new(o) this = o;
public var field(get, never):String;
function get_field()
return
if (quotes == Quoted)
return
if (quotes == Quoted)
this.field.substr(QUOTED.length);
else this.field;
public var quotes(get, never):QuoteStatus;
function get_quotes()
return if (StringTools.startsWith(this.field, QUOTED)) Quoted else Unquoted;
@:from static function ofFull(o:{>F, quotes:QuoteStatus }):ObjectField
return switch o.quotes {
case null | Unquoted:
case null | Unquoted:
new ObjectField({ field: o.field, expr: o.expr });
default:
new ObjectField({ field: QUOTED + o.field, expr: o.expr });

View File

@@ -17,18 +17,18 @@ typedef BuildContextN = {
typedef BuildContext = {
pos:Position,
type:Type,
usings:Array<TypePath>,
name:String,
var pos(default, never):Position;
var type(default, never):Type;
var usings(default, never):Array<TypePath>;
var name(default, never):String;
}
typedef BuildContext2 = {>BuildContext,
type2:Type,
var type2(default, never):Type;
}
typedef BuildContext3 = {>BuildContext2,
type3:Type,
var type3(default, never):Type;
}
class BuildCache {
@@ -36,29 +36,20 @@ class BuildCache {
@:persistent static var cache = new Map();
static public function getType3(name, ?types, ?pos:Position, build:BuildContext3->TypeDefinition, ?normalizer:Type->Type) {
if (types == null)
switch Context.getLocalType() {
case TInst(_.toString() == name => true, [t1, t2, t3]):
types = { t1: t1, t2: t2, t3: t3 };
default:
throw 'assert';
}
var t1 = types.t1.toComplexType(),
t2 = types.t2.toComplexType(),
t3 = types.t2.toComplexType();
return getType(name, (macro : { t1: $t1, t2: $t2, t3: $t3 } ).toType(), pos, function (ctx) return build({
type: types.t1,
type2: types.t2,
type3: types.t3,
return _getTypeN(name, 3, switch types {
case null: null;
case v: [types.t1, types.t2, types.t3];
}, pos, ctx -> build({
pos: ctx.pos,
name: ctx.name,
usings: ctx.usings
type: ctx.types[0],
type2: ctx.types[1],
type3: ctx.types[2],
usings: ctx.usings,
name: ctx.name
}), normalizer);
}
static public function getTypeN(name, ?types, ?pos:Position, build:BuildContextN->TypeDefinition, ?normalizer:Type->Type) {
static function _getTypeN(name, length, ?types, ?pos:Position, build:BuildContextN->TypeDefinition, ?normalizer:Type->Type) {
if (pos == null)
pos = Context.currentPos();
@@ -67,50 +58,37 @@ class BuildCache {
switch Context.getLocalType() {
case TInst(_.toString() == name => true, params):
types = params;
default:
throw 'assert';
case t:
pos.error('expected $name but found ${t.toString()}');
}
var compound = ComplexType.TAnonymous([for (i in 0...types.length) {
name: 't$i',
pos: pos,
kind: FVar(switch types[i] {
case TInst(_.get().kind => KExpr(e), _):
TPath('tink.macro.ConstParam'.asTypePath([TPExpr(e)]));
case t: t.toComplex();
}),
}]).toType();
if (length != -1 && types.length != length)
pos.error('expected $length parameter${if (length == 1) '' else 's'}');
return getType(name, compound, pos, function (ctx) return build({
types: types,
pos: ctx.pos,
name: ctx.name,
usings: ctx.usings
}), normalizer);
}
static public function getType2(name, ?types, ?pos:Position, build:BuildContext2->TypeDefinition, ?normalizer:Type->Type) {
if (types == null)
switch Context.getLocalType() {
case TInst(_.toString() == name => true, [t1, t2]):
types = { t1: t1, t2: t2 };
default:
throw 'assert';
var forName =
switch cache[name] {
case null: cache[name] = new Group(name);
case v: v;
}
var t1 = types.t1.toComplexType(),
t2 = types.t2.toComplexType();
return getType(name, (macro : { t1: $t1, t2: $t2 } ).toType(), pos, function (ctx) return build({
type: types.t1,
type2: types.t2,
pos: ctx.pos,
name: ctx.name,
usings: ctx.usings
}), normalizer);
var ret = forName.get(types, pos.sanitize(), build, normalizer);
ret.getFields();// workaround for https://github.com/HaxeFoundation/haxe/issues/7905
return ret;
}
static public function getParams(name:String, ?pos:Position)
static public function getType2(name, ?types, ?pos:Position, build:BuildContext2->TypeDefinition, ?normalizer:Type->Type)
return _getTypeN(name, 2, switch types {
case null: null;
case v: [types.t1, types.t2];
}, pos, ctx -> build({
pos: ctx.pos,
type: ctx.types[0],
type2: ctx.types[1],
usings: ctx.usings,
name: ctx.name
}), normalizer);
static public function getParams(name:String, ?pos:Position, ?count:Int)
return
switch Context.getLocalType() {
case TInst(_.toString() == name => true, v):
@@ -131,70 +109,50 @@ class BuildCache {
});
static public function getType(name, ?type, ?pos:Position, build:BuildContext->TypeDefinition, ?normalizer:Type->Type) {
if (type == null)
type = getParam(name, pos).sure();
var forName =
switch cache[name] {
case null: cache[name] = new Group(name, normalizer);
case v: v;
}
var ret = forName.get(type, pos.sanitize(), build);
ret.getFields();// workaround for https://github.com/HaxeFoundation/haxe/issues/7905
return ret;
return _getTypeN(name, 1, switch type {
case null: null;
case v: [v];
}, pos, ctx -> build({
pos: ctx.pos,
type: ctx.types[0],
usings: ctx.usings,
name: ctx.name
}), normalizer);
}
}
private typedef Entry = {
name:String,
}
private class Group {
private class Group {//TODO: this is somewhat obsolete
var name:String;
var counter = 0;
var entries:TypeMap<Entry>;
public function new(name, ?normalizer) {
public function new(name) {
this.name = name;
this.entries = new TypeMap(normalizer);
}
public function get(type:Type, pos:Position, build:BuildContext->TypeDefinition):Type {
public function get(types:Array<Type>, pos:Position, build:BuildContextN->TypeDefinition, ?normalizer):Type {
function make(path:String) {
var usings = [];
var def = build({
pos: pos,
type: type,
usings: usings,
name: path.split('.').pop()
});
types = types.map(switch normalizer {
case null: function (t) return Context.follow(t);
case f: f;
});
entries.set(type, { name: path } );
Context.defineModule(path, [def], usings);
return Context.getType(path);
var retName = name + '_' + Context.signature(Sisyphus.exactParams(types));
return switch retName.definedType() {
case Some(v): v;
case None:
var usings = [];
var path = name.split('.');
var ret = build({
pos: pos,
types: types,
usings: usings,
name: retName.split('.').pop(),
});
Context.defineModule(retName, [ret], usings);
Context.getType(retName);
}
function doMake()
while (true)
switch '$name${counter++}' {
case _.definedType() => Some(_):
case v:
return make(v);
}
return
switch entries.get(type) {
case null:
doMake();
case v:
switch v.name.definedType() {
case Some(v): v;
default: doMake();
}
}
}
}

View File

@@ -23,7 +23,10 @@ class ClassBuilder {
target = Context.getLocalClass().get();
if (fields == null)
fields = Context.getBuildFields();
fields = switch MacroApi.getBuildFields() {
case None: target.pos.error('Impossible to get builds fields now. Possible cause: https://github.com/HaxeFoundation/haxe/issues/9853');
case Some(v): v;
}
this.initializeFrom = fields;
this.target = target;
@@ -192,10 +195,14 @@ class ClassBuilder {
return m;
}
static public function run(plugins:Array<ClassBuilder->Void>, ?verbose) {
var builder = new ClassBuilder();
for (p in plugins)
p(builder);
return builder.export(verbose);
}
static public function run(plugins:Array<ClassBuilder->Void>, ?verbose)
return switch MacroApi.getBuildFields() {
case None: null;
case Some(fields):
var builder = new ClassBuilder(fields);
for (p in plugins)
p(builder);
return builder.export(verbose);
}
}

View File

@@ -2,6 +2,8 @@ package tink.macro;
import haxe.macro.Expr;
import haxe.macro.Type;
using haxe.macro.Tools;
using tink.MacroApi;
class Sisyphus {
@@ -23,8 +25,8 @@ class Sisyphus {
if (cf.params.length == 0) {
name: cf.name,
doc: cf.doc,
access:
(cf.isPublic ? [ APublic ] : [ APrivate ])
access:
(cf.isPublic ? [ APublic ] : [ APrivate ])
#if haxe4 .concat(if (cf.isFinal) [AFinal] else []) #end
,
kind: switch([ cf.kind, cf.type ]) {
@@ -61,8 +63,7 @@ class Sisyphus {
}
}
public static function toComplexType(type : Null<Type>) : Null<ComplexType> return {
static public function toComplexType(type : Null<Type>) : Null<ComplexType> return {
inline function direct()
return Types.toComplex(type, { direct: true });
switch (type) {
@@ -84,7 +85,7 @@ class Sisyphus {
cl.kind.match(KTypeParameter(_))
&& cl.module == classType.module
&& cl.pack.join('.') == classType.pack.join('.')
): ct;
): ct;
default:
direct();
}
@@ -95,8 +96,12 @@ class Sisyphus {
TPath(toTypePath(baseType, params));
case TFun(args, ret):
TFunction(
[ for (a in args) a.opt ? nullable(toComplexType(a.t)) : toComplexType(a.t) ],
toComplexType(ret));
[for (a in args) {
var t = #if haxe4 TNamed(a.name, #else ( #end toComplexType(a.t));
if (a.opt) TOptional(t) else t;
}],
toComplexType(ret)
);
case TAnonymous(_.get() => { fields: fields }):
TAnonymous([ for (cf in fields) toField(cf) ]);
case TDynamic(t):
@@ -121,7 +126,7 @@ class Sisyphus {
case _ == name => true: null;
case v: v;
}
{
pack: baseType.pack,
name: name,
@@ -132,4 +137,91 @@ class Sisyphus {
}],
}
}
static function exactBase<T:BaseType>(r:Ref<T>, params:Array<Type>) {
var t = r.get();
var isMain = !t.isPrivate && switch t.pack {
case []: t.module == t.name || t.module == 'StdTypes';
default: StringTools.endsWith(t.module, '.${t.name}');
}
return (
if (isMain) t.pack.concat([t.name]).join('.')
else t.module + '.' + t.name
) + exactParams(params);
}
static inline function isFinal(c:ClassField)
return #if haxe4 c.isFinal #else false #end;
static function exactAnonField(c:ClassField) {
var kw =
switch c.kind {
case FMethod(_): 'function';
case FVar(_):
if (isFinal(c)) 'final' else 'var';
}
return [for (m in c.meta.get()) m.toString() + ' '].join('') + '$kw ${c.name}' + (switch c.kind {
case FVar(read, write):
(
if (isFinal(c) || (read == AccNormal && write == AccNormal)) ''
else '(${read.accessToName()}, ${read.accessToName(false)})'
) + ':' + c.type.toExactString();
case FMethod(_):
switch haxe.macro.Context.follow(c.type) {
case TFun(arg, ret): exactSig(arg, ret, ':');
default: throw 'assert';
}
}) + ';';
}
static function exactSig(args:Array<{name:String, opt:Bool, t:Type}>, ret:Type, sep:String)
return '(${[for (a in args) (if (a.opt) '?' else '') + a.name + ':' + toExactString(a.t)].join(', ')})$sep${toExactString(ret)}';
static public function exactParams(params:Array<Type>)
return switch params {
case []: '';
case params:
'<${params.map(toExactString).join(', ')}>';
}
static public function toExactString(t:Type)
return switch t {
case TMono(t): t.toString();
case TEnum(r, params): exactBase(r, params);
case TInst(r, params): exactBase(r, params);
case TType(r, params): exactBase(r, params);
case TAbstract(r, params): exactBase(r, params);
case TFun(args, ret): exactSig(args, ret, '->');
case TAnonymous(a): '{ ${[for (f in a.get().fields) exactAnonField(f)].join(' ')} }';
case TDynamic(null): 'Dynamic';
case TDynamic(t): 'Dynamic<${toExactString(t)}>';
case TLazy(f): toExactString(f());
}
static function eager(t:Type)
return switch t {
case TLazy(f): eager(f());
default: t;
}
static public function compare(t1:Type, t2:Type, ?follow:Bool = true) {
if (follow) {
t1 = t1.reduce();
t2 = t2.reduce();
}
else {
t1 = eager(t1);
t2 = eager(t2);
}
return switch t1.getIndex() - t2.getIndex() {
case 0:
switch Reflect.compare(t1.toString(), t2.toString()) {
case 0: Reflect.compare(t1.toExactString(), t2.toExactString());
case v: v;
}
case v: v;
}
}
}

View File

@@ -0,0 +1,55 @@
package tink.macro;
import haxe.macro.Type;
import haxe.ds.Option;
using haxe.macro.Tools;
class TypedExprs {
static public function extractAll<T>(t:TypedExpr, f:TypedExpr->Option<T>):Array<T> {
var out = [];
function rec(t:TypedExpr)
if (t != null) {
switch f(t) {
case Some(v): out.push(v);
default:
}
t.iter(rec);
}
rec(t);
return out;
}
static public function extract<T>(t:TypedExpr, f:TypedExpr->Option<T>):Option<T> {
try extractAll(t, function (t) {
var ret = f(t);
if (ret != None)
throw ret;
return ret;
})
catch (e:Option<Dynamic>) return cast e;
return None;
}
static public function isThis(t:TypedExpr):Bool
return switch t {
case null: false;
case { expr: TConst(TThis) | TLocal({ name: '`this' })}: true;
default: false;
}
static public inline function hasThis(t)
return contains(t, isThis);
static public function findAll(t:TypedExpr, f:TypedExpr->Bool):Array<TypedExpr>
return extractAll(t, collect(f));
static public function find(t:TypedExpr, f:TypedExpr->Bool):Option<TypedExpr>
return extract(t, collect(f));
static public function contains(t:TypedExpr, f:TypedExpr->Bool):Bool
return find(t, f) != None;
static inline function collect(f)
return function (t) return if (f(t)) Some(t) else None;
}

View File

@@ -10,9 +10,11 @@ using haxe.macro.Tools;
using tink.MacroApi;
using tink.CoreApi;
import haxe.macro.Type.Ref;
class Types {
static public function definedType(typeName:String)
static public function definedType(typeName:String):Option<Type>
return
try {
Some(Context.getType(typeName));
@@ -172,6 +174,9 @@ class Types {
#if haxe4
isExtern: field.isExtern,
isFinal: field.isFinal,
#if (haxe >= version("4.2.0-rc.1"))
isAbstract: field.isAbstract,
#end
#end
}:ClassField)
]);
@@ -205,7 +210,22 @@ class Types {
default: Failure('type "$t" has no position');
}
static public function deduceCommonType(types:Array<Type>):Outcome<Type, Error> {
var exprs = types.map(function(t) {
var ct = t.toComplex();
return macro (null:$ct);
});
return switch (macro $a{exprs}).typeof() {
case Success(TInst(_, [v])): Success(v);
case Success(_): throw 'unreachable';
case Failure(e): Failure(new Error('Unable to deduce common type among $types'));
}
}
/// like haxe.macro.TypeTools.toString, but not lossy
static public function toExactString(t:Type)
return Sisyphus.toExactString(t);
static public function toString(t:ComplexType)
return new Printer().printComplexType(t);
@@ -258,8 +278,7 @@ class Types {
return if (once) t else reduce(t, false);
return switch type {
case TAbstract(_.get() => { name: 'Null', pack: [] }, [t]): rec(t);
case TLazy(f): rec(f());
case TType(_, _): rec(Context.follow(type, once));
case TLazy(_) | TType(_): rec(Context.follow(type, once));
default: type;
}
}
@@ -294,6 +313,9 @@ class Types {
if (types.length == 1) return Success(types[1]);
#if haxe4
return Success(TIntersection(types));
#end
var paths = [],
fields = [];
@@ -335,18 +357,8 @@ class Types {
throw 'assert';
}
static public function compare(t1:Type, t2:Type, ?follow:Bool = true) {
if (follow) {
t1 = t1.reduce();
t2 = t2.reduce();
}
return switch t1.getIndex() - t2.getIndex() {
case 0:
Reflect.compare(t1.toString(), t2.toString());//much to my surprise, this actually seems to work (at least with 3.4)
case v: v;
}
}
static public function compare(t1:Type, t2:Type, ?follow:Bool = true)
return Sisyphus.compare(t1, t2, follow);
static var SUGGESTIONS = ~/ \(Suggestions?: .*\)$/;

8
tests/Dummy.hx Normal file
View File

@@ -0,0 +1,8 @@
class Dummy {
public function new() {}
static public var p(default, never) = new Private();
}
private class Private {
public function new() {}
}

21
tests/ExactStrings.hx Normal file
View File

@@ -0,0 +1,21 @@
import haxe.macro.Context.typeof;
using tink.MacroApi;
class ExactStrings extends Base {
function test() {
function expect(s:String, e, ?pos)
assertEquals(s, typeof(e).toExactString(), pos);
expect('Dummy', macro new Dummy());
expect('nested.Dummy', macro new nested.Dummy());
expect('Dummy.Private', macro Dummy.p);
expect('nested.Dummy.Private', macro nested.Dummy.p);
expect('{ @foo var x:Int; }', macro (null:{@foo var x:Int;}));
expect('{ @foo @bar var x:Int; }', macro (null:{@foo @bar var x:Int;}));
expect('{ @bar @foo var x:Int; }', macro (null:{@bar @foo var x:Int;}));// not 100% sure this is always the best choice, but let's roll with it
expect('{ @bar var x:Int; }', macro (null:{@bar var x:Int;}));
expect('{ var x:Int; var y:Int; }', macro (null:{x:Int,y:Int}));
expect('{ var x:Int; var y:Int; }', macro (null:{y:Int,x:Int}));
expect('{ function foo(x:Int, ?y:Int):Void; }', macro (null:{ function foo(x:Int, ?y:Int):Void; }));
}
}

View File

@@ -1,6 +1,9 @@
package ;
import haxe.unit.*;
#if macro
using haxe.macro.Tools;
#end
class Run {
#if !macro
@@ -14,6 +17,7 @@ class Run {
new TypeMapTest(),
new Functions(),
new Misc(),
new ExactStrings(),
];
#end
macro static function test() {

View File

@@ -11,6 +11,8 @@ class TypeMapTest extends TestCase {
var t = new TypeMap();
var t1 = (macro [{ foo: [{ bar: '5' }]}]).typeof().sure();
var t2 = (macro [{ foo: [{ bar: 5 }]}]).typeof().sure();
var t3 = (macro [{ foo: [{ bar: 5 }]}]).typeof().sure();
var t4 = (macro [{ foo: [({ bar: 5 }:{ @foo var bar:Int; })]}]).typeof().sure();
t.set(t1, 0);
assertEquals(Lambda.count(t), 1);
@@ -19,7 +21,10 @@ class TypeMapTest extends TestCase {
t.set(t1, 2);
assertEquals(Lambda.count(t), 2);
t.set(t2, 3);
t.set(t3, 3);
assertEquals(Lambda.count(t), 2);
t.set(t4, 4);
assertEquals(Lambda.count(t), 3);
assertEquals(t.get(t1), 2);
assertEquals(t.get(t2), 3);

View File

@@ -10,36 +10,35 @@ using tink.MacroApi;
class Types extends Base {
function type(c:ComplexType)
return c.toType().sure();
function resolve(type:String)
return Context.getType(type);
inline function assertSuccess<S, F>(o:Outcome<S, F>)
assertTrue(o.isSuccess());
inline function assertFailure<S, F>(o:Outcome<S, F>)
assertFalse(o.isSuccess());
function testIs() {
assertSuccess(resolve('Int').isSubTypeOf(resolve('Float')));
assertFailure(resolve('Float').isSubTypeOf(resolve('Int')));
}
}
function testFields() {
var expected = type(macro : Void -> Iterator<Arrayish>),
iterator = type(macro : haxe.ds.StringMap<Arrayish>).getFields(true).sure().filter(function (c) return c.name == 'iterator')[0];
assertSuccess(iterator.type.isSubTypeOf(expected));
assertSuccess(expected.isSubTypeOf(iterator.type));
}
function testConvert() {
assertSuccess((macro : Int).toType());
assertFailure((macro : Tni).toType());
function blank()
return type(MacroApi.pos().makeBlankType());
var bool = type(macro : Bool);
assertTrue(blank().isSubTypeOf(bool).isSuccess());
assertTrue(bool.isSubTypeOf(blank()).isSuccess());
@@ -65,5 +64,20 @@ class Types extends Base {
assertEquals('String', Context.getType('String').toComplex().toString());
assertEquals('tink.CoreApi.Noise', Context.getType('tink.CoreApi.Noise').toComplex().toString());
}
function testDeduceCommonType() {
function ct2t(ct:ComplexType) return ct.toType().sure();
assertEquals('StdTypes.Float', tink.macro.Types.deduceCommonType([(macro:Float), (macro:Int)].map(ct2t)).sure().toComplex().toString());
assertEquals('Types.CommonI1', tink.macro.Types.deduceCommonType([(macro:Types.CommonA), (macro:Types.CommonB), (macro:Types.CommonC)].map(ct2t)).sure().toComplex().toString());
assertEquals('Types.CommonI2', tink.macro.Types.deduceCommonType([(macro:Types.CommonB), (macro:Types.CommonC)].map(ct2t)).sure().toComplex().toString());
// assertEquals('Types.CommonI3', tink.macro.Types.deduceCommonType([(macro:Types.CommonC)].map(ct2t)).sure().toComplex().toString());
}
}
#end
#end
interface CommonI1 {}
interface CommonI2 {}
interface CommonI3 {}
class CommonA implements CommonI1 {}
class CommonB implements CommonI2 implements CommonI1 {}
class CommonC implements CommonI3 implements CommonI2 implements CommonI1 {}

10
tests/nested/Dummy.hx Normal file
View File

@@ -0,0 +1,10 @@
package nested;
class Dummy {
public function new() {}
static public var p(default, never) = new Private();
}
private class Private {
public function new() {}
}