From 4b042ef1e7ae43cfbd6b81d19c1e8dee4164d2e6 Mon Sep 17 00:00:00 2001 From: Eric Milles Date: Wed, 5 Jun 2024 15:37:09 -0500 Subject: [PATCH] GROOVY-11399: STC: `getDelegate()`, `getOwner()`, `getThisObject()` type 4_0_X backport --- .../stc/StaticTypeCheckingVisitor.java | 46 ++- .../groovy/transform/stc/BugsSTCTest.groovy | 388 +++++++----------- .../transform/stc/ClosuresSTCTest.groovy | 297 ++++++++++++-- .../stc/WithSTCStandaloneTest.groovy | 77 ---- .../groovy/transform/stc/WithSTCTest.groovy | 80 ---- .../asm/sc/WithStaticCompileTest.groovy | 29 -- 6 files changed, 444 insertions(+), 473 deletions(-) delete mode 100644 src/test/groovy/transform/stc/WithSTCStandaloneTest.groovy delete mode 100644 src/test/groovy/transform/stc/WithSTCTest.groovy delete mode 100644 src/test/org/codehaus/groovy/classgen/asm/sc/WithStaticCompileTest.groovy diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index d5663a340d2..5c2d231dca1 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -601,26 +601,26 @@ public void visitVariableExpression(final VariableExpression vexp) { case "delegate": DelegationMetadata dm = getDelegationMetadata(enclosingClosure.getClosureExpression()); if (dm != null) { - storeType(vexp, dm.getType()); + vexp.putNodeMetaData(INFERRED_TYPE, dm.getType()); return; } // falls through case "owner": if (typeCheckingContext.getEnclosingClosureStack().size() > 1) { - storeType(vexp, CLOSURE_TYPE); + vexp.putNodeMetaData(INFERRED_TYPE, CLOSURE_TYPE.getPlainNodeReference()); return; } // falls through case "thisObject": - storeType(vexp, typeCheckingContext.getEnclosingClassNode()); + vexp.putNodeMetaData(INFERRED_TYPE, makeThis()); return; case "parameterTypes": - storeType(vexp, CLASS_Type.makeArray()); + vexp.putNodeMetaData(INFERRED_TYPE, CLASS_Type.getPlainNodeReference().makeArray()); return; case "maximumNumberOfParameters": case "resolveStrategy": case "directive": - storeType(vexp, int_TYPE); + vexp.putNodeMetaData(INFERRED_TYPE, int_TYPE); return; } } @@ -3611,6 +3611,7 @@ public void visitMethodCallExpression(final MethodCallExpression call) { if (mn.isEmpty() && isThisObjectExpression && call.isImplicitThis() && typeCheckingContext.getEnclosingClosure() != null) { mn = CLOSURE_TYPE.getDeclaredMethods(name); if (!mn.isEmpty()) { + receiver = CLOSURE_TYPE.getPlainNodeReference(); objectExpression.removeNodeMetaData(INFERRED_TYPE); } } @@ -3661,20 +3662,31 @@ && isClassType(receiver) && chosenReceiver.getData() == null && !Boolean.TRUE.eq visitMethodCallArguments(chosenReceiver.getType(), argumentList, true, targetMethodCandidate); callArgsVisited = true; ClassNode returnType = getType(targetMethodCandidate); - if (isUsingGenericsOrIsArrayUsingGenerics(returnType)) { + // GROOVY-5470, GROOVY-6091, GROOVY-9604, GROOVY-11399: + if (isThisObjectExpression && call.isImplicitThis() && typeCheckingContext.getEnclosingClosure() != null + && (targetMethodCandidate == GET_DELEGATE || targetMethodCandidate == GET_OWNER || targetMethodCandidate == GET_THISOBJECT)) { + switch (name) { + case "getDelegate": + DelegationMetadata dm = getDelegationMetadata(typeCheckingContext.getEnclosingClosure().getClosureExpression()); + if (dm != null) { + returnType = dm.getType(); + break; + } + // falls through + case "getOwner": + if (typeCheckingContext.getEnclosingClosureStack().size() > 1) { + returnType = CLOSURE_TYPE.getPlainNodeReference(); + break; + } + // falls through + case "getThisObject": + returnType = makeThis(); + } + } else if (isUsingGenericsOrIsArrayUsingGenerics(returnType)) { ClassNode irtg = inferReturnTypeGenerics(chosenReceiver.getType(), targetMethodCandidate, callArguments, call.getGenericsTypes()); if (irtg != null && implementsInterfaceOrIsSubclassOf(irtg, returnType)) returnType = irtg; } - // GROOVY-6091: use of "delegate" or "getDelegate()" does not make use of @DelegatesTo metadata - if (targetMethodCandidate == GET_DELEGATE && typeCheckingContext.getEnclosingClosure() != null) { - DelegationMetadata md = getDelegationMetadata(typeCheckingContext.getEnclosingClosure().getClosureExpression()); - if (md != null) { - returnType = md.getType(); - } else { - returnType = typeCheckingContext.getEnclosingClassNode(); - } - } // GROOVY-7106, GROOVY-7274, GROOVY-8909, GROOVY-8961, GROOVY-9734, GROOVY-9844, GROOVY-9915, et al. Parameter[] parameters = targetMethodCandidate.getParameters(); if (chosenReceiver.getType().getGenericsTypes() != null && !targetMethodCandidate.isStatic() && !(targetMethodCandidate instanceof ExtensionMethodNode)) { @@ -5240,10 +5252,6 @@ protected ClassNode getType(final ASTNode node) { } if (node instanceof MethodNode) { - if ((node == GET_DELEGATE || node == GET_OWNER || node == GET_THISOBJECT) - && typeCheckingContext.getEnclosingClosure() != null) { - return typeCheckingContext.getEnclosingClassNode(); - } type = ((MethodNode) node).getReturnType(); return Optional.ofNullable(getInferredReturnType(node)).orElse(type); } diff --git a/src/test/groovy/transform/stc/BugsSTCTest.groovy b/src/test/groovy/transform/stc/BugsSTCTest.groovy index 41ded10f056..b66b9d09342 100644 --- a/src/test/groovy/transform/stc/BugsSTCTest.groovy +++ b/src/test/groovy/transform/stc/BugsSTCTest.groovy @@ -30,77 +30,86 @@ class BugsSTCTest extends StaticTypeCheckingTestCase { shouldFailWithMessages ''' def foo(Closure cls) {} def bar() { foo { it / 2 } } - ''', 'Cannot find matching method java.lang.Object#div(int)' + ''', + 'Cannot find matching method java.lang.Object#div(int)' } - void testShouldNotAllowDivBynUntypedVariable() { + void testShouldNotAllowDivByUntypedVariable() { shouldFailWithMessages ''' def foo(Closure cls) {} def bar() { foo { 2 / it } } - ''', 'Cannot find matching method int#div(java.lang.Object)' + ''', + 'Cannot find matching method int#div(java.lang.Object)' } void testShouldNotAllowModOnUntypedVariable() { shouldFailWithMessages ''' def foo(Closure cls) {} def bar() { foo { it % 2 } } - ''', 'Cannot find matching method java.lang.Object#mod(int)' + ''', + 'Cannot find matching method java.lang.Object#mod(int)' } - void testShouldNotAllowModBynUntypedVariable() { + void testShouldNotAllowModByUntypedVariable() { shouldFailWithMessages ''' def foo(Closure cls) {} def bar() { foo { 2 % it } } - ''', 'Cannot find matching method int#mod(java.lang.Object)' + ''', + 'Cannot find matching method int#mod(java.lang.Object)' } void testShouldNotAllowMulOnUntypedVariable() { shouldFailWithMessages ''' def foo(Closure cls) {} def bar() { foo { it * 2 } } - ''', 'Cannot find matching method java.lang.Object#multiply(int)' + ''', + 'Cannot find matching method java.lang.Object#multiply(int)' } - void testShouldNotAllowMulBynUntypedVariable() { + void testShouldNotAllowMulByUntypedVariable() { shouldFailWithMessages ''' def foo(Closure cls) {} def bar() { foo { 2 * it } } - ''', 'Cannot find matching method int#multiply(java.lang.Object)' + ''', + 'Cannot find matching method int#multiply(java.lang.Object)' } void testShouldNotAllowPlusOnUntypedVariable() { shouldFailWithMessages ''' def foo(Closure cls) {} def bar() { foo { it + 2 } } - ''', 'Cannot find matching method java.lang.Object#plus(int)' + ''', + 'Cannot find matching method java.lang.Object#plus(int)' } void testShouldNotAllowPlusWithUntypedVariable() { shouldFailWithMessages ''' def foo(Closure cls) {} def bar() { foo { 2 + it } } - ''', 'Cannot find matching method int#plus(java.lang.Object)' + ''', + 'Cannot find matching method int#plus(java.lang.Object)' } void testShouldNotAllowMinusOnUntypedVariable() { shouldFailWithMessages ''' def foo(Closure cls) {} def bar() { foo { it - 2 } } - ''', 'Cannot find matching method java.lang.Object#minus(int)' + ''', + 'Cannot find matching method java.lang.Object#minus(int)' } void testShouldNotAllowMinusByUntypedVariable() { shouldFailWithMessages ''' def foo(Closure cls) {} def bar() { foo { 2 - it } } - ''', 'Cannot find matching method int#minus(java.lang.Object)' + ''', + 'Cannot find matching method int#minus(java.lang.Object)' } // GROOVY-7929 void testShouldDetectInvalidMethodUseWithinTraitWithCompileStaticAndSelfType() { shouldFailWithMessages ''' class C { - def m() { } } - @groovy.transform.CompileStatic @groovy.transform.SelfType(C) trait T { void test() { x() } } - ''', 'Cannot find matching method #x' + ''', + 'Cannot find matching method #x' } // GROOVY-10102 @@ -155,33 +164,33 @@ class BugsSTCTest extends StaticTypeCheckingTestCase { void testGroovy5487ReturnNull() { assertScript ''' - @ASTTest(phase=INSTRUCTION_SELECTION, value= { - assert node.getNodeMetaData(INFERRED_RETURN_TYPE) == null // null since 2.1.9 - }) - List getList() { - null - } + @ASTTest(phase=INSTRUCTION_SELECTION, value= { + assert node.getNodeMetaData(INFERRED_RETURN_TYPE) == null // null since 2.1.9 + }) + List getList() { + null + } ''' } void testGroovy5487ReturnNullWithExplicitReturn() { assertScript ''' - @ASTTest(phase=INSTRUCTION_SELECTION, value= { - assert node.getNodeMetaData(INFERRED_RETURN_TYPE) == null // null since 2.1.9 - }) - List getList() { - return null - } + @ASTTest(phase=INSTRUCTION_SELECTION, value= { + assert node.getNodeMetaData(INFERRED_RETURN_TYPE) == null // null since 2.1.9 + }) + List getList() { + return null + } ''' } void testGroovy5487ReturnNullWithEmptyBody() { assertScript ''' - @ASTTest(phase=INSTRUCTION_SELECTION, value= { - assert node.getNodeMetaData(INFERRED_RETURN_TYPE) == null // null since 2.1.9 - }) - List getList() { - } + @ASTTest(phase=INSTRUCTION_SELECTION, value= { + assert node.getNodeMetaData(INFERRED_RETURN_TYPE) == null // null since 2.1.9 + }) + List getList() { + } ''' } @@ -204,7 +213,8 @@ class BugsSTCTest extends StaticTypeCheckingTestCase { L items = ['foo', 'bar'] as L items.removeIf({a, b -> 1} as Comparator) assert items - ''', 'Cannot call L#removeIf(java.util.Comparator) with arguments [java.util.Comparator]' + ''', + 'Cannot call L#removeIf(java.util.Comparator) with arguments [java.util.Comparator]' } void testGroovy5482ListsAndFlowTyping() { @@ -219,177 +229,84 @@ class BugsSTCTest extends StaticTypeCheckingTestCase { ''' } - void testClosureThisObjectDelegateOwnerProperty() { - assertScript ''' - class C { - void m() { - C that = this; - - { -> - @ASTTest(phase=INSTRUCTION_SELECTION, value={ - assert node.getNodeMetaData(INFERRED_TYPE)?.name == 'C' - }) - def ref = thisObject - assert ref == that - }(); - - { -> - @ASTTest(phase=INSTRUCTION_SELECTION, value={ - assert node.getNodeMetaData(INFERRED_TYPE)?.name == 'C' - }) - def ref = delegate - assert ref == that - }(); - - { -> - @ASTTest(phase=INSTRUCTION_SELECTION, value={ - assert node.getNodeMetaData(INFERRED_TYPE)?.name == 'C' - }) - def ref = owner - assert ref == that - }(); - } - } - new C().m() - ''' - } - - void testClosureThisObjectDelegateOwnerAccessor() { - assertScript ''' - class C { - void m() { - C that = this; - - { -> - @ASTTest(phase=INSTRUCTION_SELECTION, value={ - assert node.getNodeMetaData(INFERRED_TYPE)?.name == 'C' - }) - def ref = getThisObject() - assert ref == that - }(); - - { -> - @ASTTest(phase=INSTRUCTION_SELECTION, value={ - assert node.getNodeMetaData(INFERRED_TYPE)?.name == 'C' - }) - def ref = getDelegate() - assert ref == that - }(); - - { -> - @ASTTest(phase=INSTRUCTION_SELECTION, value={ - assert node.getNodeMetaData(INFERRED_TYPE)?.name == 'C' - }) - def ref = getOwner() - assert ref == that - }(); - } - } - new C().m() - ''' - } - - // GROOVY-9604 - void testClosureResolveStrategy() { - assertScript ''' - class C { - def m() { - return { -> - resolveStrategy + getResolveStrategy() - }(); - } - } - assert new C().m() == 0 - ''' - } - // GROOVY-5616 void testAssignToGroovyObject() { assertScript ''' - class A {} - GroovyObject obj = new A() + class A {} + GroovyObject obj = new A() ''' } void testAssignJavaClassToGroovyObject() { shouldFailWithMessages ''' - GroovyObject obj = 'foo' - ''', 'Cannot assign value of type java.lang.String to variable of type groovy.lang.GroovyObject' + GroovyObject obj = 'foo' + ''', + 'Cannot assign value of type java.lang.String to variable of type groovy.lang.GroovyObject' } void testCastToGroovyObject() { assertScript ''' - class A {} - GroovyObject obj = new A() + class A {} + GroovyObject obj = new A() ''' } void testAssignInnerClassToGroovyObject() { assertScript ''' - class A { static class B {} } - GroovyObject obj = new A.B() + class A { static class B {} } + GroovyObject obj = new A.B() ''' } void testCastInnerClassToGroovyObject() { assertScript ''' - class A { static class B {} } - GroovyObject obj = (GroovyObject)new A.B() + class A { static class B {} } + GroovyObject obj = (GroovyObject)new A.B() ''' } void testGroovyObjectInGenerics() { assertScript ''' - class A {} - List list = new LinkedList() - list.add(new A()) + class A {} + List list = new LinkedList() + list.add(new A()) ''' } // GROOVY-5656 void testShouldNotThrowAmbiguousMethodError() { assertScript '''import groovy.transform.* + class Expr {} + class VarExpr extends Expr {} - class Expr {} - class VarExpr extends Expr {} - - class ArgList { - ArgList(Expr e1) { } - ArgList(Expr[] es) { } - } + class ArgList { + ArgList(Expr e1) { } + ArgList(Expr[] es) { } + } - class Bug4 { - void test() { - new ArgList(new VarExpr()) + class Bug4 { + void test() { + new ArgList(new VarExpr()) + } } - } - new Bug4().test() + new Bug4().test() ''' } // GROOVY-5793 void testByteAsParameter() { assertScript ''' - void testMethod(java.lang.Byte param){ - println(param) - } - - void execute(){ - testMethod(java.lang.Byte.valueOf("123")) - } - - execute()''' - } + void testMethod(java.lang.Byte param){ + println(param) + } - // GROOVY-5874 (pt.1) - void testClosureSharedVariableInBinExp() { - shouldFailWithMessages ''' - def sum = 0 - def cl1 = { sum = sum + 1 } - def cl2 = { sum = new Date() } + void execute(){ + testMethod(java.lang.Byte.valueOf("123")) + } - ''', 'A closure shared variable [sum] has been assigned with various types' + execute() + ''' } // GROOVY-5870 @@ -403,71 +320,71 @@ class BugsSTCTest extends StaticTypeCheckingTestCase { // GROOVY-5889 void testShouldNotGoIntoInfiniteLoop() { assertScript ''' - class Enclosing { - static class FMessage { - static enum LogLevel { finest, finer, fine, config, info, warning, severe } - LogLevel logLevel + class Enclosing { + static class FMessage { + static enum LogLevel { finest, finer, fine, config, info, warning, severe } + LogLevel logLevel + } } - } - new Enclosing() + new Enclosing() ''' } // GROOVY-5959 void testSwitchCaseShouldNotRemoveBreakStatements() { assertScript ''' - int test(Map token) { - switch(token.type) { - case 'case one': - 1 - break - case 'case two': - 2 - break - default: - 3 - break - } - } - assert test([type:'case one']) == 1 - assert test([type:'case two']) == 2 - assert test([type:'default']) == 3 + int test(Map token) { + switch(token.type) { + case 'case one': + 1 + break + case 'case two': + 2 + break + default: + 3 + break + } + } + assert test([type:'case one']) == 1 + assert test([type:'case two']) == 2 + assert test([type:'default']) == 3 ''' } void testShouldChooseFindMethodFromList() { assertScript ''' - class Mylist implements List { - - int size() { } - boolean isEmpty() {} - boolean contains(final Object o) {} - Iterator iterator() {[].iterator()} - Object[] toArray() {} - Object[] toArray(final Object[] a) {} - boolean add(final Object e) {} - boolean remove(final Object o) {} - boolean containsAll(final Collection c) {} - boolean addAll(final Collection c) {} - boolean addAll(final int index, final Collection c) {} - boolean removeAll(final Collection c) {} - boolean retainAll(final Collection c) {} - void clear() {} - Object get(final int index) {} - Object set(final int index, final Object element) {} - void add(final int index, final Object element) {} - Object remove(final int index) {} - int indexOf(final Object o) {} - int lastIndexOf(final Object o) {} - ListIterator listIterator() {} - ListIterator listIterator(final int index) {} - List subList(final int fromIndex, final int toIndex) {} - } + class Mylist implements List { + int size() { } + boolean isEmpty() {} + boolean contains(final Object o) {} + Iterator iterator() {[].iterator()} + Object[] toArray() {} + Object[] toArray(final Object[] a) {} + boolean add(final Object e) {} + boolean remove(final Object o) {} + boolean containsAll(final Collection c) {} + boolean addAll(final Collection c) {} + boolean addAll(final int index, final Collection c) {} + boolean removeAll(final Collection c) {} + boolean retainAll(final Collection c) {} + void clear() {} + Object get(final int index) {} + Object set(final int index, final Object element) {} + void add(final int index, final Object element) {} + Object remove(final int index) {} + int indexOf(final Object o) {} + int lastIndexOf(final Object o) {} + ListIterator listIterator() {} + ListIterator listIterator(final int index) {} + List subList(final int fromIndex, final int toIndex) {} + } - def whatthe(Mylist a) { - a.find { true } - } - whatthe(new Mylist()) + def whatthe(Mylist a) { + a.find { true } + } + + whatthe(new Mylist()) ''' } @@ -583,32 +500,31 @@ class BugsSTCTest extends StaticTypeCheckingTestCase { void testConstructorNewInstance() { assertScript '''import java.lang.reflect.Constructor + class Person { + String name + Person(String name) { this.name = name } + } -class Person { - String name - Person(String name) { this.name = name } -} - -Constructor ctor = Person.getConstructor(String) -def p = ctor.newInstance('Bob') -assert p.name == 'Bob' -''' + Constructor ctor = Person.getConstructor(String) + def p = ctor.newInstance('Bob') + assert p.name == 'Bob' + ''' } void testOuterDotThisNotation() { assertScript ''' -class Outer { - int x - class Inner { - int foo() { 2*Outer.this.x } - } - int bar() { - new Inner().foo() - } -} -def o = new Outer(x:123) -assert o.bar() == 2*o.x -''' + class Outer { + int x + class Inner { + int foo() { 2*Outer.this.x } + } + int bar() { + new Inner().foo() + } + } + def o = new Outer(x:123) + assert o.bar() == 2*o.x + ''' } // GROOVY-6965 @@ -716,7 +632,8 @@ assert o.bar() == 2*o.x void testAmbiguousMethodResolutionNoArgsOverload() { shouldFailWithMessages ''' Arrays.sort() - ''', 'Reference to method is ambiguous. Cannot choose between ' + ''', + 'Reference to method is ambiguous. Cannot choose between ' } // GROOVY-7711 @@ -784,6 +701,7 @@ assert o.bar() == 2*o.x assert AbstractSubClass.name == 'AbstractSubClass' ''' + assertScript ''' interface SomeInterface { void foo() } interface SomeOtherInterface { void bar() } @@ -844,6 +762,7 @@ assert o.bar() == 2*o.x } assert new Foo().bar() == ['x'] ''' + assertScript ''' class Foo { def bar() { @@ -856,6 +775,7 @@ assert o.bar() == 2*o.x } assert new Foo().bar() == [[x:1], [y:2]] ''' + assertScript ''' import groovy.transform.* @ToString(includeFields=true) diff --git a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy index cd0daaaf31f..87c70c73c43 100644 --- a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy +++ b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy @@ -18,6 +18,8 @@ */ package groovy.transform.stc +import groovy.test.NotYetImplemented + /** * Unit tests for static type checking : closures. */ @@ -37,11 +39,14 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase { ''' } + // GROOVY-11394 + @NotYetImplemented void testCallClosure3() { - shouldFail MissingMethodException, ''' + shouldFailWithMessages ''' def c = { -> } c("") - ''' + ''', + 'Cannot call closure that accepts [] with [java.lang.String]' } void testCallClosure4() { @@ -125,8 +130,6 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase { ''' } - // - void testClosureReturnTypeInference1() { assertScript ''' def c = { int a, int b -> return a + b } @@ -286,14 +289,14 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase { } // GROOVY-5145 - void testCollect() { + void testCollect1() { assertScript ''' List strings = [1,2,3].collect { it.toString() } ''' } // GROOVY-5145 - void testCollectWithSubclass() { + void testCollect2() { assertScript ''' class StringClosure extends Closure { StringClosure() { super(null,null) } @@ -303,33 +306,91 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase { ''' } - // GROOVY-7701 - void testWithDelegateVsOwnerField() { + void testWithIntReturnType() { assertScript ''' - class Foo { - List type + class Test { + static int a(String s) { + s.toCharArray().with { + length + } + } } + assert Test.a( 'Daniel' ) == 6 + ''' + } - class Bar { - int type = 10 - - @Lazy - List something = { -> - List tmp = [] - def foo = new Foo() - foo.with { - type = ['String'] - // ^^^^ should be Foo.type, not Bar.type + void testWithLongReturnType() { + assertScript ''' + class Test { + static long a() { + Long.with { + MAX_VALUE } - tmp.add(foo) - tmp - }() + } } + assert Test.a() == Long.MAX_VALUE + ''' + } - def bar = new Bar() - assert bar.type == 10 - assert bar.something*.type == [['String']] - assert bar.type == 10 + void testWithStringReturnType() { + assertScript ''' + class Test { + static String a( String s ) { + s.with { String it -> it.toLowerCase() } + } + } + assert Test.a( 'TIM' ) == 'tim' + ''' + } + + // GROOVY-5907 + void testWithGenericReturnType() { + assertScript ''' + class Test { + static List a( String s ) { + s.with { String it -> [ "$it".toString() ] } + } + } + assert Test.a( 'tim' ) == [ 'tim' ] + ''' + } + + void testWithNestedMemberAccess() { + assertScript ''' + class Foo { + String foo = 'foo' + String foom() { 'foom' } + } + class Bar { + String bar = 'bar' + String barm() { 'barm' } + } + class Baz { + String baz = 'baz' + String bazm() { 'bazm' } + } + String other() { 'other' } + new Foo().with { + assert foo == 'foo' + assert foom() == 'foom' + assert other() == 'other' + new Bar().with { + assert foo == 'foo' + assert bar == 'bar' + assert foom() == 'foom' + assert barm() == 'barm' + assert other() == 'other' + new Baz().with { + assert foo == 'foo' + assert bar == 'bar' + assert baz == 'baz' + assert foom() == 'foom' + assert barm() == 'barm' + assert bazm() == 'bazm' + assert other() == 'other' + } + } + } ''' } @@ -350,8 +411,18 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase { 'Cannot find matching method (java.io.Serializable or java.lang.Comparable' } - // GROOVY-9516 + // GROOVY-5874 void testClosureSharedVariable3() { + shouldFailWithMessages ''' + def sum = 0 + def cl1 = { sum = sum + 1 } + def cl2 = { sum = new Date() } + ''', + 'A closure shared variable [sum] has been assigned with various types' + } + + // GROOVY-9516 + void testClosureSharedVariable4() { shouldFailWithMessages ''' class A {} class B extends A { def m() {} } @@ -368,7 +439,7 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase { } // GROOVY-10356 - void testClosureSharedVariable4() { + void testClosureSharedVariable5() { assertScript ''' interface A { void m() @@ -382,7 +453,7 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase { } // GROOVY-10052 - void testClosureSharedVariable5() { + void testClosureSharedVariable6() { assertScript ''' String x def f = { -> @@ -394,7 +465,7 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase { } // GROOVY-10052 - void testClosureSharedVariable6() { + void testClosureSharedVariable7() { assertScript ''' def x def f = { -> @@ -773,6 +844,36 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase { } } + // GROOVY-7701 + void testOwnerVersusDelegate() { + assertScript ''' + class Foo { + List type + } + + class Bar { + int type = 10 + + @Lazy + List something = { -> + List tmp = [] + def foo = new Foo() + foo.with { + type = ['String'] + // ^^^^ should be Foo.type, not Bar.type + } + tmp.add(foo) + tmp + }() + } + + def bar = new Bar() + assert bar.type == 10 + assert bar.something*.type == [['String']] + assert bar.type == 10 + ''' + } + // GROOVY-9089 void testOwnerVersusDelegateFromNestedClosure() { String declarations = ''' @@ -791,7 +892,6 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase { new B().with(block) } ''' - assertScript declarations + ''' outer { inner { @@ -801,7 +901,6 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase { } } ''' - assertScript declarations + ''' outer { inner { @@ -824,9 +923,125 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase { ''' } + // GROOVY-5470, GROOVY-6091, GROOVY-11399 + void testClosureThisObjectDelegateOwnerAccessor() { + for (meth in ['getThisObject()', 'getDelegate()', 'getOwner()']) { + assertScript """ + class C { + void m() { + C that = this; + { -> + @ASTTest(phase=INSTRUCTION_SELECTION, value={ + def type = node.getNodeMetaData(INFERRED_TYPE)?.toString(false) + assert type/*of $meth*/ == 'C' + }) + Object ref = $meth + assert ref == that + }() + } + } + new C().m() + """ + assertScript """ + class C { + static m() { + { -> + @ASTTest(phase=INSTRUCTION_SELECTION, value={ + def type = node.getNodeMetaData(INFERRED_TYPE)?.toString(false) + assert type/*of $meth*/ == 'java.lang.Class' + }) + Object ref = $meth + assert ref == C + }() + } + } + C.m() + """ + } + for (meth in ['getDelegate()', 'getOwner()']) { + assertScript """ + def that + that = { -> + { -> + @ASTTest(phase=INSTRUCTION_SELECTION, value={ + def type = node.getNodeMetaData(INFERRED_TYPE) + assert type/*of $meth*/ == CLOSURE_TYPE + }) + Object ref = $meth + assert ref == that + }() + } + that.call() + """ + } + assertScript ''' + void test(Closure c) { + { -> + @ASTTest(phase=INSTRUCTION_SELECTION, value={ + def type = node.getNodeMetaData(INFERRED_TYPE) + assert type == OBJECT_TYPE + }) + def x = c.getThisObject() + }() + } + test({->}) + ''' + } + + // GROOVY-5470, GROOVY-6091, GROOVY-11399 + void testClosureThisObjectDelegateOwnerProperty() { + for (prop in ['thisObject', 'delegate', 'owner']) { + assertScript """ + class C { + void m() { + C that = this; + { -> + @ASTTest(phase=INSTRUCTION_SELECTION, value={ + def type = node.getNodeMetaData(INFERRED_TYPE)?.toString(false) + assert type/*of $prop*/ == 'C' + }) + Object ref = $prop + assert ref == that + }() + } + } + new C().m() + """ + assertScript """ + class C { + static m() { + { -> + @ASTTest(phase=INSTRUCTION_SELECTION, value={ + def type = node.getNodeMetaData(INFERRED_TYPE)?.toString(false) + assert type/*of $prop*/ == 'java.lang.Class' + }) + Object ref = $prop + assert ref == C + }() + } + } + C.m() + """ + } + assertScript ''' + def that + that = { -> + { -> + @ASTTest(phase=INSTRUCTION_SELECTION, value={ + def type = node.getNodeMetaData(INFERRED_TYPE) + assert type == CLOSURE_TYPE + }) + Object ref = owner + assert ref == that + }() + } + that.call() + ''' + } + // GROOVY-9652 void testDelegatePropertyAndCharCompareOptimization() { - ['String', 'Character', 'char'].each { type -> + for (type in ['String', 'Character', 'char']) { assertScript """ class Node { String name @@ -901,4 +1116,18 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase { assert result == 'foo' ''' } + + // GROOVY-9604 + void testClosureResolveStrategy() { + assertScript ''' + class C { + def m() { + return { -> + resolveStrategy + getResolveStrategy() + }(); + } + } + assert new C().m() == 0 + ''' + } } diff --git a/src/test/groovy/transform/stc/WithSTCStandaloneTest.groovy b/src/test/groovy/transform/stc/WithSTCStandaloneTest.groovy deleted file mode 100644 index 48cba59f4bc..00000000000 --- a/src/test/groovy/transform/stc/WithSTCStandaloneTest.groovy +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package groovy.transform.stc - -import groovy.test.GroovyTestCase - -/** - * Unit tests for static type checking : with method. - */ -class WithSTCStandaloneTest extends GroovyTestCase { - void testMethodAndPropertyAccessWithinNestedWithStatements() { - assertScript ''' - import groovy.transform.CompileStatic - - class Foo { - String foo = 'foo' - String foom() { 'foom' } - } - - class Bar { - String bar = 'bar' - String barm() { 'barm' } - } - - class Baz { - String baz = 'baz' - String bazm() { 'bazm' } - } - - def other() { 'other' } - - @CompileStatic - def main() { - new Foo().with { - assert other() == 'other' - assert foom() == 'foom' - assert foo == 'foo' - new Bar().with { - assert foo == 'foo' - assert bar == 'bar' - assert barm() == 'barm' - assert other() == 'other' - assert foom() == 'foom' - new Baz().with { - assert foo == 'foo' - assert bar == 'bar' - assert baz == 'baz' - assert barm() == 'barm' - assert other() == 'other' - assert foom() == 'foom' - assert bazm() == 'bazm' - } - } - } - } - - main() - ''' - } -} diff --git a/src/test/groovy/transform/stc/WithSTCTest.groovy b/src/test/groovy/transform/stc/WithSTCTest.groovy deleted file mode 100644 index de3c4d156d1..00000000000 --- a/src/test/groovy/transform/stc/WithSTCTest.groovy +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package groovy.transform.stc - -/** - * Unit tests for static type checking : with method. - */ -class WithSTCTest extends StaticTypeCheckingTestCase { - - // GROOVY-5907 - void testGenericReturnType() { - assertScript ''' - class Test { - static List a( String s ) { - s.with { String it -> [ "$it".toString() ] } - } - } - - assert Test.a( 'tim' ) == [ 'tim' ] - ''' - } - - void testStringReturnType() { - assertScript ''' - class Test { - static String a( String s ) { - s.with { String it -> it.toLowerCase() } - } - } - - assert Test.a( 'TIM' ) == 'tim' - ''' - } - - void testIntReturnType() { - assertScript ''' - class Test { - static int a(String s) { - s.toCharArray().with { - length - } - } - } - - assert Test.a( 'Daniel' ) == 6 - ''' - } - - void testLongReturnType() { - assertScript ''' - class Test { - static long a() { - Long.with { - MAX_VALUE - } - } - } - - assert Test.a() == Long.MAX_VALUE - ''' - } -} - diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/WithStaticCompileTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/WithStaticCompileTest.groovy deleted file mode 100644 index 1decdfb35b4..00000000000 --- a/src/test/org/codehaus/groovy/classgen/asm/sc/WithStaticCompileTest.groovy +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.codehaus.groovy.classgen.asm.sc - -import groovy.transform.stc.WithSTCTest - -/** - * Unit tests for static compilation : with method. - */ -class WithStaticCompileTest extends WithSTCTest implements StaticCompilationTestSupport { - -} -