IDEA-118222 Closure to SAM interface coercion doesn't handle contravariant types correctly

This commit is contained in:
Max Medvedev
2014-02-10 16:52:48 +04:00
parent 1c724d9d0e
commit cb73b8d2d2
7 changed files with 154 additions and 33 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -55,6 +55,7 @@ public abstract class GroovyConfigUtils extends AbstractConfigUtils {
public static final String GROOVY2_0 = "2.0";
public static final String GROOVY2_1 = "2.1";
public static final String GROOVY2_2 = "2.2";
public static final String GROOVY2_3 = "2.3";
private static GroovyConfigUtils myGroovyConfigUtils;
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +15,12 @@
*/
package org.jetbrains.plugins.groovy.lang.psi.typeEnhancers;
import com.intellij.psi.PsiSubstitutor;
import com.intellij.psi.PsiType;
import com.intellij.psi.PsiWildcardType;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.config.GroovyConfigUtils;
import org.jetbrains.plugins.groovy.gpp.GppClosureParameterTypeProvider;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrSafeCastExpression;
@@ -63,7 +66,13 @@ public class ClosureAsAnonymousParameterEnhancer extends AbstractClosureParamete
for (PsiType constraint : expectedTypes) {
final PsiType suggestion = GppClosureParameterTypeProvider.getSingleMethodParameterType(constraint, index, closure);
if (suggestion != null) {
return suggestion;
if (GroovyConfigUtils.getInstance().isVersionAtLeast(closure, GroovyConfigUtils.GROOVY2_3)) {
if (suggestion instanceof PsiWildcardType && ((PsiWildcardType)suggestion).isSuper()) {
return ((PsiWildcardType)suggestion).getBound();
}
}
return TypesUtil.substituteBoxAndNormalizeType(suggestion, PsiSubstitutor.EMPTY, null, closure);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,8 +24,7 @@ import com.intellij.openapi.vfs.JarFileSystem
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor
import static org.jetbrains.plugins.groovy.util.TestUtils.getMockGroovy2_1LibraryName
import static org.jetbrains.plugins.groovy.util.TestUtils.getMockGroovy2_2LibraryName
import static org.jetbrains.plugins.groovy.util.TestUtils.*
/**
* @author Max Medvedev
@@ -33,6 +32,7 @@ import static org.jetbrains.plugins.groovy.util.TestUtils.getMockGroovy2_2Librar
class GroovyLightProjectDescriptor extends DefaultLightProjectDescriptor {
public static final GroovyLightProjectDescriptor GROOVY_2_1 = new GroovyLightProjectDescriptor(mockGroovy2_1LibraryName)
public static final GroovyLightProjectDescriptor GROOVY_2_2 = new GroovyLightProjectDescriptor(mockGroovy2_2LibraryName)
public static final GroovyLightProjectDescriptor GROOVY_2_3 = new GroovyLightProjectDescriptor(mockGroovy2_3LibraryName)
private final String myLibPath
@@ -0,0 +1,75 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed 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.jetbrains.plugins.groovy.lang.resolve
import com.intellij.testFramework.LightProjectDescriptor
import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor
/**
* Created by Max Medvedev on 10/02/14
*/
class TypeInference2_3Test extends TypeInferenceTestBase {
@Override
protected LightProjectDescriptor getProjectDescriptor() {
return GroovyLightProjectDescriptor.GROOVY_2_3
}
public void testContravariantType() throws Exception {
doTest('''\
import groovy.transform.CompileStatic
import java.util.concurrent.Callable
@CompileStatic
class TestCase {
interface Action<T> {
void execute(T thing)
}
static class Wrapper<T> {
private final T thing
Wrapper(T thing) {
this.thing = thing
}
void contravariantTake(Action<? super T> action) {
action.execute(thing)
}
}
static <T> Wrapper<T> wrap(Callable<T> callable) {
new Wrapper(callable.call())
}
static Integer dub(Integer integer) {
integer * 2
}
static void main(String[] args) {
wrap {
1
} contravariantTake {
dub(i<caret>t) // fails static compile, 'it' is not known to be Integer
}
}
}
''', 'java.lang.Integer')
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,31 +18,20 @@ package org.jetbrains.plugins.groovy.lang.resolve
import com.intellij.psi.PsiIntersectionType
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiType
import org.intellij.lang.annotations.Language
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression
import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType
import org.jetbrains.plugins.groovy.util.TestUtils
import static com.intellij.psi.CommonClassNames.*
/**
* @author ven
*/
public class TypeInferenceTest extends GroovyResolveTestCase {
final String basePath = TestUtils.testDataPath + "resolve/inference/"
@Override
protected void setUp() {
super.setUp()
myFixture.addClass("package java.math; public class BigDecimal extends Number implements Comparable<BigDecimal> {}");
}
public class TypeInferenceTest extends TypeInferenceTestBase {
public void testTryFinallyFlow() {
GrReferenceExpression ref = (GrReferenceExpression)configureByFile("tryFinallyFlow/A.groovy").element;
@@ -707,17 +696,4 @@ class Any {
doExprTest('1f/2.4', 'java.lang.Double')
}
private void doTest(@Language("Groovy") String text, String type) {
def file = myFixture.configureByText('_.groovy', text)
def ref = file.findReferenceAt(myFixture.editor.caretModel.offset) as GrReferenceExpression
def actual = ref.type
assertType(type, actual)
}
private void doExprTest(@Language("Groovy") String text, String expectedType) {
GroovyFile file = myFixture.configureByText('_.groovy', text) as GroovyFile
GrStatement lastStatement = file.statements.last()
assertInstanceOf lastStatement, GrExpression
assertType(expectedType, (lastStatement as GrExpression).type)
}
}
@@ -0,0 +1,51 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed 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.jetbrains.plugins.groovy.lang.resolve
import org.intellij.lang.annotations.Language
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression
import org.jetbrains.plugins.groovy.util.TestUtils
/**
* Created by Max Medvedev on 10/02/14
*/
abstract class TypeInferenceTestBase extends GroovyResolveTestCase {
final String basePath = TestUtils.testDataPath + "resolve/inference/"
@Override
protected void setUp() {
super.setUp()
myFixture.addClass("package java.math; public class BigDecimal extends Number implements Comparable<BigDecimal> {}");
}
protected void doTest(@Language("Groovy") String text, String type) {
def file = myFixture.configureByText('_.groovy', text)
def ref = file.findReferenceAt(myFixture.editor.caretModel.offset) as GrReferenceExpression
def actual = ref.type
assertType(type, actual)
}
protected void doExprTest(@Language("Groovy") String text, String expectedType) {
GroovyFile file = myFixture.configureByText('_.groovy', text) as GroovyFile
GrStatement lastStatement = file.statements.last()
assertInstanceOf lastStatement, GrExpression
assertType(expectedType, (lastStatement as GrExpression).type)
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,6 +59,7 @@ public abstract class TestUtils {
public static final String GROOVY_JAR_18 = "groovy-1.8.0-beta-2.jar";
public static final String GROOVY_JAR_21 = "groovy-all-2.1.3.jar";
public static final String GROOVY_JAR_22 = "groovy-all-2.2.0-beta-1.jar";
public static final String GROOVY_JAR_23 = "groovy-all-2.3.0.jar";
public static String getMockJdkHome() {
return getAbsoluteTestDataPath() + "/mockJDK";
@@ -92,6 +93,10 @@ public abstract class TestUtils {
return getAbsoluteTestDataPath() + "/mockGroovyLib2.2";
}
private static String getMockGroovy2_3LibraryHome() {
return getAbsoluteTestDataPath() + "/mockGroovyLib2.3";
}
public static String getMockGroovy1_8LibraryName() {
return getMockGroovy1_8LibraryHome()+"/"+GROOVY_JAR_18;
}
@@ -104,6 +109,10 @@ public abstract class TestUtils {
return getMockGroovy2_2LibraryHome() + "/" + GROOVY_JAR_22;
}
public static String getMockGroovy2_3LibraryName() {
return getMockGroovy2_3LibraryHome() + "/" + GROOVY_JAR_23;
}
public static PsiFile createPseudoPhysicalGroovyFile(final Project project, final String text) throws IncorrectOperationException {
return createPseudoPhysicalFile(project, TEMP_FILE, text);
}