I'm using Groovy to allow users to script and customize certain parts of my Java application. I am using static compilation of all user-provided scripts.
My question is: if my code expects to receive e.g. a value of type boolean as the result of the user script, is there any way for me to check whether or not the provided user script will always return a boolean without actually executing the script? In other words, how can I access the results of the type inference performed by the static groovy compiler? I would like to be able to tell the user "hey, your script does not always return a boolean" while they are editing the script content.
There is no straight-forward way, but it's possible. Boolean is especially hard as the compiler will happily apply all sorts of type-coersions (e.g. transform an int to boolean without complaining). I've had the exact same problem and did the following steps:
ASTTransformation to set the return type to boolean(not required, was an artifact of a half-working earlier iteration)For 1:
Extend
ClassCodeVisitorSupport
, invisitMethod
identify all methods you want to return boolean (e.g. check for matching naming conventions)Set the
returnType
of theMethodNode
toClassHelper.boolean_TYPE
For 2:
For the same methods as above call
org.codehaus.groovy.classgen.ReturnAdder.visitMethod
For 3:
Extend
AbstractTypeCheckingExtension
, overrideafterVisitMethod
. At this point theAbstractTypeCheckingExtension
will have inferred the lower bound of all expressions inside the method. Use aClassCodeVisitorSupport
subclass and overridevisitReturnStatement
. UsegetType(statement.expression)
to get the inferred type. Note that this can be different fromstatement.expression.type
(the type according to the AST). CalladdStaticTypeError
on non boolean type.For 4:
Extend
StaticTypesTransformation
OverridenewVisitor
and create a newStaticTypeCheckingVisitor
and calladdTypeCheckingExtension
on it to add yourTypeCheckingExtension
GitHub Project
https://github.com/MeneDev/groovy-dsl-building-blocks-enforce-return-type
It may even be reusable as a dependency ;)