KT-38450 Add functional interface converter to the NJ2K
- Enable custom compiler options for `AbstractNewJavaToKotlinConverterSingleFileTest` - ^KT-38450 Fixed
This commit is contained in:
committed by
Roman Golyshev
parent
50fc9d3692
commit
4436142f00
+12
-2
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.references.readWriteAccess
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
|
||||
import org.jetbrains.kotlin.idea.util.CommentSaver
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
@@ -42,6 +43,7 @@ import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
|
||||
import org.jetbrains.kotlin.resolve.sam.getSingleAbstractMethodOrNull
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -383,15 +385,18 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing(
|
||||
val realProperty = group.firstIsInstanceOrNull<RealProperty>()
|
||||
val name = realGetter?.name ?: realSetter?.name!!
|
||||
|
||||
val superDeclarationOwner = (realGetter?.function ?: realSetter?.function)
|
||||
val functionDescriptor = (realGetter?.function ?: realSetter?.function)
|
||||
?.resolveToDescriptorIfAny(resolutionFacade)
|
||||
|
||||
if (functionDescriptor != null && isSamDescriptor(functionDescriptor)) return@mapNotNull null
|
||||
|
||||
val superDeclarationOwner = functionDescriptor
|
||||
?.overriddenDescriptors
|
||||
?.firstOrNull()
|
||||
?.findPsi()
|
||||
?.safeAs<KtDeclaration>()
|
||||
?.containingClassOrObject
|
||||
|
||||
collectingState.propertyNameToSuperType[superDeclarationOwner to name]
|
||||
val type = collectingState.propertyNameToSuperType[superDeclarationOwner to name]
|
||||
?: calculatePropertyType(
|
||||
realGetter?.function,
|
||||
@@ -408,6 +413,11 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing(
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtClassOrObject.isSamDescriptor(functionDescriptor: FunctionDescriptor): Boolean {
|
||||
val classDescriptor = descriptor as? ClassDescriptor ?: return false
|
||||
return getSingleAbstractMethodOrNull(classDescriptor) == functionDescriptor
|
||||
}
|
||||
|
||||
private fun List<PropertyData>.filterGettersAndSetters(
|
||||
klass: KtClassOrObject,
|
||||
factory: KtPsiFactory
|
||||
|
||||
@@ -78,6 +78,8 @@ object ConversionsRunner {
|
||||
LiteralConversion(context),
|
||||
StaticMemberAccessConversion(context),
|
||||
RemoveRedundantQualifiersForCallsConversion(context),
|
||||
FunctionalInterfacesConverter(context),
|
||||
|
||||
FilterImportsConversion(context),
|
||||
MoveInitBlocksToTheEndConversion(context),
|
||||
AddElementsInfoConversion(context)
|
||||
|
||||
@@ -18,7 +18,8 @@ data class NewJ2kConverterContext(
|
||||
val inConversionContext: (PsiElement) -> Boolean,
|
||||
val importStorage: JKImportStorage,
|
||||
val elementsInfoStorage: JKElementInfoStorage,
|
||||
val externalCodeProcessor: NewExternalCodeProcessing
|
||||
val externalCodeProcessor: NewExternalCodeProcessing,
|
||||
val functionalInterfaceConversionEnabled: Boolean
|
||||
) : ConverterContext {
|
||||
val project: Project
|
||||
get() = converter.project
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Computable
|
||||
import com.intellij.psi.*
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||
import org.jetbrains.kotlin.j2k.*
|
||||
@@ -161,7 +162,8 @@ class NewJavaToKotlinConverter(
|
||||
inConversionContext,
|
||||
importStorage,
|
||||
JKElementInfoStorage(),
|
||||
externalCodeProcessing
|
||||
externalCodeProcessing,
|
||||
languageVersion.supportsFeature(LanguageFeature.FunctionalInterfaceConversion)
|
||||
)
|
||||
ConversionsRunner.doApply(asts.withIndex().mapNotNull { (i, ast) ->
|
||||
processor.updateState(i, 1, phaseDescription)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.nj2k.conversions
|
||||
|
||||
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
|
||||
import org.jetbrains.kotlin.nj2k.annotationByFqName
|
||||
import org.jetbrains.kotlin.nj2k.tree.*
|
||||
|
||||
private const val FUNCTIONAL_INTERFACE = "java.lang.FunctionalInterface"
|
||||
|
||||
internal class FunctionalInterfacesConverter(context: NewJ2kConverterContext) : RecursiveApplicableConversionBase(context) {
|
||||
override fun applyToElement(element: JKTreeElement): JKTreeElement {
|
||||
if (!context.functionalInterfaceConversionEnabled) return recurse(element)
|
||||
if (element !is JKClass) return recurse(element)
|
||||
if (element.classKind != JKClass.ClassKind.INTERFACE) return recurse(element)
|
||||
if (element.inheritance.extends.isNotEmpty()) return recurse(element)
|
||||
|
||||
val functionalInterfaceMarker = element.annotationList.annotationByFqName(FUNCTIONAL_INTERFACE) ?: return recurse(element)
|
||||
|
||||
val samMethod = element.classBody.declarations.filterIsInstance<JKMethod>().singleOrNull { it.block is JKBodyStub }
|
||||
if (samMethod == null || samMethod.typeParameterList.typeParameters.isNotEmpty()) return recurse(element)
|
||||
|
||||
element.otherModifierElements += JKOtherModifierElement(OtherModifier.FUN)
|
||||
element.annotationList.annotations -= functionalInterfaceMarker
|
||||
|
||||
return recurse(element)
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ enum class OtherModifier(override val text: String) : Modifier {
|
||||
SUSPEND("suspend"),
|
||||
TAILREC("tailrec"),
|
||||
VARARG("vararg"),
|
||||
FUN("fun"),
|
||||
|
||||
NATIVE("native"),
|
||||
STATIC("static"),
|
||||
@@ -136,8 +137,8 @@ val JKModifierElement.modifier: Modifier
|
||||
|
||||
inline fun JKModifiersListOwner.forEachModifier(action: (JKModifierElement) -> Unit) {
|
||||
safeAs<JKVisibilityOwner>()?.visibilityElement?.let(action)
|
||||
safeAs<JKOtherModifiersOwner>()?.otherModifierElements?.forEach(action)
|
||||
safeAs<JKModalityOwner>()?.modalityElement?.let(action)
|
||||
safeAs<JKOtherModifiersOwner>()?.otherModifierElements?.forEach(action)
|
||||
safeAs<JKMutabilityOwner>()?.mutabilityElement?.let(action)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// RUNTIME_WITH_FULL_JDK
|
||||
|
||||
@FunctionalInterface
|
||||
public interface MyRunnable {
|
||||
int getResult();
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// RUNTIME_WITH_FULL_JDK
|
||||
fun interface MyRunnable {
|
||||
fun getResult(): Int
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// RUNTIME_WITH_FULL_JDK
|
||||
|
||||
@FunctionalInterface
|
||||
public interface MyRunnable {
|
||||
int getResult();
|
||||
|
||||
default int getDoubleResult() {
|
||||
return getResult() * 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// RUNTIME_WITH_FULL_JDK
|
||||
fun interface MyRunnable {
|
||||
fun getResult(): Int
|
||||
val doubleResult: Int
|
||||
get() = getResult() * 2
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
// RUNTIME_WITH_FULL_JDK
|
||||
|
||||
public interface MyRunnable {
|
||||
int getResult();
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// RUNTIME_WITH_FULL_JDK
|
||||
interface MyRunnable {
|
||||
val result: Int
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// RUNTIME_WITH_FULL_JDK
|
||||
|
||||
// we intentionally do not convert interface to Kotlin fun interface
|
||||
// if it inherits from some other interface, because it is hard to deal
|
||||
// with default methods which were already converted to properties
|
||||
// (and in kotlin fun interface cannot have abstract property)
|
||||
|
||||
public interface MyRunnableBase {
|
||||
default int getValue() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface MyRunnable extends MyRunnableBase {
|
||||
@Override
|
||||
int getValue();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// RUNTIME_WITH_FULL_JDK
|
||||
// we intentionally do not convert interface to Kotlin fun interface
|
||||
// if it inherits from some other interface, because it is hard to deal
|
||||
// with default methods which were already converted to properties
|
||||
// (and in kotlin fun interface cannot have abstract property)
|
||||
interface MyRunnableBase {
|
||||
val value: Int
|
||||
get() = 0
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface MyRunnable : MyRunnableBase {
|
||||
abstract override val value: Int
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// RUNTIME_WITH_FULL_JDK
|
||||
|
||||
@FunctionalInterface
|
||||
public interface MyRunnable {
|
||||
void run();
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// RUNTIME_WITH_FULL_JDK
|
||||
fun interface MyRunnable {
|
||||
fun run()
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
public interface MyRunnable {
|
||||
void run();
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
interface MyRunnable {
|
||||
fun run()
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// RUNTIME_WITH_FULL_JDK
|
||||
// COMPILER_ARGUMENTS: -XXLanguage:-FunctionalInterfaceConversion
|
||||
|
||||
@FunctionalInterface
|
||||
public interface MyRunnable {
|
||||
void run();
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// RUNTIME_WITH_FULL_JDK
|
||||
// COMPILER_ARGUMENTS: -XXLanguage:-FunctionalInterfaceConversion
|
||||
@FunctionalInterface
|
||||
interface MyRunnable {
|
||||
fun run()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// RUNTIME_WITH_FULL_JDK
|
||||
|
||||
@FunctionalInterface
|
||||
public interface MyRunnable {
|
||||
<T> void process(T t);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// RUNTIME_WITH_FULL_JDK
|
||||
@FunctionalInterface
|
||||
interface MyRunnable {
|
||||
fun <T> process(t: T)
|
||||
}
|
||||
+12
-8
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.nj2k
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleSettings
|
||||
import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
|
||||
import org.jetbrains.kotlin.idea.test.withCustomCompilerOptions
|
||||
import org.jetbrains.kotlin.j2k.AbstractJavaToKotlinConverterSingleFileTest
|
||||
import org.jetbrains.kotlin.j2k.ConverterSettings
|
||||
import org.jetbrains.kotlin.nj2k.postProcessing.NewJ2kPostProcessor
|
||||
@@ -16,15 +17,18 @@ import java.io.File
|
||||
|
||||
abstract class AbstractNewJavaToKotlinConverterSingleFileTest : AbstractJavaToKotlinConverterSingleFileTest() {
|
||||
override fun doTest(javaPath: String) {
|
||||
val directory = File(javaPath).parentFile
|
||||
val expectedFileName = "${File(javaPath).nameWithoutExtension}.external"
|
||||
val expectedFiles = directory.listFiles { _, name ->
|
||||
name == "$expectedFileName.kt" || name == "$expectedFileName.java"
|
||||
}!!.filterNotNull()
|
||||
for (expectedFile in expectedFiles) {
|
||||
addFile(expectedFile, dirName = null)
|
||||
val javaFile = File(javaPath)
|
||||
withCustomCompilerOptions(javaFile.readText(), project, module) {
|
||||
val directory = javaFile.parentFile
|
||||
val expectedFileName = "${javaFile.nameWithoutExtension}.external"
|
||||
val expectedFiles = directory.listFiles { _, name ->
|
||||
name == "$expectedFileName.kt" || name == "$expectedFileName.java"
|
||||
}!!.filterNotNull()
|
||||
for (expectedFile in expectedFiles) {
|
||||
addFile(expectedFile, dirName = null)
|
||||
}
|
||||
super.doTest(javaPath)
|
||||
}
|
||||
super.doTest(javaPath)
|
||||
}
|
||||
|
||||
override fun compareResults(expectedFile: File, actual: String) {
|
||||
|
||||
+53
@@ -2583,6 +2583,59 @@ public class NewJavaToKotlinConverterSingleFileTestGenerated extends AbstractNew
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("nj2k/testData/newJ2k/functionalInterfaces")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class FunctionalInterfaces extends AbstractNewJavaToKotlinConverterSingleFileTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("AbstractGetter1.java")
|
||||
public void testAbstractGetter1() throws Exception {
|
||||
runTest("nj2k/testData/newJ2k/functionalInterfaces/AbstractGetter1.java");
|
||||
}
|
||||
|
||||
@TestMetadata("AbstractGetter2.java")
|
||||
public void testAbstractGetter2() throws Exception {
|
||||
runTest("nj2k/testData/newJ2k/functionalInterfaces/AbstractGetter2.java");
|
||||
}
|
||||
|
||||
@TestMetadata("AbstractGetterNoFunctionalInterfaceAnnotation.java")
|
||||
public void testAbstractGetterNoFunctionalInterfaceAnnotation() throws Exception {
|
||||
runTest("nj2k/testData/newJ2k/functionalInterfaces/AbstractGetterNoFunctionalInterfaceAnnotation.java");
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInFunctionalInterfaces() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("nj2k/testData/newJ2k/functionalInterfaces"), Pattern.compile("^([^\\.]+)\\.java$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("InterfacesHierarchy.java")
|
||||
public void testInterfacesHierarchy() throws Exception {
|
||||
runTest("nj2k/testData/newJ2k/functionalInterfaces/InterfacesHierarchy.java");
|
||||
}
|
||||
|
||||
@TestMetadata("MyRunnable.java")
|
||||
public void testMyRunnable() throws Exception {
|
||||
runTest("nj2k/testData/newJ2k/functionalInterfaces/MyRunnable.java");
|
||||
}
|
||||
|
||||
@TestMetadata("NoFunctionalInterfaceAnnotation.java")
|
||||
public void testNoFunctionalInterfaceAnnotation() throws Exception {
|
||||
runTest("nj2k/testData/newJ2k/functionalInterfaces/NoFunctionalInterfaceAnnotation.java");
|
||||
}
|
||||
|
||||
@TestMetadata("NoFunctionalInterfaceConversionEnabled.java")
|
||||
public void testNoFunctionalInterfaceConversionEnabled() throws Exception {
|
||||
runTest("nj2k/testData/newJ2k/functionalInterfaces/NoFunctionalInterfaceConversionEnabled.java");
|
||||
}
|
||||
|
||||
@TestMetadata("SamMethodWithGenerics.java")
|
||||
public void testSamMethodWithGenerics() throws Exception {
|
||||
runTest("nj2k/testData/newJ2k/functionalInterfaces/SamMethodWithGenerics.java");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("nj2k/testData/newJ2k/identifier")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user