You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/10/11 18:53:10 UTC

[groovy] 01/01: GROOVY-10771: STC: extension documentation

This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit a77c1a43af14a7670ee813a85222a3b695fb56c3
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Oct 11 12:28:10 2022 -0500

    GROOVY-10771: STC: extension documentation
---
 .../stc/GroovyTypeCheckingExtensionSupport.java    |  80 ++++-
 src/spec/doc/type-checking-extensions.adoc         |  81 +++--
 ...riable.groovy => incompatiblereturntype.groovy} |   5 +-
 src/spec/test-resources/unresolvedattribute.groovy |   6 +-
 src/spec/test-resources/unresolvedproperty.groovy  |   6 +-
 src/spec/test-resources/unresolvedvariable.groovy  |   6 +-
 .../typing/TypeCheckingExtensionSpecTest.groovy    | 362 +++++++++++----------
 .../stc/TypeCheckingExtensionsTest.groovy          | 182 ++++++-----
 8 files changed, 421 insertions(+), 307 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/transform/stc/GroovyTypeCheckingExtensionSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/GroovyTypeCheckingExtensionSupport.java
index 0852cf33da..6a1a8cdaeb 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/GroovyTypeCheckingExtensionSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/GroovyTypeCheckingExtensionSupport.java
@@ -393,14 +393,78 @@ public class GroovyTypeCheckingExtensionSupport extends AbstractTypeCheckingExte
         return methodList;
     }
 
