"Fossies" - the Fresh Open Source Software Archive

Member "groovy-3.0.9/src/test/groovy/transform/stc/ClosuresSTCTest.groovy" (3 Sep 2021, 21530 Bytes) of package /linux/misc/apache-groovy-src-3.0.9.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Java source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2  *  Licensed to the Apache Software Foundation (ASF) under one
    3  *  or more contributor license agreements.  See the NOTICE file
    4  *  distributed with this work for additional information
    5  *  regarding copyright ownership.  The ASF licenses this file
    6  *  to you under the Apache License, Version 2.0 (the
    7  *  "License"); you may not use this file except in compliance
    8  *  with the License.  You may obtain a copy of the License at
    9  *
   10  *    http://www.apache.org/licenses/LICENSE-2.0
   11  *
   12  *  Unless required by applicable law or agreed to in writing,
   13  *  software distributed under the License is distributed on an
   14  *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15  *  KIND, either express or implied.  See the License for the
   16  *  specific language governing permissions and limitations
   17  *  under the License.
   18  */
   19 package groovy.transform.stc
   20 
   21 import groovy.test.NotYetImplemented
   22 
   23 /**
   24  * Unit tests for static type checking : closures.
   25  */
   26 class ClosuresSTCTest extends StaticTypeCheckingTestCase {
   27 
   28     void testClosureWithoutArguments() {
   29         assertScript '''
   30         def clos = { println "hello!" }
   31 
   32         println "Executing the Closure:"
   33         clos() //prints "hello!"
   34         '''
   35     }
   36 
   37     // GROOVY-9079: no params to statically type check but shouldn't get NPE
   38     void testClosureWithoutArgumentsExplicit() {
   39         assertScript '''
   40             import java.util.concurrent.Callable
   41 
   42             String makeFoo() {
   43                 Callable<String> call = { -> 'foo' }
   44                 call()
   45             }
   46 
   47             assert makeFoo() == 'foo'
   48         '''
   49     }
   50 
   51     void testClosureWithArguments() {
   52         assertScript '''
   53             def printSum = { int a, int b -> print a+b }
   54             printSum( 5, 7 ) //prints "12"
   55         '''
   56 
   57         shouldFailWithMessages '''
   58             def printSum = { int a, int b -> print a+b }
   59             printSum( '5', '7' ) //prints "12"
   60         ''', 'Closure argument types: [int, int] do not match with parameter types: [java.lang.String, java.lang.String]'
   61     }
   62 
   63     void testClosureWithArgumentsAndNoDef() {
   64         assertScript '''
   65             { int a, int b -> print a+b }(5,7)
   66         '''
   67     }
   68 
   69     void testClosureWithArgumentsNoDefAndWrongType() {
   70         shouldFailWithMessages '''
   71             { int a, int b -> print a+b }('5',7)
   72         ''', 'Closure argument types: [int, int] do not match with parameter types: [java.lang.String, int]'
   73     }
   74 
   75     void testClosureReturnTypeInference1() {
   76         assertScript '''
   77             def closure = { int x, int y -> return x+y }
   78             int total = closure(2,3)
   79         '''
   80     }
   81 
   82     void testClosureReturnTypeInference2() {
   83         shouldFailWithMessages '''
   84             def closure = { int x, int y -> return x+y }
   85             int total = closure('2',3)
   86         ''', 'Closure argument types: [int, int] do not match with parameter types: [java.lang.String, int]'
   87     }
   88 
   89     void testClosureReturnTypeInference3() {
   90         assertScript '''
   91             int total = { int x, int y -> return x+y }(2,3)
   92         '''
   93     }
   94 
   95     void testClosureReturnTypeInference4() {
   96         shouldFailWithMessages '''
   97             def cl = { int x ->
   98                 if (x==0) {
   99                     1L
  100                 } else {
  101                     x // int
  102                 }
  103             }
  104             byte res = cl(0) // should throw an error because return type inference should be a long
  105         ''', 'Possible loss of precision from long to byte'
  106     }
  107 
  108     // GROOVY-9907
  109     void testClosureReturnTypeInference5() {
  110         assertScript '''
  111             Integer foo(x) {
  112                 if (x instanceof Integer) {
  113                     def bar = { -> return x }
  114                     return bar.call()
  115                 }
  116                 return 0
  117             }
  118             assert foo(1) == 1
  119         '''
  120     }
  121 
  122     // GROOVY-8427
  123     void testClosureReturnTypeInference6() {
  124         assertScript '''
  125             import java.util.function.Consumer
  126 
  127             class C {
  128                 static <T> void m(T a, Consumer<T> c) {
  129                     c.accept(a)
  130                 }
  131                 static void main(args) {
  132                     def c = { ->
  133                         int x = 0
  134                         m('') {
  135                             print 'void return'
  136                         }
  137                     }
  138                     c.call()
  139                 }
  140             }
  141         '''
  142     }
  143 
  144     // GROOVY-8202
  145     void testClosureReturnTypeInference7() {
  146         assertScript '''
  147             void proc() {
  148             }
  149             String test0(flag) {
  150               if (flag) {
  151                 'foo'
  152               } else {
  153                 proc()
  154               }
  155             }
  156             String test1(flag) {
  157               Closure<String> c = { ->
  158                 if (flag) {
  159                   'bar'
  160                 } else {
  161                   proc()
  162                   null
  163                 }
  164               }
  165               c.call()
  166             }
  167             String test2(flag) {
  168               Closure<String> c = { -> // Cannot assign Closure<Object> to Closure<String>
  169                 if (flag) {
  170                   'baz'
  171                 } else {
  172                   proc()
  173                 }
  174               }
  175               c.call()
  176             }
  177 
  178             assert test0(true) == 'foo'
  179             assert test1(true) == 'bar'
  180             assert test2(true) == 'baz'
  181             assert test0(false) == null
  182             assert test1(false) == null
  183             assert test2(false) == null
  184         '''
  185 
  186         assertScript '''
  187             Closure<Void> c = { flag ->
  188                 if (flag) {
  189                     print 'true'
  190                 } else {
  191                     print 'false'
  192                 }
  193             }
  194         '''
  195     }
  196 
  197     // GROOVY-9971
  198     void testClosureReturnTypeInference8() {
  199         assertScript '''
  200             def m(Closure<String> c) {
  201                 c.call()
  202             }
  203             final x = 123
  204             Closure<String> c = { -> "x=$x" }
  205             String type = c.call().class.name
  206             assert type == 'java.lang.String'
  207             type = (m { -> "x=$x" }).class.name
  208             assert type == 'java.lang.String' // not GStringImpl
  209         '''
  210     }
  211 
  212     // GROOVY-5145
  213     void testCollect() {
  214         assertScript '''
  215             List<String> strings = [1,2,3].collect { it.toString() }
  216         '''
  217     }
  218 
  219     // GROOVY-5145
  220     void testCollectWithSubclass() {
  221         assertScript '''
  222             class StringClosure extends Closure<String> {
  223                 StringClosure() { super(null,null) }
  224                 void doCall(int x) { x }
  225             }
  226             List<String> strings = [1,2,3].collect(new StringClosure())
  227         '''
  228     }
  229 
  230     // GROOVY-7701
  231     void testWithDelegateVsOwnerField() {
  232         assertScript '''
  233             class Foo {
  234                 List type
  235             }
  236 
  237             class Bar {
  238                 int type = 10
  239 
  240                 @Lazy
  241                 List<Foo> something = { ->
  242                     List<Foo> tmp = []
  243                     def foo = new Foo()
  244                     foo.with {
  245                         type = ['String']
  246                     //  ^^^^ should be Foo.type, not Bar.type
  247                     }
  248                     tmp.add(foo)
  249                     tmp
  250                 }()
  251             }
  252 
  253             def bar = new Bar()
  254             assert bar.type == 10
  255             assert bar.something*.type == [['String']]
  256             assert bar.type == 10
  257         '''
  258     }
  259 
  260     void testClosureSharedVariable1() {
  261         assertScript '''
  262             def x = '123';
  263             { -> x = new StringBuffer() }
  264             x.charAt(0) // available in String and StringBuffer
  265         '''
  266     }
  267 
  268     void testClosureSharedVariable2() {
  269         shouldFailWithMessages '''
  270             def x = '123';
  271             { -> x = 123 }
  272             x.charAt(0) // available in String but not available in Integer
  273         ''', 'Cannot find matching method java.io.Serializable or java.lang.Comparable'
  274     }
  275 
  276     // GROOVY-9516
  277     void testClosureSharedVariable3() {
  278         shouldFailWithMessages '''
  279             class A {}
  280             class B extends A { def m() {} }
  281             class C extends A {}
  282 
  283             void test() {
  284               def x = new B();
  285               { -> x = new C() }();
  286               def c = x
  287               c.m()
  288             }
  289         ''', 'Cannot find matching method A#m()'
  290     }
  291 
  292     // GROOVY-10052
  293     void testClosureSharedVariable4() {
  294         assertScript '''
  295             String x
  296             def f = { ->
  297                 x = Optional.of('x').orElseThrow{ new Exception() }
  298             }
  299             assert f() == 'x'
  300             assert x == 'x'
  301         '''
  302     }
  303 
  304     @NotYetImplemented // GROOVY-10052
  305     void testClosureSharedVariable5() {
  306         assertScript '''
  307             def x
  308             def f = { ->
  309                 x = Optional.of('x').orElseThrow{ new Exception() }
  310             }
  311             assert f() == 'x'
  312             assert x == 'x'
  313         '''
  314     }
  315 
  316     // GROOVY-10052
  317     void testNotClosureSharedVariable() {
  318         assertScript '''
  319             String x = Optional.of('x').orElseThrow{ new Exception() }
  320             def f = { ->
  321                 String y = Optional.of('y').orElseThrow{ new Exception() }
  322             }
  323 
  324             assert x == 'x'
  325             assert f() == 'y'
  326         '''
  327     }
  328 
  329     void testClosureCallAsAMethod() {
  330         assertScript '''
  331             Closure cl = { 'foo' }
  332             assert cl() == 'foo'
  333         '''
  334     }
  335 
  336     void testClosureCallWithOneArgAsAMethod() {
  337         assertScript '''
  338             Closure cl = { int x -> "foo$x" }
  339             assert cl(1) == 'foo1'
  340         '''
  341     }
  342 
  343     void testRecurseClosureCallAsAMethod() {
  344         assertScript '''
  345             Closure<Integer> cl
  346             cl = { int x-> x==0?x:1+cl(x-1) }
  347         '''
  348     }
  349 
  350     void testFibClosureCallAsAMethod() {
  351         assertScript '''
  352             Closure<Integer> fib
  353             fib = { int x-> x<1?x:fib(x-1)+fib(x-2) }
  354             fib(2)
  355         '''
  356     }
  357 
  358     void testFibClosureCallAsAMethodFromWithinClass() {
  359         assertScript '''
  360             class FibUtil {
  361                 private Closure<Integer> fibo
  362                 FibUtil() {
  363                     fibo = { int x-> x<1?x:fibo(x-1)+fibo(x-2) }
  364                 }
  365 
  366                 int fib(int n) { fibo(n) }
  367             }
  368             FibUtil fib = new FibUtil()
  369             fib.fib(2)
  370         '''
  371     }
  372 
  373     void testClosureRecursionWithoutClosureTypeArgument() {
  374         shouldFailWithMessages '''
  375             Closure fib
  376             fib = { int n -> n<2?n:fib(n-1)+fib(n-2) }
  377         ''', 'Cannot find matching method java.lang.Object#plus(java.lang.Object)'
  378     }
  379 
  380     void testClosureRecursionWithDef() {
  381         shouldFailWithMessages '''
  382             def fib
  383             fib = { int n -> n<2?n:fib(n-1)+fib(n-2) }
  384         ''',
  385                 'Cannot find matching method java.lang.Object#plus(java.lang.Object)',
  386                 'Cannot find matching method java.lang.Object#call(int)',
  387                 'Cannot find matching method java.lang.Object#call(int)'
  388     }
  389 
  390     void testClosureRecursionWithClosureTypeArgument() {
  391         assertScript '''
  392             Closure<Integer> fib
  393             fib = { int n -> n<2?n:fib(n-1)+fib(n-2) }
  394         '''
  395     }
  396 
  397     void testClosureMemoizeWithClosureTypeArgument() {
  398         assertScript '''
  399             Closure<Integer> fib
  400             fib = { int n -> n<2?n:fib(n-1)+fib(n-2) }
  401             def memoized = fib.memoizeAtMost(2)
  402             assert fib(5) == memoized(5)
  403         '''
  404     }
  405 
  406     // GROOVY-5639
  407     void testShouldNotThrowClosureSharedVariableError() {
  408         assertScript '''
  409         Closure<Void> c = {
  410             List<String> list = new ArrayList<String>()
  411             String s = "foo"
  412             10.times {
  413                 list.add(s)
  414             }
  415         }
  416         '''
  417     }
  418 
  419     // a case in Grails
  420     void testShouldNotThrowClosureSharedVariableError2() {
  421         assertScript '''
  422             class AntPathMatcher {
  423                 boolean match(String x, String y) { true }
  424             }
  425             private String relativePath() { '' }
  426             def foo() {
  427                 AntPathMatcher pathMatcher = new AntPathMatcher()
  428                 def relPath = relativePath()
  429                 def cl = { String it ->
  430                     pathMatcher.match(it, relPath)
  431                 }
  432                 cl('foo')
  433             }
  434 
  435             foo()
  436         '''
  437     }
  438 
  439     // GROOVY-5693
  440     void testClosureArgumentCheckWithFlowTyping() {
  441         assertScript '''
  442             Closure a = {
  443                 int i ->
  444                 println "First closure "+ i
  445             }
  446             Closure b = {
  447                 String s ->
  448                 println "Second closure "+ s
  449             }
  450             a(5)
  451             Closure c = a
  452             a=b
  453             a("Testing!")
  454             a = c
  455             a(5)
  456             a=a
  457             a(5)
  458             a = b
  459             a('Testing!')
  460         '''
  461     }
  462 
  463     // GROOVY-5705
  464     void testNPEWhenCallingClosureFromAField() {
  465         assertScript '''
  466             import groovy.transform.*
  467 
  468             class Test {
  469                 Closure c = { it }
  470 
  471                 @TypeChecked
  472                 void test() {
  473                     c("123")
  474                 }
  475             }
  476 
  477             new Test().test()
  478         '''
  479     }
  480 
  481     // GROOVY-6219
  482     void testShouldFailBecauseClosureReturnTypeDoesnMatchMethodSignature() {
  483         shouldFailWithMessages '''
  484             void printMessage(Closure<String> messageProvider) {
  485                 println "Received message : ${messageProvider()}"
  486             }
  487 
  488             void testMessage() {
  489                 printMessage { int x, int y -> x+y }
  490             }
  491         ''', 'Cannot find matching method'
  492     }
  493 
  494     // GROOVY-6189
  495     void testSAMsInMethodSelection() {
  496         // simple direct case
  497         assertScript """
  498             interface MySAM {
  499                 def someMethod()
  500             }
  501             def foo(MySAM sam) {sam.someMethod()}
  502             assert foo {1} == 1
  503         """
  504 
  505         // overloads with classes implemented by Closure
  506         ["java.util.concurrent.Callable", "Object", "Closure", "GroovyObjectSupport", "Cloneable", "Runnable", "GroovyCallable", "Serializable", "GroovyObject"].each {
  507             className ->
  508             assertScript """
  509                 interface MySAM {
  510                     def someMethod()
  511                 }
  512                 def foo(MySAM sam) {sam.someMethod()}
  513                 def foo($className x) {2}
  514                 assert foo {1} == 2
  515             """
  516         }
  517     }
  518 
  519     void testSAMVariable() {
  520         assertScript """
  521             interface SAM { def foo(); }
  522 
  523             @ASTTest(phase=INSTRUCTION_SELECTION, value={
  524                 assert node.getNodeMetaData(INFERRED_TYPE).name == 'SAM'
  525             })
  526             SAM s = {1}
  527             assert s.foo() == 1
  528             def t = (SAM) {2}
  529             assert t.foo() == 2
  530         """
  531     }
  532 
  533     // GROOVY-7927
  534     void testSAMGenericsInAssignment() {
  535         assertScript """
  536             interface SAM<T,R> { R accept(T t); }
  537             SAM<Integer,Integer> s = { Integer n -> -n }
  538             assert s.accept(1) == -1
  539         """
  540     }
  541 
  542     void testSAMProperty() {
  543         assertScript """
  544             interface SAM { def foo(); }
  545             class X {
  546                 SAM s
  547             }
  548             def x = new X(s:{1})
  549             assert x.s.foo() == 1
  550         """
  551     }
  552 
  553     void testSAMAttribute() {
  554         assertScript """
  555             interface SAM { def foo(); }
  556             class X {
  557                 public SAM s
  558             }
  559             def x = new X()
  560             x.s = {1}
  561             assert x.s.foo() == 1
  562             x = new X()
  563             x.@s = {2}
  564             assert x.s.foo() == 2
  565         """
  566     }
  567 
  568     void testMultipleSAMSignature() {
  569         assertScript '''
  570             interface SAM { def foo() }
  571             def method(SAM a, SAM b) {
  572                 a.foo()
  573                 b.foo()
  574             }
  575             method({println 'a'}, {println 'b'})
  576         '''
  577     }
  578 
  579     void testMultipleSAMSignature2() {
  580         assertScript '''
  581             interface SAM { def foo() }
  582             def method(Object o, SAM a, SAM b) {
  583                 a.foo()
  584                 b.foo()
  585             }
  586             method(new Object(), {println 'a'}, {println 'b'})
  587         '''
  588     }
  589 
  590     void testMultipleSAMMethodWithClosure() {
  591         assertScript '''
  592             interface SAM { def foo() }
  593             def method(SAM a, SAM b) {
  594                 a.foo()
  595                 b.foo()
  596             }
  597             def method(Closure a, SAM b) {
  598                 b.foo()
  599             }
  600             def called = false
  601             method({called = true;println 'a'}, {println 'b'})
  602             assert !called
  603         '''
  604     }
  605 
  606     void testMultipleSAMMethodWithClosureInverted() {
  607         assertScript '''
  608             interface SAM { def foo() }
  609             def method(SAM a, SAM b) {
  610                 a.foo()
  611                 b.foo()
  612             }
  613             def method(SAM a, Closure b) {
  614                 a.foo()
  615             }
  616             def called = false
  617             method({println 'a'}, {called=true;println 'b'})
  618             assert !called
  619         '''
  620     }
  621 
  622     void testAmbiguousSAMOverload() {
  623         shouldFailWithMessages '''
  624             interface Sammy { def sammy() }
  625             interface Sam { def sam() }
  626             def method(Sam sam) { sam.sam() }
  627             def method(Sammy sammy) { sammy.sammy() }
  628             method {
  629                 println 'foo'
  630             }
  631         ''', 'Reference to method is ambiguous. Cannot choose between'
  632     }
  633 
  634     void testSAMType() {
  635         assertScript """
  636             interface Foo {int foo()}
  637             Foo f = {1}
  638             assert f.foo() == 1
  639             abstract class Bar implements Foo {}
  640             Bar b = {2}
  641             assert b.foo() == 2
  642         """
  643         shouldFailWithMessages """
  644             interface Foo2 {
  645                 String toString()
  646             }
  647             Foo2 f2 = {int i->"hi"}
  648         """, "Cannot assign"
  649         shouldFailWithMessages """
  650             interface Foo2 {
  651                 String toString()
  652             }
  653             abstract class Bar2 implements Foo2 {}
  654             Bar2 b2 = {"there"}
  655         """, "Cannot assign"
  656         assertScript """
  657             interface Foo3 {
  658                 boolean equals(Object)
  659                 int f()
  660             }
  661             Foo3 f3 = {1}
  662             assert f3.f() == 1
  663         """
  664         shouldFailWithMessages """
  665             interface Foo3 {
  666                 boolean equals(Object)
  667                 int f()
  668             }
  669             abstract class Bar3 implements Foo3 {
  670                 int f(){2}
  671             }
  672             Bar3 b3 = {2}
  673         """, "Cannot assign"
  674     }
  675 
  676     // GROOVY-6238
  677     void testDirectMethodCallOnClosureExpression() {
  678         assertScript '''
  679             @ASTTest(phase=INSTRUCTION_SELECTION,value={
  680                 def dit = node.getNodeMetaData(INFERRED_TYPE)
  681                 def irt = node.rightExpression.getNodeMetaData(INFERRED_TYPE)
  682                 assert irt == CLOSURE_TYPE
  683                 assert dit == CLOSURE_TYPE
  684             })
  685             def cl = { it }.curry(42)
  686             def val = cl.call()
  687             assert val == 42
  688         '''
  689     }
  690 
  691     // GROOVY-6343
  692     void testAccessStaticFieldFromNestedClosures() {
  693         assertScript '''
  694             class A {
  695 
  696               public static final CONST = "a"
  697 
  698               public static List doSomething() {
  699                 return (0..1).collect{ int x ->
  700                   (0..1).collect{ int y ->
  701                     return CONST
  702                   }
  703                 }
  704               }
  705             }
  706             A.doSomething()
  707         '''
  708     }
  709 
  710     void testParameterlessClosureToSAMTypeArgumentCoercion() {
  711         assertScript '''
  712             interface SamType {
  713                 int sam()
  714             }
  715 
  716             int foo(SamType samt) {
  717                 samt.sam()
  718             }
  719 
  720             assert foo { -> 1 }  == 1
  721         '''
  722     }
  723 
  724     // GROOVY-9558
  725     void testPutAtClosureDelegateProperty() {
  726         assertScript '''
  727             def config = new org.codehaus.groovy.control.CompilerConfiguration()
  728             config.tap {
  729                 optimizationOptions['indy'] = true
  730                 optimizationOptions.indy = true
  731             }
  732         '''
  733     }
  734 
  735     // GROOVY-9652
  736     void testDelegatePropertyAndCharCompareOptimization() {
  737         ['String', 'Character', 'char'].each { type ->
  738             assertScript """
  739                 class Node {
  740                     String name
  741                     ${type} text
  742                 }
  743                 class Root implements Iterable<Node> {
  744                     @Override
  745                     Iterator<Node> iterator() {
  746                         return [
  747                             new Node(name: 'term', text: (${type}) 'a'),
  748                             new Node(name: 'dash', text: (${type}) '-'),
  749                             new Node(name: 'term', text: (${type}) 'b')
  750                         ].iterator()
  751                     }
  752                 }
  753 
  754                 void test() {
  755                     Root root = new Root()
  756                     root[0].with {
  757                         assert name == 'term'
  758                         assert text == 'a' // GroovyCastException: Cannot cast object 'script@b91d8c4' with class 'script' to class 'bugs.Node'
  759                     }
  760                 }
  761 
  762                 test()
  763             """
  764         }
  765     }
  766 }