This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
new eee5a0aec4 GROOVY-10771: STC: extension documentation
eee5a0aec4 is described below
commit eee5a0aec4b611fb0b2120be65ce6ddc13ccb6be
Author: Eric Milles <[email protected]>
AuthorDate: Tue Oct 11 12:28:10 2022 -0500
GROOVY-10771: STC: extension documentation
---
.../stc/GroovyTypeCheckingExtensionSupport.java | 78 ++++-
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, 419 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 d45633174b..064afa46d6 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,76 @@ 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>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>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
<<static-type-checking,@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 954237eb52..a1c2a968ed 100644
--- a/src/spec/test/typing/TypeCheckingExtensionSpecTest.groovy
+++ b/src/spec/test/typing/TypeCheckingExtensionSpecTest.groovy
@@ -18,9 +18,10 @@
*/
package typing
-import groovy.$Temp
import groovy.test.GroovyAssert
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
@@ -28,7 +29,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())
@@ -45,95 +46,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
@@ -142,9 +87,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')
@@ -153,9 +98,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')
@@ -164,11 +109,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')
@@ -177,33 +122,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')
@@ -212,10 +157,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")
@@ -224,10 +169,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")
@@ -235,89 +180,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
@@ -328,11 +287,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)
@@ -341,7 +302,6 @@ shell.evaluate(script)
}
void testRobotExamplePassWithCompileStatic() {
-
assertScript '''import groovy.transform.CompileStatic
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
@@ -367,7 +327,6 @@ shell.evaluate(script)
}
void testRobotExampleDelegatingScript() {
-
assertScript '''import groovy.transform.CompileStatic
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
@@ -391,7 +350,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
@@ -424,7 +382,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
@@ -448,6 +405,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 {
@@ -465,13 +470,13 @@ class AClass {
}
def x = "owner"
-
+
def test() {
def theDelegate = new ADelegate()
def res = closureExecuter(theDelegate) {
return x
}
-
+
return res
}
}
@@ -479,7 +484,6 @@ assert new AClass().test() == "$expected"
"""
}
-
void doDelegateResolutionForPropertyWriteTest(String strategy, String
expected) {
assertScript """import groovy.transform.CompileStatic
class ADelegate {
@@ -497,13 +501,13 @@ class AClass {
}
def x = "owner"
-
+
def test() {
def theDelegate = new ADelegate()
def res = closureExecuter(theDelegate) {
x = "changed"
}
-
+
return [theDelegate.x, this.x].toSet()
}
}
@@ -589,38 +593,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'
}
}