-    // -------------------------------------
-    // delegate to the type checking context
-    // -------------------------------------
-
-    // --------------------------------------------
-    // end of delegate to the type checking context
-    // --------------------------------------------
-
+    /**
+     * Event handler registration:
+     * <dl>
+     *   <dt>setup</dt>                          <dd>Registers closure that runs after the type checker finishes initialization</dd>
+     *   <dt>finish</dt>                         <dd>Registers closure that runs after the type checker completes type checking</dd>
+     *   <dt>beforeVisitClass</dt>               <dd>Registers closure that runs before type checking a class</dd>
+     *   <dt>afterVisitClass</dt>                <dd>Registers closure that runs after having finished the visit of a type checked class</dd>
+     *   <dt>beforeVisitMethod</dt>              <dd>Registers closure that runs before type checking a method body</dd>
+     *   <dt>afterVisitMethod</dt>               <dd>Registers closure that runs after type checking a method body</dd>
+     *   <dt>beforeMethodCall</dt>               <dd>Registers closure that runs before the type checker starts type checking a method call</dd>
+     *   <dt>afterMethodCall</dt>                <dd>Registers closure that runs once the type checker has finished type checking a method call</dd>
+     *   <dt>methodNotFound</dt>                 <dd>Registers closure that runs when it fails to find an appropriate method for a method call</dd>
+     *   <dt>ambiguousMethods</dt>               <dd>Registers closure that runs when the type checker cannot choose between several candidate methods</dd>
+     *   <dt>onMethodSelection</dt>              <dd>Registers closure that runs when it finds a method appropriate for a method call</dd>
+     *   <dt>unresolvedVariable</dt>             <dd>Registers closure that runs when the type checker finds an unresolved variable</dd>
+     *   <dt>unresolvedProperty</dt>             <dd>Registers closure that runs when the type checker cannot find a property on the receiver</dd>
+     *   <dt>unresolvedAttribute</dt>            <dd>Registers closure that runs when the type checker cannot find an attribute on the receiver</dd>
+     *   <dt>incompatibleAssignment</dt>         <dd>Registers closure that runs when the type checker thinks that the right-hand side of an assignment is incompatible with the left-hand side</dd>
+     *   <dt>incompatibleReturnType</dt>         <dd>Registers closure that runs when the type checker thinks that a return value is incompatibe with the return type</dd>
+     * </dl>
+     *
+     * Expression categorization:
+     * <dl>
+     *   <dt>isAnnotationConstantExpression</dt> <dd>Determines if argument is an {@link org.codehaus.groovy.ast.expr.AnnotationConstantExpression AnnotationConstantExpression}</dd>
+     *   <dt>isArgumentListExpression</dt>       <dd>Determines if argument is an {@link org.codehaus.groovy.ast.expr.ArgumentListExpression ArgumentListExpression}</dd>
+     *   <dt>isArrayExpression</dt>              <dd>Determines if argument is an {@link org.codehaus.groovy.ast.expr.ArrayExpression ArrayExpression}</dd>
+     *   <dt>isAttributeExpression</dt>          <dd>Determines if argument is an {@link org.codehaus.groovy.ast.expr.AttributeExpression AttributeExpression}</dd>
+     *   <dt>isBinaryExpression</dt>             <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.BinaryExpression BinaryExpression}</dd>
+     *   <dt>isBitwiseNegationExpression</dt>    <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.BitwiseNegationExpression BitwiseNegationExpression}</dd>
+     *   <dt>isBooleanExpression</dt>            <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.BooleanExpression BooleanExpression}</dd>
+     *   <dt>isCastExpression</dt>               <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.CastExpression CastExpression}</dd>
+     *   <dt>isClassExpression</dt>              <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.ClassExpression ClassExpression}</dd>
+     *   <dt>isClosureExpression</dt>            <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.ClosureExpression ClosureExpression}</dd>
+     *   <dt>isConstantExpression</dt>           <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.ConstantExpression ConstantExpression}</dd>
+     *   <dt>isConstructorCallExpression</dt>    <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.ConstructorCallExpression ConstructorCallExpression}</dd>
+     *   <dt>isDeclarationExpression</dt>        <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.DeclarationExpression DeclarationExpression}</dd>
+     *   <dt>isElvisOperatorExpression</dt>      <dd>Determines if argument is an {@link org.codehaus.groovy.ast.expr.ElvisOperatorExpression ElvisOperatorExpression}</dd>
+     *   <dt>isEmptyExpression</dt>              <dd>Determines if argument is an {@link org.codehaus.groovy.ast.expr.EmptyExpression EmptyExpression}</dd>
+     *   <dt>isFieldExpression</dt>              <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.FieldExpression FieldExpression}</dd>
+     *   <dt>isGStringExpression</dt>            <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.GStringExpression GStringExpression}</dd>
+     *   <dt>isLambdaExpression</dt>             <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.LambdaExpression LambdaExpression}</dd>
+     *   <dt>isListExpression</dt>               <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.ListExpression ListExpression}</dd>
+     *   <dt>isMapExpression</dt>                <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.MapExpression MapExpression}</dd>
+     *   <dt>isMapEntryExpression</dt>           <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.MapEntryExpression MapEntryExpression}</dd>
+     *   <dt>isMethodCallExpression</dt>         <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.MethodCallExpression MethodCallExpression}</dd>
+     *   <dt>isMethodPointerExpression</dt>      <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.MethodPointerExpression MethodPointerExpression}</dd>
+     *   <dt>isMethodReferenceExpression</dt>    <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.MethodReferenceExpression MethodReferenceExpression}</dd>
+     *   <dt>isNamedArgumentListExpression</dt>  <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.NamedArgumentListExpression NamedArgumentListExpression}</dd>
+     *   <dt>isNotExpression</dt>                <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.NotExpression NotExpression}</dd>
+     *   <dt>isPostfixExpression</dt>            <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.PostfixExpression PostfixExpression}</dd>
+     *   <dt>isPrefixExpression</dt>             <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.PrefixExpression PrefixExpression}</dd>
+     *   <dt>isPropertyExpression</dt>           <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.PropertyExpression PropertyExpression}</dd>
+     *   <dt>isRangeExpression</dt>              <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.RangeExpression RangeExpression}</dd>
+     *   <dt>isSpreadExpression</dt>             <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.SpreadExpression SpreadExpression}</dd>
+     *   <dt>isSpreadMapExpression</dt>          <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.SpreadMapExpression SpreadMapExpression}</dd>
+     *   <dt>isStaticMethodCallExpression</dt>   <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.StaticMethodCallExpression StaticMethodCallExpression}</dd>
+     *   <dt>isTernaryExpression</dt>            <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.TernaryExpression TernaryExpression}</dd>
+     *   <dt>isTupleExpression</dt>              <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.TupleExpression TupleExpression}</dd>
+     *   <dt>isUnaryMinusExpression</dt>         <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.UnaryMinusExpression UnaryMinusExpression}</dd>
+     *   <dt>isUnaryPlusExpression</dt>          <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.UnaryPlusExpression UnaryPlusExpression}</dd>
+     *   <dt>isVariableExpression</dt>           <dd>Determines if argument is a  {@link org.codehaus.groovy.ast.expr.VariableExpression VariableExpression}</dd>
+     * </dl>
+     *
+     * General utility:
+     * <ul>
+     *   <li>Delegates to {@link AbstractTypeCheckingExtension}</li>
+     *   <li>Imports static members of {@link org.codehaus.groovy.ast.ClassHelper ClassHelper}</li>
+     *   <li>Imports static members of {@link org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport StaticTypeCheckingSupport}</li>
+     * </ul>
+     *
+     * @see <a href="https://docs.groovy-lang.org/latest/html/documentation/#_a_dsl_for_type_checking">Groovy Language Documentation</a>
+     */
     public abstract static class TypeCheckingDSL extends Script {
         private GroovyTypeCheckingExtensionSupport extension;
 
diff --git a/src/spec/doc/type-checking-extensions.adoc b/src/spec/doc/type-checking-extensions.adoc
index 8c281f2cd7..1983854a85 100644
--- a/src/spec/doc/type-checking-extensions.adoc
+++ b/src/spec/doc/type-checking-extensions.adoc
@@ -25,13 +25,12 @@
 
 === Towards a smarter type checker
 
-Despite being a dynamic language, Groovy can be used with a static type
-checker at compile time, enabled using the <<s...@TypeChecked>>
-annotation. In this mode, the compiler becomes
-more verbose and throws errors for, example, typos, non-existent
-methods,… This comes with a few limitations though, most of them coming
-from the fact that Groovy remains inherently a dynamic language. For
-example, you wouldn’t be able to use type checking on code that uses the markup builder:
+Despite being a dynamic language, Groovy can be used with a <<static-type-checking,static type checker>>
+at compile time, enabled using the `@TypeChecked` annotation. In this mode, the
+compiler becomes more verbose and throws errors for, example, typos, non-existent
+methods, etc. This comes with a few limitations though, most of them coming from
+the fact that Groovy remains inherently a dynamic language. For example, you
+wouldn’t be able to use type checking on code that uses the markup builder:
 
 [source,groovy]
 ----
@@ -246,7 +245,7 @@ Can be used to perform additional checks after the type checker has finished its
 | Called when the type checker finds an
   unresolved variable
 | *Arguments*
-| VariableExpression var
+| VariableExpression vexp
 | *Usage*
 |
 [source,groovy]
@@ -286,7 +285,7 @@ Allows the developer to handle "dynamic" properties
 | Called when the type checker cannot
   find an attribute on the receiver
 | *Arguments*
-| AttributeExpression aex
+| AttributeExpression aexp
 | *Usage*
 |
 [source,groovy]
@@ -424,8 +423,8 @@ include::{projectdir}/src/spec/test-resources/beforevisitmethod.groovy[tags=even
 The type checker will call this method before
 starting to type check a method body. If you want, for example, to
 perform type checking by yourself instead of letting the type checker do
-it, you have to set the handled flag to true.This event can also be used
-to help defining the scope of your extension (for example, applying it
+it, you have to set the handled flag to true. This event can also be used
+to help define the scope of your extension (for example, applying it
 only if you are inside method foo).
 |===
 
@@ -506,10 +505,10 @@ inner/anonymous class defined in the same class with is not skipped.
 | incompatibleAssignment
 | *Called When*
 | Called when the type checker thinks
-    that an assignment is incorrect, meaning that the right hand side of an
-    assignment is incompatible with the left hand side
+    that an assignment is incorrect, meaning that the right-hand side of an
+    assignment is incompatible with the left-hand side
 | *Arguments*
-| ClassNode lhsType, ClassNode rhsType,  Expression assignment
+| ClassNode lhsType, ClassNode rhsType, Expression assignment
 | *Usage*
 |
 [source,groovy]
@@ -526,6 +525,30 @@ can help the type checker just by telling it that the assignment is
 valid (using `handled` set to `true`).
 |===
 
+[[event-incompatibleReturnType]]
+[cols="1,3a",width="100%"]
+|===
+| *Event name*
+| incompatibleReturnType
+| *Called When*
+| Called when the type checker thinks that a return value is incompatibe with
+    the return type of the enclosing closure or method
+| *Arguments*
+| ReturnStatement statement, ClassNode valueType
+| *Usage*
+|
+[source,groovy]
+----
+include::{projectdir}/src/spec/test-resources/incompatiblereturntype.groovy[tags=event,indent=0]
+----
+
+Gives the developer the ability to handle incorrect return values. This is for
+example useful when the return value will undergo implicit conversion or the
+enclosing closure's target type is difficult to infer properly. In that case,
+you can help the type checker just by telling it that the assignment is valid
+(by setting the `handled` property).
+|===
+
 [[event-ambiguousMethods]]
 [cols="1,3a",width="100%"]
 |===
@@ -564,19 +587,16 @@ make things easier.
 [[Typecheckingextensions-Supportclasses]]
 ==== Support classes
 
-The DSL relies on a support class
-called gapi:org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport[] .
-This class itself
-extends gapi:org.codehaus.groovy.transform.stc.TypeCheckingExtension[] . Those
-two classes define a number of _helper_ methods that will make working
+The DSL relies on a support class called gapi:org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport[] .
+This class itself extends gapi:org.codehaus.groovy.transform.stc.TypeCheckingExtension[] .
+Those two classes define a number of _helper_ methods that will make working
 with the AST easier, especially regarding type checking. One interesting
 thing to know is that you *have access to the type checker*. This means
 that you can programmatically call methods of the type checker,
 including those that allow you to *throw compilation errors*.
 
-The extension script delegates to
-the gapi:org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport[] class, meaning that you have
-direct access to the following variables:
+The extension script delegates to the gapi:org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport[] class,
+meaning that you have direct access to the following variables:
 
 * _context_: the type checker context, of type gapi:org.codehaus.groovy.transform.stc.TypeCheckingContext[]
 * _typeCheckingVisitor_: the type checker itself, a gapi:org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor[] instance
@@ -589,6 +609,12 @@ enclosing method calls, binary expressions, closures, … This information
 is in particular important if you have to know _where_ you are when an
 error occurs and that you want to handle it.
 
+In addition to facilities provided by `GroovyTypeCheckingExtensionSupport` and `StaticTypeCheckingVisitor`,
+a type-checking DSL script imports static members from gapi:org.codehaus.groovy.ast.ClassHelper[] and
+gapi:org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport[] granting access to common types via
+`OBJECT_TYPE`, `STRING_TYPE`, `THROWABLE_TYPE`, etc. and checks like `missesGenericsTypes(ClassNode)`,
+`isClassClassNodeWrappingConcreteType(ClassNode)` and so on.
+
 [[Typecheckingextensions-Classnodes]]
 ==== Class nodes
 
@@ -677,8 +703,7 @@ if (node instanceof BinaryExpression) {
 }
 ---------------------------------------
 
-which requires you to import the `BinaryExpression` class, you can just
-write:
+you can just write:
 
 [source,groovy]
 -------------------------------
@@ -758,7 +783,7 @@ builder.foo {
 -------------
 
 Your extension, then, should only be active once you’ve entered
-the `foo` method, and inactive outside of this scope. But you could have
+the `foo` method, and inactive outside this scope. But you could have
 complex situations like multiple builders in the same file or embedded
 builders (builders in builders). While you should not try to fix all
 this from start (you must accept limitations to type checking), the type
@@ -923,7 +948,7 @@ The more advanced one is to use <<ast-xform-as-extension,AST transformations dur
 complex.
 
 Type checking extensions allow you to help the type checker where it
-fails, but it also allow you to fail where it doesn’t. In that context,
+fails, but it also allows you to fail where it doesn’t. In that context,
 it makes sense to support extensions for `@CompileStatic` too. Imagine
 an extension that is capable of type checking SQL queries. In that case,
 the extension would be valid in both dynamic and static context, because
@@ -933,7 +958,7 @@ without the extension, the code would still pass.
 === Mixed mode compilation
 
 In the previous section, we highlighted the fact that you can activate type checking extensions with
-`@CompileStatic`. In that context, the type checker would not complain anymore about some unresolved variables or
+`@CompileStatic`. In that context, the type checker would not complain any more about some unresolved variables or
 unknown method calls, but it would still wouldn't know how to compile them statically.
 
 Mixed mode compilation offers a third way, which is to instruct the compiler that whenever an unresolved variable
@@ -1032,7 +1057,7 @@ The `makeDynamic` call does 3 things:
 * but also marks the `call` to be done dynamically
 
 So when the compiler will have to generate bytecode for the call to `move`, since it is now marked as a dynamic call,
-it will fallback to the dynamic compiler and let it handle the call. And since the extension tells us that the return
+it will fall back to the dynamic compiler and let it handle the call. And since the extension tells us that the return
 type of the dynamic call is a `Robot`, subsequent calls will be done statically!
 
 Some would wonder why the static compiler doesn't do this by default without an extension. It is a design decision:
diff --git a/src/spec/test-resources/unresolvedvariable.groovy b/src/spec/test-resources/incompatiblereturntype.groovy
similarity index 88%
copy from src/spec/test-resources/unresolvedvariable.groovy
copy to src/spec/test-resources/incompatiblereturntype.groovy
index b8e112bb5b..5872bf07df 100644
--- a/src/spec/test-resources/unresolvedvariable.groovy
+++ b/src/spec/test-resources/incompatiblereturntype.groovy
@@ -17,9 +17,8 @@
  *  under the License.
  */
 // tag::event[]
-unresolvedVariable { var ->
-    if ('people' == var.name) {
-        storeType(var, classNodeFor(List))
+incompatibleReturnType { stmt, type ->
+    if (type == STRING_TYPE) {
         handled = true
     }
 }
diff --git a/src/spec/test-resources/unresolvedattribute.groovy b/src/spec/test-resources/unresolvedattribute.groovy
index 1b361fbbd1..21fee60cf3 100644
--- a/src/spec/test-resources/unresolvedattribute.groovy
+++ b/src/spec/test-resources/unresolvedattribute.groovy
@@ -17,9 +17,9 @@
  *  under the License.
  */
 // tag::event[]
-unresolvedAttribute { aex ->
-    if (getType(aex.objectExpression)==classNodeFor(String)) {
-        storeType(aex,classNodeFor(String))
+unresolvedAttribute { aexp ->
+    if (getType(aexp.objectExpression) == STRING_TYPE) {
+        storeType(aexp, STRING_TYPE)
         handled = true
     }
 }
diff --git a/src/spec/test-resources/unresolvedproperty.groovy b/src/spec/test-resources/unresolvedproperty.groovy
index 386cdae3a4..89a3f3815b 100644
--- a/src/spec/test-resources/unresolvedproperty.groovy
+++ b/src/spec/test-resources/unresolvedproperty.groovy
@@ -20,9 +20,9 @@
 
 // tag::event[]
 unresolvedProperty { pexp ->
-    if ('longueur'==pexp.propertyAsString &&
-        getType(pexp.objectExpression)==classNodeFor(String)) {
-        storeType(pexp,classNodeFor(int))
+    if (pexp.propertyAsString == 'longueur' &&
+            getType(pexp.objectExpression) == STRING_TYPE) {
+        storeType(pexp, int_TYPE)
         handled = true
     }
 }
diff --git a/src/spec/test-resources/unresolvedvariable.groovy b/src/spec/test-resources/unresolvedvariable.groovy
index b8e112bb5b..3d848434c0 100644
--- a/src/spec/test-resources/unresolvedvariable.groovy
+++ b/src/spec/test-resources/unresolvedvariable.groovy
@@ -17,9 +17,9 @@
  *  under the License.
  */
 // tag::event[]
-unresolvedVariable { var ->
-    if ('people' == var.name) {
-        storeType(var, classNodeFor(List))
+unresolvedVariable { vexp ->
+    if (vexp.name == 'people') {
+        storeType(vexp, LIST_TYPE)
         handled = true
     }
 }
diff --git a/src/spec/test/typing/TypeCheckingExtensionSpecTest.groovy b/src/spec/test/typing/TypeCheckingExtensionSpecTest.groovy
index 717b4610a7..8abf6be377 100644
--- a/src/spec/test/typing/TypeCheckingExtensionSpecTest.groovy
+++ b/src/spec/test/typing/TypeCheckingExtensionSpecTest.groovy
@@ -18,10 +18,11 @@
  */
 package typing
 
-import groovy.$Temp
 import groovy.test.GroovyAssert
 import groovy.test.GroovyTestCase
 import groovy.transform.TypeChecked
+import groovy.transform.stc.ClosureParams
+import groovy.transform.stc.SimpleType
 import groovy.xml.MarkupBuilder
 import org.codehaus.groovy.control.CompilerConfiguration
 import org.codehaus.groovy.control.MultipleCompilationErrorsException
@@ -29,7 +30,7 @@ import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
 
 import static asciidoctor.Utils.stripAsciidocMarkup
 
-class TypeCheckingExtensionSpecTest extends GroovyTestCase {
+final class TypeCheckingExtensionSpecTest extends GroovyTestCase {
 
     void testIntro() {
         def out = new PrintWriter(new ByteArrayOutputStream())
@@ -46,95 +47,39 @@ class TypeCheckingExtensionSpecTest extends GroovyTestCase {
         // end::intro_stc_extensions[]
     }
 
-    void testRobotExample() {
-
-        def err = shouldFail(MultipleCompilationErrorsException, '''import groovy.transform.TypeChecked
-import org.codehaus.groovy.control.CompilerConfiguration
-import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
-import typing.Robot
-
-def script = """
-// tag::example_robot_script[]
-robot.move 100
-// end::example_robot_script[]
-"""
-
-// tag::example_robot_setup[]
-def config = new CompilerConfiguration()
-config.addCompilationCustomizers(
-    new ASTTransformationCustomizer(TypeChecked)            // <1>
-)
-def shell = new GroovyShell(config)                         // <2>
-def robot = new Robot()
-shell.setVariable('robot', robot)
-shell.evaluate(script)                                      // <3>
-// end::example_robot_setup[]
-''')
-        assert err.contains(stripAsciidocMarkup('''
-// tag::example_robot_expected_err[]
-[Static type checking] - The variable [robot] is undeclared.
-// end::example_robot_expected_err[]
-'''))
-    }
-
-    void testRobotExampleFixed() {
-        assertScript '''import groovy.transform.TypeChecked
-import org.codehaus.groovy.control.CompilerConfiguration
-import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
-import typing.Robot
-
-def script = """
-robot.move 100
-"""
-
-def config = new CompilerConfiguration()
-// tag::example_robot_fixed_conf[]
-config.addCompilationCustomizers(
-    new ASTTransformationCustomizer(
-        TypeChecked,
-        extensions:['robotextension.groovy'])
-)
-// end::example_robot_fixed_conf[]
-def shell = new GroovyShell(config)
-def robot = new Robot()
-shell.setVariable('robot', robot)
-shell.evaluate(script)
-'''
-    }
-
     void testSetup() {
-        assertScriptWithExtension('setup.groovy', '''
+        assertScriptWithExtension 'setup.groovy', '''
             1+1
-        ''')
+        '''
     }
 
     void testFinish() {
-        assertScriptWithExtension('finish.groovy', '''
+        assertScriptWithExtension 'finish.groovy', '''
             1+1
-        ''')
+        '''
     }
 
     void testUnresolvedVariable() {
-        assertScriptWithExtension('unresolvedvariable.groovy', '''
+        assertScriptWithExtension 'unresolvedvariable.groovy', '''
             assert people.size() == 2
-        ''') {
+        ''', {
             it.setVariable('people', ['John','Meg'])
         }
     }
 
     void testUnresolvedProperty() {
         use (SpecSupport) {
-            assertScriptWithExtension('unresolvedproperty.groovy', '''
-            assert 'string'.longueur == 6
-        ''')
+            assertScriptWithExtension 'unresolvedproperty.groovy', '''
+                assert 'string'.longueur == 6
+            '''
         }
     }
 
     void testUnresolvedAttribute() {
         try {
-            assertScriptWithExtension('unresolvedattribute.groovy', '''
-            assert 'string'.@longueur == 6
-        ''')
+            assertScriptWithExtension 'unresolvedattribute.groovy', '''
+                assert 'string'.@longueur == 6
+            '''
             assert false
         } catch (MissingFieldException mfe) {
             // ok
@@ -143,9 +88,9 @@ shell.evaluate(script)
 
     void testBeforeMethodCall() {
         try {
-            assertScriptWithExtension('beforemethodcall.groovy', '''
-            'string'.toUpperCase()
-        ''')
+            assertScriptWithExtension 'beforemethodcall.groovy', '''
+                'string'.toUpperCase()
+            '''
             assert false
         } catch (MultipleCompilationErrorsException err) {
             assert err.message.contains('[Static type checking] - Not allowed')
@@ -154,9 +99,9 @@ shell.evaluate(script)
 
     void testAfterMethodCall() {
         try {
-            assertScriptWithExtension('aftermethodcall.groovy', '''
-            'string'.toUpperCase()
-        ''')
+            assertScriptWithExtension 'aftermethodcall.groovy', '''
+                'string'.toUpperCase()
+            '''
             assert false
         } catch (MultipleCompilationErrorsException err) {
             assert err.message.contains('[Static type checking] - Not allowed')
@@ -165,11 +110,11 @@ shell.evaluate(script)
 
     void testOnMethodSelection() {
         try {
-            assertScriptWithExtension('onmethodselection.groovy', '''
-            'string'.toUpperCase()
-            'string 2'.toLowerCase()
-            'string 3'.length()
-        ''')
+            assertScriptWithExtension 'onmethodselection.groovy', '''
+                'string'.toUpperCase()
+                'string 2'.toLowerCase()
+                'string 3'.length()
+            '''
             assert false
         } catch (MultipleCompilationErrorsException err) {
             assert err.message.contains('[Static type checking] - You can use only 2 calls on String in your source code')
@@ -178,33 +123,33 @@ shell.evaluate(script)
 
     void testMethodNotFound() {
         use (SpecSupport) {
-            assertScriptWithExtension('methodnotfound.groovy', '''
-            assert 'string'.longueur() == 6
-        ''')
+            assertScriptWithExtension 'methodnotfound.groovy', '''
+                assert 'string'.longueur() == 6
+            '''
         }
     }
 
     void testBeforeVisitMethod() {
         use (SpecSupport) {
-            assertScriptWithExtension('beforevisitmethod.groovy', '''
-            void skipIt() {
-                'blah'.doesNotExist()
-            }
-            skipIt()
-        ''')
+            assertScriptWithExtension 'beforevisitmethod.groovy', '''
+                void skipIt() {
+                    'blah'.doesNotExist()
+                }
+                skipIt()
+            '''
         }
     }
 
     void testAfterVisitMethod() {
         try {
-            assertScriptWithExtension('aftervisitmethod.groovy', '''
-            void foo() {
-               'string'.toUpperCase()
-               'string 2'.toLowerCase()
-               'string 3'.length()
-            }
-            foo()
-        ''')
+            assertScriptWithExtension 'aftervisitmethod.groovy', '''
+                void foo() {
+                   'string'.toUpperCase()
+                   'string 2'.toLowerCase()
+                   'string 3'.length()
+                }
+                foo()
+            '''
             assert false
         } catch (MultipleCompilationErrorsException err) {
             assert err.message.contains('[Static type checking] - Method foo contains more than 2 method calls')
@@ -213,10 +158,10 @@ shell.evaluate(script)
 
     void testBeforeVisitClass() {
         try {
-            assertScriptWithExtension('beforevisitclass.groovy', '''
-            class someclass {
-            }
-        ''')
+            assertScriptWithExtension 'beforevisitclass.groovy', '''
+                class someclass {
+                }
+            '''
             assert false
         } catch (MultipleCompilationErrorsException err) {
             assert err.message.contains("[Static type checking] - Class 'someclass' doesn't start with an uppercase letter")
@@ -225,10 +170,10 @@ shell.evaluate(script)
 
     void testAfterVisitClass() {
         try {
-            assertScriptWithExtension('aftervisitclass.groovy', '''
-            class someclass {
-            }
-        ''')
+            assertScriptWithExtension 'aftervisitclass.groovy', '''
+                class someclass {
+                }
+            '''
             assert false
         } catch (MultipleCompilationErrorsException err) {
             assert err.message.contains("[Static type checking] - Class 'someclass' doesn't start with an uppercase letter")
@@ -236,89 +181,103 @@ shell.evaluate(script)
     }
 
     void testIncompatibleAssignment() {
-        use (SpecSupport) {
-            assertScriptWithExtension('incompatibleassignment.groovy', '''import groovy.transform.TypeChecked
-import groovy.transform.TypeCheckingMode
+        assertScriptWithExtension 'incompatibleassignment.groovy', '''
+            import groovy.transform.TypeChecked
+            import groovy.transform.TypeCheckingMode
+
+            @TypeChecked(TypeCheckingMode.SKIP)
+            class Point {
+                int x, y
 
-@TypeChecked(TypeCheckingMode.SKIP)
-class Point {
-    int x, y = 1
+                void setProperty(String name, value) {
+                    def v = value instanceof Closure ? value() : value
+                    this.@(name) *= v // set field to prevent recursion
+                }
+            }
 
-    void setProperty(String name, value) {
-        def v = value instanceof Closure ? value() : value
-        this.@"$name" *= v
+            def p = new Point(x: 3, y: 4)
+            p.x = { 2 } // allowed by setProperty
+            assert p.x == 6
+        '''
     }
-}
 
-def p = new Point(x: 3, y: 4)
-p.x = { 2 }
-assert p.x == 6
-        ''')
-        }
+    void testIncompatibleReturnType() {
+        assertScriptWithExtension 'incompatiblereturntype.groovy', '''
+            Date m() { '1' }
+        '''
     }
 
     void testAmbiguousMethods() {
         def err = shouldFail {
-            assertScriptWithExtension('ambiguousmethods.groovy', '''
-            int foo(Integer x) { 1 }
-            int foo(String s) { 2 }
-            int foo(Date d) { 3 }
-            assert foo(null) == 2
-        ''')
+            assertScriptWithExtension 'ambiguousmethods.groovy', '''
+                int foo(Integer x) { 1 }
+                int foo(String s) { 2 }
+                int foo(Date d) { 3 }
+                assert foo(null) == 2
+            '''
         }
-        assert err.contains(/Cannot resolve which method to invoke for [null] due to overlapping prototypes/)
+        assert err =~ /Cannot resolve which method to invoke for \[null\] due to overlapping prototypes/
     }
 
     void testSupportMethods() {
-        assertScriptWithExtension('selfcheck.groovy','''
+        assertScriptWithExtension 'selfcheck.groovy', '''
             class Foo {}
             1+1
-        ''')
+        '''
     }
 
     void testNewMethod() {
-        assertScriptWithExtension('newmethod.groovy','''
+        assertScriptWithExtension 'newmethod.groovy','''
             class Foo {
                 def methodMissing(String name, args) { this }
             }
             def f = new Foo()
             f.foo().bar()
-        ''')
+        '''
     }
 
     void testScopingMethods() {
-        assertScriptWithExtension('scoping.groovy','''
+        assertScriptWithExtension 'scoping.groovy','''
             1+1
-        ''')
-        assertScriptWithExtension('scoping_alt.groovy','''
+        '''
+        assertScriptWithExtension 'scoping_alt.groovy','''
             1+1
-        ''')
+        '''
     }
 
-    void testPrecompiledExtensions() {
-        assertScript '''import groovy.transform.TypeChecked
+    //--------------------------------------------------------------------------
+
+    void testRobotExample() {
+        def err = shouldFail(MultipleCompilationErrorsException, '''import groovy.transform.TypeChecked
 import org.codehaus.groovy.control.CompilerConfiguration
 import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
 import typing.Robot
 
 def script = """
+// tag::example_robot_script[]
 robot.move 100
+// end::example_robot_script[]
 """
 
+// tag::example_robot_setup[]
 def config = new CompilerConfiguration()
-// tag::setup_precompiled[]
 config.addCompilationCustomizers(
-    new ASTTransformationCustomizer(
-        TypeChecked,
-        extensions:['typing.PrecompiledExtension'])
+    new ASTTransformationCustomizer(TypeChecked)            // <1>
 )
-// end::setup_precompiled[]
-def shell = new GroovyShell(config)
+def shell = new GroovyShell(config)                         // <2>
 def robot = new Robot()
 shell.setVariable('robot', robot)
-shell.evaluate(script)
-'''
+shell.evaluate(script)                                      // <3>
+// end::example_robot_setup[]
+''')
+        assert err.contains(stripAsciidocMarkup('''
+// tag::example_robot_expected_err[]
+[Static type checking] - The variable [robot] is undeclared.
+// end::example_robot_expected_err[]
+'''))
+    }
 
+    void testRobotExampleFixed() {
         assertScript '''import groovy.transform.TypeChecked
 import org.codehaus.groovy.control.CompilerConfiguration
 import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
@@ -329,11 +288,13 @@ robot.move 100
 """
 
 def config = new CompilerConfiguration()
+// tag::example_robot_fixed_conf[]
 config.addCompilationCustomizers(
     new ASTTransformationCustomizer(
         TypeChecked,
-        extensions:['typing.PrecompiledJavaExtension'])
+        extensions:['robotextension.groovy'])
 )
+// end::example_robot_fixed_conf[]
 def shell = new GroovyShell(config)
 def robot = new Robot()
 shell.setVariable('robot', robot)
@@ -342,7 +303,6 @@ shell.evaluate(script)
     }
 
     void testRobotExamplePassWithCompileStatic() {
-
         assertScript '''import groovy.transform.CompileStatic
 import org.codehaus.groovy.control.CompilerConfiguration
 import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
@@ -368,7 +328,6 @@ shell.evaluate(script)
     }
 
     void testRobotExampleDelegatingScript() {
-
         assertScript '''import groovy.transform.CompileStatic
 import org.codehaus.groovy.control.CompilerConfiguration
 import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
@@ -392,7 +351,6 @@ runner.run()                                                // <4>
     }
 
     void testRobotExampleFailsWithCompileStatic() {
-
         def err = GroovyAssert.shouldFail '''import groovy.transform.CompileStatic
 import org.codehaus.groovy.control.CompilerConfiguration
 import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
@@ -425,7 +383,6 @@ java.lang.NoSuchMethodError: java.lang.Object.move()Ltyping/Robot;
     }
 
     void testRobotExamplePassesWithCompileStatic() {
-
         assertScript '''import groovy.transform.CompileStatic
 import org.codehaus.groovy.control.CompilerConfiguration
 import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
@@ -449,6 +406,54 @@ runner.run()
 '''
     }
 
+    void testPrecompiledExtensions() {
+        assertScript '''import groovy.transform.TypeChecked
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
+import typing.Robot
+
+def script = """
+robot.move 100
+"""
+
+def config = new CompilerConfiguration()
+// tag::setup_precompiled[]
+config.addCompilationCustomizers(
+    new ASTTransformationCustomizer(
+        TypeChecked,
+        extensions:['typing.PrecompiledExtension'])
+)
+// end::setup_precompiled[]
+def shell = new GroovyShell(config)
+def robot = new Robot()
+shell.setVariable('robot', robot)
+shell.evaluate(script)
+'''
+
+        assertScript '''import groovy.transform.TypeChecked
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
+import typing.Robot
+
+def script = """
+robot.move 100
+"""
+
+def config = new CompilerConfiguration()
+config.addCompilationCustomizers(
+    new ASTTransformationCustomizer(
+        TypeChecked,
+        extensions:['typing.PrecompiledJavaExtension'])
+)
+def shell = new GroovyShell(config)
+def robot = new Robot()
+shell.setVariable('robot', robot)
+shell.evaluate(script)
+'''
+    }
+
+    //--------------------------------------------------------------------------
+
     void doDelegateResolutionForPropertyReadTest(String strategy, String expected) {
         assertScript """import groovy.transform.CompileStatic
 class ADelegate {
@@ -466,13 +471,13 @@ class AClass {
     }
 
     def x = "owner"
-    
+
     def test() {
         def theDelegate = new ADelegate()
         def res = closureExecuter(theDelegate) {
             return x
         }
-        
+
         return res
     }
 }
@@ -480,7 +485,6 @@ assert new AClass().test() == "$expected"
 """
     }
 
-
     void doDelegateResolutionForPropertyWriteTest(String strategy, String expected) {
         assertScript """import groovy.transform.CompileStatic
 class ADelegate {
@@ -498,13 +502,13 @@ class AClass {
     }
 
     def x = "owner"
-    
+
     def test() {
         def theDelegate = new ADelegate()
         def res = closureExecuter(theDelegate) {
             x = "changed"
         }
-        
+
         return [theDelegate.x, this.x].toSet()
     }
 }
@@ -590,38 +594,38 @@ new DelegateTest().delegate()
 
     void testDelegateVariableFromDifferentOwningClass() {
         assertScript '''
-        @groovy.transform.CompileStatic
-        class A {
-            static private int MAX_LINES = 2
-            static class B {
-                @Delegate
-                private Map<String, Object> delegate = [:]
-                void m(int c) {
-                    if (c > MAX_LINES) {
-                        return
+            @groovy.transform.CompileStatic
+            class A {
+                static private int MAX_LINES = 2
+                static class B {
+                    @Delegate
+                    private Map<String, Object> delegate = [:]
+                    void m(int c) {
+                        if (c > MAX_LINES) {
+                            return
+                        }
                     }
                 }
             }
-        }
-        null
+            null
         '''
     }
 
+    //--------------------------------------------------------------------------
+
+    private static assertScriptWithExtension(String extensionName, String script,
+            @ClosureParams(value=SimpleType, options='groovy.lang.Binding') Closure<Void> configurator=null) {
+        def shell = new GroovyShell(new CompilerConfiguration().addCompilationCustomizers(
+                new ASTTransformationCustomizer(TypeChecked, extensions:[extensionName])))
+        if (configurator) {
+            configurator.call(shell.context)
+        }
+        shell.evaluate(script)
+    }
+
     private static class SpecSupport {
         static int getLongueur(String self) { self.length() }
         static int longueur(String self) { self.length() }
         static void doesNotExist(String self) {}
     }
-
-    private def assertScriptWithExtension(String extensionName, String code, Closure<Void> configurator=null) {
-        def config = new CompilerConfiguration()
-        config.addCompilationCustomizers(
-                new ASTTransformationCustomizer(TypeChecked, extensions:[extensionName]))
-        def binding = new Binding()
-        def shell = new GroovyShell(binding,config)
-        if (configurator) {
-            configurator.call(binding)
-        }
-        shell.evaluate(code)
-    }
 }
diff --git a/src/test/groovy/transform/stc/TypeCheckingExtensionsTest.groovy b/src/test/groovy/transform/stc/TypeCheckingExtensionsTest.groovy
index bb4793d4c5..b82274b65c 100644
--- a/src/test/groovy/transform/stc/TypeCheckingExtensionsTest.groovy
+++ b/src/test/groovy/transform/stc/TypeCheckingExtensionsTest.groovy
@@ -86,7 +86,8 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
         extension = 'groovy/transform/stc/NewMethodAndIsGeneratedTestExtension.groovy'
         shouldFailWithMessages '''
             'foo'
-        ''', 'Extension was executed properly'
+        ''',
+        'Extension was executed properly'
     }
 
     void testUndefinedVariable() {
@@ -103,8 +104,9 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
     void testUndefinedVariableNoHandle() {
         extension = 'groovy/transform/stc/UndefinedVariableNoHandleTestExtension.groovy'
         shouldFailWithMessages '''
-                foo.toUpperCase() // normal type checker would fail here
-            ''', 'The variable [foo] is undeclared'
+            foo.toUpperCase() // normal type checker would fail here
+        ''',
+        'The variable [foo] is undeclared'
     }
 
     void testMissingMethod() {
@@ -112,7 +114,9 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             String msg = 'foo'
             msg.TOUPPERCASE()
-        ''', 'Cannot find matching method'
+        ''',
+        'Cannot find matching method'
+
         extension = 'groovy/transform/stc/MissingMethod1TestExtension.groovy'
         try {
             assertScript '''
@@ -130,7 +134,9 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
             String msg = 'foo'
             msg.SIZE()
             msg.CONCAT('bar')
-        ''', 'Cannot find matching method java.lang.String#SIZE()', 'Cannot find matching method java.lang.String#CONCAT(java.lang.String)'
+        ''',
+        'Cannot find matching method java.lang.String#SIZE()', 'Cannot find matching method java.lang.String#CONCAT(java.lang.String)'
+
         extension = 'groovy/transform/stc/MissingMethod2TestExtension.groovy'
         try {
             assertScript '''
@@ -157,7 +163,8 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
         extension = 'groovy/transform/stc/PrefixChangerTestExtension.groovy'
         shouldFailWithMessages '''
            int x = 'foo'
-        ''', '[Custom] - Cannot assign value of type java.lang.String to variable of type int'
+        ''',
+        '[Custom] - Cannot assign value of type java.lang.String to variable of type int'
     }
 
     void testAfterMethodCallHook() {
@@ -165,8 +172,9 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             String count = 'foo'
             sprintf("Count = %d", count)
-        ''', 'Parameter types didn\'t match types expected from the format String',
-                'For placeholder 1 [%d] expected \'int\' but was \'java.lang.String\''
+        ''',
+        'Parameter types didn\'t match types expected from the format String',
+        'For placeholder 1 [%d] expected \'int\' but was \'java.lang.String\''
     }
 
     void testBeforeMethodCallHook() {
@@ -176,7 +184,8 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
             String BOO() { 'bar' }
             method() // ok
             BOO() // error
-        ''', 'Calling a method which is all uppercase is not allowed'
+        ''',
+        'Calling a method which is all uppercase is not allowed'
     }
 
     void testBeforeMethodHook() {
@@ -184,7 +193,8 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             String method() { 'foo' } // ok
             String BOO() { 'bar' } // error
-        ''', 'Defining method which is all uppercase is not allowed'
+        ''',
+        'Defining method which is all uppercase is not allowed'
     }
 
     void testAfterMethodHook() {
@@ -192,26 +202,27 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             String method() { 'foo' } // ok
             String BOO() { 'bar' } // error
-        ''', 'Defining method which is all uppercase is not allowed'
+        ''',
+        'Defining method which is all uppercase is not allowed'
     }
 
     void testMethodSelection() {
         // first step checks that without extension, type checking works properly
         extension = null
         assertScript '''
-        @ASTTest(phase=INSTRUCTION_SELECTION, value={
-            assert node.getNodeMetaData('selected') == null
-        })
-        def str = 'foo'.toUpperCase()
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                assert node.getNodeMetaData('selected') == null
+            })
+            def str = 'foo'.toUpperCase()
         '''
 
         // then we use a type checking extension, we add node metadata
         extension = 'groovy/transform/stc/OnMethodSelectionTestExtension.groovy'
         assertScript '''
-        @ASTTest(phase=INSTRUCTION_SELECTION, value={
-            assert node.getNodeMetaData('selected') == true
-        })
-        def str = 'foo'.toUpperCase()
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                assert node.getNodeMetaData('selected') == true
+            })
+            def str = 'foo'.toUpperCase()
         '''
     }
 
@@ -219,7 +230,8 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
         extension = null
         shouldFailWithMessages '''
             'str'.FOO
-        ''', 'No such property: FOO for class: java.lang.String'
+        ''',
+        'No such property: FOO for class: java.lang.String'
 
         extension = 'groovy/transform/stc/UnresolvedPropertyTestExtension.groovy'
         assertScript '''
@@ -234,7 +246,8 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
         extension = null
         shouldFailWithMessages '''
             'str'.@FOO
-        ''', 'No such attribute: FOO for class: java.lang.String'
+        ''',
+        'No such attribute: FOO for class: java.lang.String'
 
         extension = 'groovy/transform/stc/UnresolvedAttributeTestExtension.groovy'
         assertScript '''
@@ -254,7 +267,8 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
             new Support().foo {
                 'a'.toUpperCase()
             }
-        ''', 'Scope enter and exit behave correctly' // we're using shouldFail just to verify that the extension is ran
+        ''',
+        'Scope enter and exit behave correctly' // we're using shouldFail just to verify that the extension is ran
     }
 
     void testMatchingArguments() {
@@ -268,11 +282,12 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
             two('foo', 1)
             three('foo', 2, new Date())
             three('foo', (Integer)2, new Date())
-        ''', 'Method [zero] with matching arguments found: 0',
-             'Method [concat] with matching arguments found: 1',
-                'Method [two] with matching arguments found: 2',
-                'Method [three] with matching arguments found: 3',
-                'Method [three] with matching arguments found: 3'
+        ''',
+        'Method [zero] with matching arguments found: 0',
+        'Method [concat] with matching arguments found: 1',
+        'Method [two] with matching arguments found: 2',
+        'Method [three] with matching arguments found: 3',
+        'Method [three] with matching arguments found: 3'
     }
 
     void testFirstArgsMatches() {
@@ -283,9 +298,10 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
             two('foo', 1)
             three('foo', 2, new Date())
             three('foo', (Integer)2, new Date())
-        ''', 'Method [two] with matching arguments found: 2',
-                'Method [three] with matching arguments found: 3',
-                'Method [three] with matching arguments found: 3'
+        ''',
+        'Method [two] with matching arguments found: 2',
+        'Method [three] with matching arguments found: 3',
+        'Method [three] with matching arguments found: 3'
     }
 
     void testNthArgMatches() {
@@ -296,21 +312,23 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
             two('foo', 1)
             three('foo', 2, new Date())
             three('foo', (Integer)2, new Date())
-        ''', 'Method [two] with matching argument found: [0, class java.lang.String]',
-             'Method [two] with matching argument found: [1, class java.lang.Integer]',
-             'Method [three] with matching argument found: [0, class java.lang.String]',
-             'Method [three] with matching argument found: [1, class java.lang.Integer]',
-             'Method [three] with matching argument found: [2, class java.util.Date]',
-             'Method [three] with matching argument found: [0, class java.lang.String]',
-             'Method [three] with matching argument found: [1, class java.lang.Integer]',
-             'Method [three] with matching argument found: [2, class java.util.Date]'
+        ''',
+        'Method [two] with matching argument found: [0, class java.lang.String]',
+        'Method [two] with matching argument found: [1, class java.lang.Integer]',
+        'Method [three] with matching argument found: [0, class java.lang.String]',
+        'Method [three] with matching argument found: [1, class java.lang.Integer]',
+        'Method [three] with matching argument found: [2, class java.util.Date]',
+        'Method [three] with matching argument found: [0, class java.lang.String]',
+        'Method [three] with matching argument found: [1, class java.lang.Integer]',
+        'Method [three] with matching argument found: [2, class java.util.Date]'
     }
 
     void testIncompatibleAssignment() {
         extension = null
         shouldFailWithMessages '''
             int x = 'foo'
-        ''', 'Cannot assign value of type java.lang.String to variable of type int'
+        ''',
+        'Cannot assign value of type java.lang.String to variable of type int'
 
         extension = 'groovy/transform/stc/IncompatibleAssignmentTestExtension.groovy'
         assertScript '''
@@ -326,7 +344,8 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
             int x = 1
             Date y = new Date()
             x+y
-        ''', 'Cannot find matching method int#plus(java.util.Date)'
+        ''',
+        'Cannot find matching method int#plus(java.util.Date)'
 
         extension = 'groovy/transform/stc/BinaryOperatorTestExtension.groovy'
         assertScript '''
@@ -344,7 +363,8 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
             int x = 1
             Date y = new Date()
             x << y
-        ''', 'Cannot find matching method int#leftShift(java.util.Date)'
+        ''',
+        'Cannot find matching method int#leftShift(java.util.Date)'
 
         extension = 'groovy/transform/stc/BinaryOperatorTestExtension.groovy'
         assertScript '''
@@ -357,45 +377,40 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
     }
 
     void testDelegatesTo() {
+        String source = '''
+            class Item {
+                void pick() {}
+            }
+            void build(Closure c) {
+                c.delegate = new Item()
+                c.call()
+            }
+            build {
+                pick()
+            }
+        '''
+
         extension = null
-        shouldFailWithMessages '''
-        class Item { void pick(){} }
-        void build(Closure arg) {
-            arg.delegate = new Item()
-            arg()
-        }
-        build {
-            pick()
-        }
-        ''', 'Cannot find matching method'
+        shouldFailWithMessages(source, 'Cannot find matching method')
 
         extension = 'groovy/transform/stc/DelegatesToTestExtension.groovy'
-        assertScript '''
-        class Item { void pick(){} }
-        void build(Closure arg) {
-            arg.delegate = new Item()
-            arg()
-        }
-        build {
-            pick()
-        }
-        '''
+        assertScript(source)
     }
 
     void testIsAnnotatedBy() {
         extension = null
         assertScript '''
-        @groovy.transform.stc.MyType(String)
-        int foo() { 1 }
+            @groovy.transform.stc.MyType(String)
+            int foo() { 1 }
         '''
 
         extension = 'groovy/transform/stc/AnnotatedByTestExtension.groovy'
         assertScript '''
-        @groovy.transform.stc.MyType(String)
-        @ASTTest(phase=INSTRUCTION_SELECTION,value={
-            assert node.getNodeMetaData(INFERRED_RETURN_TYPE) == STRING_TYPE
-        })
-        int foo() { 1 }
+            @groovy.transform.stc.MyType(String)
+            @ASTTest(phase=INSTRUCTION_SELECTION,value={
+                assert node.getNodeMetaData(INFERRED_RETURN_TYPE) == STRING_TYPE
+            })
+            int foo() { 1 }
         '''
     }
 
@@ -444,7 +459,7 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
                 b.elems()
             '''
         } catch (MissingMethodException e) {
-            // it's ok
+            // expected
         }
     }
 
@@ -459,12 +474,14 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
         ''', 'Reference to method is ambiguous'
         // fail with error from runtime
         extension = 'groovy/transform/stc/AmbiguousMethods.groovy'
-        shouldFail { assertScript '''
-            int foo(Integer x) { 1 }
-            int foo(String s) { 2 }
-            int foo(Date d) { 3 }
-            assert foo(null) == 2
-        '''}
+        shouldFail {
+            assertScript '''
+                int foo(Integer x) { 1 }
+                int foo(String s) { 2 }
+                int foo(Date d) { 3 }
+                assert foo(null) == 2
+            '''
+        }
     }
 
     void testIncompatibleReturnType() {
@@ -472,7 +489,9 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             Date foo() { '1' }
             true
-        ''', 'Cannot return value of type'
+        ''',
+        'Cannot return value of type'
+
         extension = 'groovy/transform/stc/IncompatibleReturnTypeTestExtension.groovy'
         assertScript '''
             Date foo() { '1' }
@@ -485,11 +504,12 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
         assertScript '''
             println 'Everything is ok'
         '''
+
         extension = 'groovy.transform.stc.PrecompiledExtension'
         shouldFailWithMessages '''
             println 'Everything is ok'
-        ''', 'Error thrown from extension'
-
+        ''',
+        'Error thrown from extension'
     }
 
     void testPrecompiledExtensionNotExtendingTypeCheckingDSL() {
@@ -497,10 +517,12 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
         assertScript '''
             println 'Everything is ok'
         '''
+
         extension = 'groovy.transform.stc.PrecompiledExtensionNotExtendingDSL'
         shouldFailWithMessages '''
             println 'Everything is ok'
-        ''', 'Error thrown from extension in setup', 'Error thrown from extension in onMethodSelection'
-
+        ''',
+        'Error thrown from extension in setup',
+        'Error thrown from extension in onMethodSelection'
     }
 }