Closed ciscoheat closed 8 years ago
Well, for one I'm not so sure the approach is really the best. If you have some 100 //TEST:
lines you'll be doing a lot of compiling.
You could be doing something like this:
class Assert {
macro public static function compileError(expr:Expr)
return
try {
Context.typeof(expr);
macro @:pos(expr.pos) assertTrue(false);
}
catch (e:Dynamic)
macro @:pos(expr.pos) assertTrue(true);
}
I'll leave it to you to adapt it to buddy and produce more helpful error messages.
But in general you could just write stuff like:
Assert.compileError(path.to.ClassThatViolatesRules);
Assert.compilerError(immutableVar = newValue);
The second type of statement is where you need a little trick. Currently, on expression level you are using Context.error
to reject invalid subexpressions. Instead, you could return an expression that - once typed - error.
class CompilerError {
static public function errorExpr(original:Expr, message:String) {
return macro @:pos(original.pos) CompilerError.raise($v{message});
}
static public macro function raise(error:String) {
Context.error(error, Context.currentPos());
}
}
That's a technique I sometimes use in tink (any time I can muster the discipline), because it allows the whole macro to run, as opposed to Context.error
which will just abort a build-macro and thus skip all of its effects, often leading to a flood of errors in dependent classes.
So to answer your question: I think this would be better suited as an addition to buddy ;)
Great, thanks, I'll add this to buddy for sure. :) Enjoy wwx!
I've added this to buddy now, it works great for one part of the problem, but there are some tricky parts left. If you look at line 120 for example, that line itself will compile, but the actual failure will occur in a callback to Macro.onGenerate
. How to test that?
Another case is a property test. If a property setter or default exists, I also want to fail compilation, but I can't use Context.typeof
on that part of the code.
Any ideas how to solve this?
Ok, hmm. I guess you have to do some extra work there :)
First, move the validation code out somewhere, to something like:
static function checkType(type:haxe.macro.Type):Some<Error>;
//and now, something rather domain-specific like this:
class Assert {
macro static public function isInvalid(e:ExprOf<Class<Dynamic>>)
return switch checkType(Context.getType(e.toString()) {
case Some(_): macro @:pos(e.pos) assertTrue(true);
case None: macro @:pos(e.pos) assertTrue(false);
}
}
So that works should deal with that.
Also, I would move some of the work into the build macro, i.e. any immutable field should already be built with null
or never
write access. So from the outside, the compiler does the heavy lifting already. You only have to check the integrity of the class itself (and subclasses I guess).
Thanks again! Since this is isn't travix related anymore, closing this.
In the immutable-hx library, I want to test if the compilation fails. I've done that through a special prefix in a test file: https://github.com/ciscoheat/immutable-hx/blob/master/tests/RunTests.hx
When testing, a small script is run that uncomments each line containing
//TEST:
one by one, the project is built, and if compilation fails, it is considered a passing test.Would this be interesting to have in travix? If so, how to do it?