Search code examples
javagroovytype-systemsstatic-compilation

Groovy: How to get statically inferred return type from AST


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.


Solution

  • 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:

    1. ASTTransformation to set the return type to boolean (not required, was an artifact of a half-working earlier iteration)
    2. ASTTransformation to meterialize all return statements
    3. TypeCheckingExtension to visit the ReturnStatements and verify they are of type boolean
    4. ASTTransformation to perform static type transformation with the TypeCheckingExtension

    For 1:

    Extend ClassCodeVisitorSupport, in visitMethod identify all methods you want to return boolean (e.g. check for matching naming conventions)

    Set the returnType of the MethodNode to ClassHelper.boolean_TYPE

    For 2:

    For the same methods as above call org.codehaus.groovy.classgen.ReturnAdder.visitMethod

    For 3:

    Extend AbstractTypeCheckingExtension, override afterVisitMethod. At this point the AbstractTypeCheckingExtension will have inferred the lower bound of all expressions inside the method. Use a ClassCodeVisitorSupport subclass and override visitReturnStatement. Use getType(statement.expression) to get the inferred type. Note that this can be different from statement.expression.type (the type according to the AST). Call addStaticTypeError on non boolean type.

    For 4:

    Extend StaticTypesTransformation Override newVisitor and create a new StaticTypeCheckingVisitor and call addTypeCheckingExtension on it to add your TypeCheckingExtension

    GitHub Project

    https://github.com/MeneDev/groovy-dsl-building-blocks-enforce-return-type

    It may even be reusable as a dependency ;)