From 09ab98a4f8dc3efdbf1a358184f2787bc43e6f4e Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Fri, 9 Aug 2019 12:46:50 +0300 Subject: [PATCH] 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 --- .../processings/diagnosticBasedProcessings.kt | 8 ++++++ .../kotlin/nj2k/JavaToJKTreeBuilder.kt | 10 +++++-- .../jetbrains/kotlin/nj2k/NewCodeBuilder.kt | 28 ++++++++++++++----- .../conversions/AddElementsInfoConversion.kt | 6 +++- .../nj2k/conversions/TypeMappingConversion.kt | 6 ++++ .../org/jetbrains/kotlin/nj2k/tree/impl/jk.kt | 5 ++++ .../org/jetbrains/kotlin/nj2k/tree/markers.kt | 8 ++++-- .../org/jetbrains/kotlin/nj2k/tree/types.kt | 5 ++++ .../types/capturedTypeInStreamsLambda.java | 13 +++++++++ .../types/capturedTypeInStreamsLambda.kt | 9 ++++++ ...ildcardTypeAsLambdaParameter.external.java | 8 ++++++ ...capturedWildcardTypeAsLambdaParameter.java | 13 +++++++++ .../capturedWildcardTypeAsLambdaParameter.kt | 13 +++++++++ ...sedCapturedWildcardTypeInSAM.external.java | 8 ++++++ .../unusedCapturedWildcardTypeInSAM.java | 13 +++++++++ .../types/unusedCapturedWildcardTypeInSAM.kt | 13 +++++++++ ...otlinConverterSingleFileTestGenerated.java | 28 +++++++++++++++++++ 17 files changed, 180 insertions(+), 14 deletions(-) create mode 100644 nj2k/testData/newJ2k/types/capturedTypeInStreamsLambda.java create mode 100644 nj2k/testData/newJ2k/types/capturedTypeInStreamsLambda.kt create mode 100644 nj2k/testData/newJ2k/types/capturedWildcardTypeAsLambdaParameter.external.java create mode 100644 nj2k/testData/newJ2k/types/capturedWildcardTypeAsLambdaParameter.java create mode 100644 nj2k/testData/newJ2k/types/capturedWildcardTypeAsLambdaParameter.kt create mode 100644 nj2k/testData/newJ2k/types/unusedCapturedWildcardTypeInSAM.external.java create mode 100644 nj2k/testData/newJ2k/types/unusedCapturedWildcardTypeInSAM.java create mode 100644 nj2k/testData/newJ2k/types/unusedCapturedWildcardTypeInSAM.kt diff --git a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/diagnosticBasedProcessings.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/diagnosticBasedProcessings.kt index bcbc9414b5b..17d210db29b 100644 --- a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/diagnosticBasedProcessings.kt +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/diagnosticBasedProcessings.kt @@ -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() + } + } } } diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/JavaToJKTreeBuilder.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/JavaToJKTreeBuilder.kt index 7b619c9c647..4ffd914d867 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/JavaToJKTreeBuilder.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/JavaToJKTreeBuilder.kt @@ -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()?.parameters?.any { it is PsiCapturedWildcardType } == true + }?.toJK(symbolProvider) + ?.takeUnless { type -> + type.isKotlinFunctionType + }?.asTypeElement() ?: JKTypeElementImpl(JKNoTypeImpl) fun PsiMethodReferenceExpression.toJK(): JKMethodReferenceExpression { val symbol = symbolProvider.provideSymbolForReference(this).let { symbol -> diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/NewCodeBuilder.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/NewCodeBuilder.kt index bad6cf8f8c6..5b533d17077 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/NewCodeBuilder.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/NewCodeBuilder.kt @@ -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()?.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()?.functionalType?.type != type + || type.nullability == Nullability.Nullable) ) { printWithNoIndent("?") } diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/conversions/AddElementsInfoConversion.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/conversions/AddElementsInfoConversion.kt index b94a2c4fadc..1588d4ad91e 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/conversions/AddElementsInfoConversion.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/conversions/AddElementsInfoConversion.kt @@ -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()?.wildcardType is JKStarProjectionType + ) { context.elementsInfoStorage.addEntry(type, UnknownNullability) } } diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/conversions/TypeMappingConversion.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/conversions/TypeMappingConversion.kt index e28ee5e3af4..b1620ac35b9 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/conversions/TypeMappingConversion.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/conversions/TypeMappingConversion.kt @@ -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) diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/impl/jk.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/impl/jk.kt index 33c53f8d3aa..64d28687add 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/impl/jk.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/impl/jk.kt @@ -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, diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/markers.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/markers.kt index d6b36f2778c..aa846e6edfe 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/markers.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/markers.kt @@ -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 } diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/types.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/types.kt index a95291a0532..ed265d7043b 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/types.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/types.kt @@ -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}") } } diff --git a/nj2k/testData/newJ2k/types/capturedTypeInStreamsLambda.java b/nj2k/testData/newJ2k/types/capturedTypeInStreamsLambda.java new file mode 100644 index 00000000000..093aab8c710 --- /dev/null +++ b/nj2k/testData/newJ2k/types/capturedTypeInStreamsLambda.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 packages) { + List sorted = packages.stream() + .filter(repoPackage -> repoPackage != null) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/nj2k/testData/newJ2k/types/capturedTypeInStreamsLambda.kt b/nj2k/testData/newJ2k/types/capturedTypeInStreamsLambda.kt new file mode 100644 index 00000000000..33daaa5e0f1 --- /dev/null +++ b/nj2k/testData/newJ2k/types/capturedTypeInStreamsLambda.kt @@ -0,0 +1,9 @@ +import java.util.stream.Collectors + +class Test { + private fun addPackages(packages: Collection) { + val sorted: List = packages.stream() + .filter { repoPackage: Number? -> repoPackage != null } + .collect(Collectors.toList()) + } +} diff --git a/nj2k/testData/newJ2k/types/capturedWildcardTypeAsLambdaParameter.external.java b/nj2k/testData/newJ2k/types/capturedWildcardTypeAsLambdaParameter.external.java new file mode 100644 index 00000000000..d04a6767a91 --- /dev/null +++ b/nj2k/testData/newJ2k/types/capturedWildcardTypeAsLambdaParameter.external.java @@ -0,0 +1,8 @@ +public interface SamFace { + void samMethod(T p); +} + +public class SamAcceptor { + public void acceptSam(SamFace sam) { + } +} \ No newline at end of file diff --git a/nj2k/testData/newJ2k/types/capturedWildcardTypeAsLambdaParameter.java b/nj2k/testData/newJ2k/types/capturedWildcardTypeAsLambdaParameter.java new file mode 100644 index 00000000000..f45924e8aa1 --- /dev/null +++ b/nj2k/testData/newJ2k/types/capturedWildcardTypeAsLambdaParameter.java @@ -0,0 +1,13 @@ +public class Test { + public void useSamUpper(SamAcceptor acceptor) { + acceptor.acceptSam(p -> {}); + } + + public void useSamLower(SamAcceptor acceptor) { + acceptor.acceptSam(p -> {}); + } + + public void useSam(SamAcceptor acceptor) { + acceptor.acceptSam(p -> {}); + } +} \ No newline at end of file diff --git a/nj2k/testData/newJ2k/types/capturedWildcardTypeAsLambdaParameter.kt b/nj2k/testData/newJ2k/types/capturedWildcardTypeAsLambdaParameter.kt new file mode 100644 index 00000000000..16e20648c27 --- /dev/null +++ b/nj2k/testData/newJ2k/types/capturedWildcardTypeAsLambdaParameter.kt @@ -0,0 +1,13 @@ +class Test { + fun useSamUpper(acceptor: SamAcceptor) { + acceptor.acceptSam { p: Number? -> } + } + + fun useSamLower(acceptor: SamAcceptor) { + acceptor.acceptSam { p -> } + } + + fun useSam(acceptor: SamAcceptor<*>) { + acceptor.acceptSam { p: Any? -> } + } +} diff --git a/nj2k/testData/newJ2k/types/unusedCapturedWildcardTypeInSAM.external.java b/nj2k/testData/newJ2k/types/unusedCapturedWildcardTypeInSAM.external.java new file mode 100644 index 00000000000..8033f5ff245 --- /dev/null +++ b/nj2k/testData/newJ2k/types/unusedCapturedWildcardTypeInSAM.external.java @@ -0,0 +1,8 @@ +public interface SamFace { + void samMethod(int p); +} + +public class SamAcceptor { + public void acceptSam(SamFace sam) { + } +} \ No newline at end of file diff --git a/nj2k/testData/newJ2k/types/unusedCapturedWildcardTypeInSAM.java b/nj2k/testData/newJ2k/types/unusedCapturedWildcardTypeInSAM.java new file mode 100644 index 00000000000..f45924e8aa1 --- /dev/null +++ b/nj2k/testData/newJ2k/types/unusedCapturedWildcardTypeInSAM.java @@ -0,0 +1,13 @@ +public class Test { + public void useSamUpper(SamAcceptor acceptor) { + acceptor.acceptSam(p -> {}); + } + + public void useSamLower(SamAcceptor acceptor) { + acceptor.acceptSam(p -> {}); + } + + public void useSam(SamAcceptor acceptor) { + acceptor.acceptSam(p -> {}); + } +} \ No newline at end of file diff --git a/nj2k/testData/newJ2k/types/unusedCapturedWildcardTypeInSAM.kt b/nj2k/testData/newJ2k/types/unusedCapturedWildcardTypeInSAM.kt new file mode 100644 index 00000000000..0108743f1a4 --- /dev/null +++ b/nj2k/testData/newJ2k/types/unusedCapturedWildcardTypeInSAM.kt @@ -0,0 +1,13 @@ +class Test { + fun useSamUpper(acceptor: SamAcceptor) { + acceptor.acceptSam { p: Int -> } + } + + fun useSamLower(acceptor: SamAcceptor) { + acceptor.acceptSam { p: Int -> } + } + + fun useSam(acceptor: SamAcceptor<*>) { + acceptor.acceptSam { p: Int -> } + } +} diff --git a/nj2k/tests/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverterSingleFileTestGenerated.java b/nj2k/tests/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverterSingleFileTestGenerated.java index 835825b7563..59506c8d6c5 100644 --- a/nj2k/tests/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverterSingleFileTestGenerated.java +++ b/nj2k/tests/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverterSingleFileTestGenerated.java @@ -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)