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 17:28:56 UTC
[groovy] branch master updated: GROOVY-10771: STC: extension documentation
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new 0a7f240b88 GROOVY-10771: STC: extension documentation
0a7f240b88 is described below
commit 0a7f240b88b212e26c1bd6f570000c68db6738bc
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Oct 11 12:28:10 2022 -0500
GROOVY-10771: STC: extension documentation
---
.../stc/GroovyTypeCheckingExtensionSupport.java | 72 +++++
src/spec/doc/_type-checking-extensions.adoc | 26 +-
src/spec/test-resources/aftervisitclass.groovy | 2 -
src/spec/test-resources/beforevisitclass.groovy | 2 -
...tclass.groovy => incompatiblereturntype.groovy} | 9 +-
src/spec/test-resources/selfcheck.groovy | 1 -
.../typing/TypeCheckingExtensionSpecTest.groovy | 345 +++++++++++----------
7 files changed, 277 insertions(+), 180 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 eb31312bfa..cc129cf573 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/GroovyTypeCheckingExtensionSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/GroovyTypeCheckingExtensionSupport.java
@@ -415,6 +415,78 @@ public class GroovyTypeCheckingExtensionSupport extends AbstractTypeCheckingExte
return methodList;
}
+ /**
+ * 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 by the type checker before type checking a class</dd>
+ * <dt>afterVisitClass</dt> <dd>Registers closure that runs by the type checker after having finished the visit of a type checked class</dd>
+ * <dt>beforeVisitMethod</dt> <dd>Registers closure that runs by the type checker before type checking a method body</dd>
+ * <dt>afterVisitMethod</dt> <dd>Registers closure that runs by the type checker 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 by the type checker 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 by the type checker 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 fa0b668424..0cda7b3cb5 100644
--- a/src/spec/doc/_type-checking-extensions.adoc
+++ b/src/spec/doc/_type-checking-extensions.adoc
@@ -508,7 +508,7 @@ inner/anonymous class defined in the same class with is not skipped.
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]
@@ -525,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::../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%"]
|===
diff --git a/src/spec/test-resources/aftervisitclass.groovy b/src/spec/test-resources/aftervisitclass.groovy
index 6417770fd4..faccbd6721 100644
--- a/src/spec/test-resources/aftervisitclass.groovy
+++ b/src/spec/test-resources/aftervisitclass.groovy
@@ -16,8 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-import org.codehaus.groovy.ast.ClassNode
-
// tag::event[]
afterVisitClass { ClassNode classNode ->
def name = classNode.nameWithoutPackage
diff --git a/src/spec/test-resources/beforevisitclass.groovy b/src/spec/test-resources/beforevisitclass.groovy
index 90ee3b93bf..5dd0f53431 100644
--- a/src/spec/test-resources/beforevisitclass.groovy
+++ b/src/spec/test-resources/beforevisitclass.groovy
@@ -16,8 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-import org.codehaus.groovy.ast.ClassNode
-
// tag::event[]
beforeVisitClass { ClassNode classNode ->
def name = classNode.nameWithoutPackage
diff --git a/src/spec/test-resources/aftervisitclass.groovy b/src/spec/test-resources/incompatiblereturntype.groovy
similarity index 75%
copy from src/spec/test-resources/aftervisitclass.groovy
copy to src/spec/test-resources/incompatiblereturntype.groovy
index 6417770fd4..5872bf07df 100644
--- a/src/spec/test-resources/aftervisitclass.groovy
+++ b/src/spec/test-resources/incompatiblereturntype.groovy
@@ -16,13 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
-import org.codehaus.groovy.ast.ClassNode
-
// tag::event[]
-afterVisitClass { ClassNode classNode ->
- def name = classNode.nameWithoutPackage
- if (!(name[0] in 'A'..'Z')) {
- addStaticTypeError("Class '${name}' doesn't start with an uppercase letter",classNode)
+incompatibleReturnType { stmt, type ->
+ if (type == STRING_TYPE) {
+ handled = true
}
}
// end::event[]
\ No newline at end of file
diff --git a/src/spec/test-resources/selfcheck.groovy b/src/spec/test-resources/selfcheck.groovy
index 0e0150b060..fa032d2414 100644
--- a/src/spec/test-resources/selfcheck.groovy
+++ b/src/spec/test-resources/selfcheck.groovy
@@ -16,7 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor
import org.codehaus.groovy.transform.stc.TypeCheckingContext
diff --git a/src/spec/test/typing/TypeCheckingExtensionSpecTest.groovy b/src/spec/test/typing/TypeCheckingExtensionSpecTest.groovy
index 7da5b6ff7f..baab7e4c24 100644
--- a/src/spec/test/typing/TypeCheckingExtensionSpecTest.groovy
+++ b/src/spec/test/typing/TypeCheckingExtensionSpecTest.groovy
@@ -21,6 +21,8 @@ package typing
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.MultipleCompilationErrorsException
@@ -43,95 +45,39 @@ final 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
@@ -140,9 +86,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')
@@ -151,9 +97,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')
@@ -162,11 +108,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')
@@ -175,33 +121,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')
@@ -210,10 +156,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")
@@ -222,10 +168,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")
@@ -233,89 +179,104 @@ 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', '''
+ Closure<Date> c = { '1' }
+ 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
@@ -326,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)
@@ -339,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
@@ -365,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
@@ -389,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
@@ -422,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
@@ -446,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 {
@@ -477,7 +484,6 @@ assert new AClass().test() == "$expected"
"""
}
-
void doDelegateResolutionForPropertyWriteTest(String strategy, String expected) {
assertScript """import groovy.transform.CompileStatic
class ADelegate {
@@ -587,36 +593,39 @@ 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 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) {
+ private static assertScriptWithExtension(String extensionName, String script,
+ @ClosureParams(value=SimpleType, options='groovy.lang.Binding') Closure<Void> configurator=null) {
def shell = GroovyShell.withConfig {
ast(TypeChecked, extensions: [extensionName])
}
if (configurator) {
configurator.call(shell.context)
}
- shell.evaluate(code)
+ 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) {}
}
}