New J2K: Don't fail on captured types & don't use functional interface with captured types

Note: we have to print captured types for lambda parameters.
It is needed for nullability inference as it requires explicit types specification to work.
Later in post-processing we remove explicit type specifications for lambda parameters which have captured types

#KT-33186 fixed
This commit is contained in:
Ilya Kirillov
2019-08-09 12:46:50 +03:00
parent 0c3c0989b4
commit 09ab98a4f8
17 changed files with 180 additions and 14 deletions
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isSignedOrUnsignedNumberType
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
@@ -56,6 +57,13 @@ val fixTypeMismatchDiagnosticBasedProcessing =
val fix = NumberConversionFix(element, expectedType, disableIfAvailable = null)
fix.invoke(element.project, null, element.containingFile)
}
element is KtLambdaExpression
&& expectedType.isNothing() -> {
for (valueParameter in element.valueParameters) {
valueParameter.typeReference?.delete()
valueParameter.colon?.delete()
}
}
}
}
@@ -360,9 +360,13 @@ class JavaToJKTreeBuilder constructor(
}
fun PsiFunctionalExpression.functionalType(): JKTypeElement =
functionalInterfaceType?.toJK(symbolProvider)?.takeUnless { type ->
type.isKotlinFunctionType
}?.asTypeElement() ?: JKTypeElementImpl(JKNoTypeImpl)
functionalInterfaceType
?.takeUnless { type ->
type.safeAs<PsiClassType>()?.parameters?.any { it is PsiCapturedWildcardType } == true
}?.toJK(symbolProvider)
?.takeUnless { type ->
type.isKotlinFunctionType
}?.asTypeElement() ?: JKTypeElementImpl(JKNoTypeImpl)
fun PsiMethodReferenceExpression.toJK(): JKMethodReferenceExpression {
val symbol = symbolProvider.provideSymbolForReference<JKSymbol>(this).let { symbol ->
@@ -935,10 +935,25 @@ private class JKPrinter(
private fun JKClassSymbol.needFqName(): Boolean =
fqName !in mappedToKotlinFqNames
private fun JKType.renderTypeInfo() {
print(elementInfoStorage.getOrCreateInfoForElement(this).render())
}
fun renderType(type: JKType, owner: JKTreeElement?) {
if (type is JKNoTypeImpl) return
print(elementInfoStorage.getOrCreateInfoForElement(type).render())
if (type is JKCapturedType) {
when (val wildcard = type.wildcardType) {
is JKVarianceTypeParameterType -> {
renderType(wildcard.boundType, owner)
}
is JKStarProjectionType -> {
type.renderTypeInfo()
print("Any?")
}
}
return
}
type.renderTypeInfo()
when (type) {
is JKClassType -> {
renderClassSymbol(type.classReference, owner)
@@ -962,12 +977,11 @@ private class JKPrinter(
renderList(type.parameters, renderElement = { renderType(it, null) })
}
}
if (type.nullability == Nullability.Nullable) {
printWithNoIndent("?")
}
// we print undefined types as nullable because we need smartcast work in nullability inference in post-processing
if (type.nullability == Nullability.Default
&& owner?.safeAs<JKLambdaExpression>()?.functionalType?.type != type
// we print undefined types as nullable because we need smartcast to work in nullability inference in post-processing
if (type !is JKWildCardType
&& (type.nullability == Nullability.Default
&& owner?.safeAs<JKLambdaExpression>()?.functionalType?.type != type
|| type.nullability == Nullability.Nullable)
) {
printWithNoIndent("?")
}
@@ -12,7 +12,9 @@ import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.nj2k.*
import org.jetbrains.kotlin.nj2k.symbols.JKUniverseMethodSymbol
import org.jetbrains.kotlin.nj2k.tree.*
import org.jetbrains.kotlin.nj2k.tree.impl.JKCapturedType
import org.jetbrains.kotlin.nj2k.tree.impl.psi
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class AddElementsInfoConversion(private val context: NewJ2kConverterContext) : RecursiveApplicableConversionBase() {
override fun applyToElement(element: JKTreeElement): JKTreeElement {
@@ -26,7 +28,9 @@ class AddElementsInfoConversion(private val context: NewJ2kConverterContext) : R
private fun addInfoForTypeElement(typeElement: JKTypeElement) {
typeElement.type.forAllInnerTypes { type ->
if (type.nullability == Nullability.Default) {
if (type.nullability == Nullability.Default
|| type.safeAs<JKCapturedType>()?.wildcardType is JKStarProjectionType
) {
context.elementsInfoStorage.addEntry(type, UnknownNullability)
}
}
@@ -113,6 +113,12 @@ class TypeMappingConversion(val context: NewJ2kConverterContext) : RecursiveAppl
variance,
boundType.mapType(null)
)
is JKCapturedType -> {
JKCapturedType(
wildcardType.mapType(null) as JKWildCardType,
nullability
)
}
else -> this
}.fixRawType(typeElement)
@@ -490,6 +490,11 @@ data class JKTypeParameterTypeImpl(
override val nullability: Nullability = Nullability.Default
) : JKTypeParameterType
data class JKCapturedType(
val wildcardType: JKWildCardType,
override val nullability: Nullability = Nullability.Default
) : JKType
class JKEnumConstantImpl(
name: JKNameIdentifier,
arguments: JKArgumentList,
@@ -61,11 +61,13 @@ interface JKType {
fun JKType.isNullable(): Boolean =
nullability != Nullability.NotNull
interface JKVarianceTypeParameterType : JKType {
interface JKWildCardType : JKType
interface JKVarianceTypeParameterType : JKWildCardType {
val variance: Variance
val boundType: JKType
override val nullability: Nullability
get() = Nullability.NotNull
get() = Nullability.Default
enum class Variance {
IN, OUT
@@ -97,7 +99,7 @@ interface JKJavaArrayType : JKType {
val type: JKType
}
interface JKStarProjectionType : JKType {
interface JKStarProjectionType : JKWildCardType {
override val nullability: Nullability
get() = Nullability.NotNull
}
@@ -121,6 +121,11 @@ fun PsiType.toJK(symbolProvider: JKSymbolProvider, nullability: Nullability = Nu
)
else -> JKStarProjectionTypeImpl()
}
is PsiCapturedWildcardType ->
JKCapturedType(
wildcard.toJK(symbolProvider, nullability) as JKWildCardType,
nullability
)
else -> throw Exception("Invalid PSI ${this::class.java}")
}
}
@@ -0,0 +1,13 @@
// RUNTIME_WITH_FULL_JDK
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
public class Test {
private void addPackages(Collection<? extends Number> packages) {
List<Number> sorted = packages.stream()
.filter(repoPackage -> repoPackage != null)
.collect(Collectors.toList());
}
}
@@ -0,0 +1,9 @@
import java.util.stream.Collectors
class Test {
private fun addPackages(packages: Collection<Number>) {
val sorted: List<Number> = packages.stream()
.filter { repoPackage: Number? -> repoPackage != null }
.collect(Collectors.toList())
}
}
@@ -0,0 +1,8 @@
public interface SamFace<T> {
void samMethod(T p);
}
public class SamAcceptor<T> {
public void acceptSam(SamFace<T> sam) {
}
}
@@ -0,0 +1,13 @@
public class Test {
public void useSamUpper(SamAcceptor<? extends Number> acceptor) {
acceptor.acceptSam(p -> {});
}
public void useSamLower(SamAcceptor<? super Number> acceptor) {
acceptor.acceptSam(p -> {});
}
public void useSam(SamAcceptor<?> acceptor) {
acceptor.acceptSam(p -> {});
}
}
@@ -0,0 +1,13 @@
class Test {
fun useSamUpper(acceptor: SamAcceptor<out Number?>) {
acceptor.acceptSam { p: Number? -> }
}
fun useSamLower(acceptor: SamAcceptor<in Number?>) {
acceptor.acceptSam { p -> }
}
fun useSam(acceptor: SamAcceptor<*>) {
acceptor.acceptSam { p: Any? -> }
}
}
@@ -0,0 +1,8 @@
public interface SamFace<T> {
void samMethod(int p);
}
public class SamAcceptor<T> {
public void acceptSam(SamFace<T> sam) {
}
}
@@ -0,0 +1,13 @@
public class Test {
public void useSamUpper(SamAcceptor<? extends Number> acceptor) {
acceptor.acceptSam(p -> {});
}
public void useSamLower(SamAcceptor<? super Number> acceptor) {
acceptor.acceptSam(p -> {});
}
public void useSam(SamAcceptor<?> acceptor) {
acceptor.acceptSam(p -> {});
}
}
@@ -0,0 +1,13 @@
class Test {
fun useSamUpper(acceptor: SamAcceptor<out Number?>) {
acceptor.acceptSam { p: Int -> }
}
fun useSamLower(acceptor: SamAcceptor<in Number?>) {
acceptor.acceptSam { p: Int -> }
}
fun useSam(acceptor: SamAcceptor<*>) {
acceptor.acceptSam { p: Int -> }
}
}
@@ -5001,6 +5001,34 @@ public class NewJavaToKotlinConverterSingleFileTestGenerated extends AbstractNew
}
}
@TestMetadata("nj2k/testData/newJ2k/types")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Types extends AbstractNewJavaToKotlinConverterSingleFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInTypes() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("nj2k/testData/newJ2k/types"), Pattern.compile("^([^\\.]+)\\.java$"), TargetBackend.ANY, true);
}
@TestMetadata("capturedTypeInStreamsLambda.java")
public void testCapturedTypeInStreamsLambda() throws Exception {
runTest("nj2k/testData/newJ2k/types/capturedTypeInStreamsLambda.java");
}
@TestMetadata("capturedWildcardTypeAsLambdaParameter.java")
public void testCapturedWildcardTypeAsLambdaParameter() throws Exception {
runTest("nj2k/testData/newJ2k/types/capturedWildcardTypeAsLambdaParameter.java");
}
@TestMetadata("unusedCapturedWildcardTypeInSAM.java")
public void testUnusedCapturedWildcardTypeInSAM() throws Exception {
runTest("nj2k/testData/newJ2k/types/unusedCapturedWildcardTypeInSAM.java");
}
}
@TestMetadata("nj2k/testData/newJ2k/varArg")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)