J2K: fixed usage processing scheme to cover exotic case with anonymous class

This commit is contained in:
Valentin Kipyatkov
2014-10-29 17:17:33 +03:00
committed by valentin
parent 7de15ec2a8
commit 2e0ccf6984
7 changed files with 57 additions and 20 deletions
+8 -11
View File
@@ -40,10 +40,9 @@ class Converter private(private val elementToConvert: PsiElement,
private val personalState: Converter.PersonalState) { private val personalState: Converter.PersonalState) {
// state which is shared between all converter's based on this one // state which is shared between all converter's based on this one
private class CommonState { private class CommonState(val usageProcessingsCollector: (UsageProcessing) -> Unit) {
val importsToAdd = LinkedHashSet<String>() val importsToAdd = LinkedHashSet<String>()
val deferredElements = ArrayList<DeferredElement<*>>() val deferredElements = ArrayList<DeferredElement<*>>()
val usageProcessings = ArrayList<UsageProcessing>()
val postUnfoldActions = ArrayList<() -> Unit>() val postUnfoldActions = ArrayList<() -> Unit>()
} }
@@ -62,8 +61,9 @@ class Converter private(private val elementToConvert: PsiElement,
class object { class object {
public fun create(elementToConvert: PsiElement, settings: ConverterSettings, conversionScope: ConversionScope, public fun create(elementToConvert: PsiElement, settings: ConverterSettings, conversionScope: ConversionScope,
referenceSearcher: ReferenceSearcher, lazyResolveSessionGetter: ((JetElement) -> KotlinCodeAnalyzer)?, postProcessor: PostProcessor?): Converter { referenceSearcher: ReferenceSearcher, lazyResolveSessionGetter: ((JetElement) -> KotlinCodeAnalyzer)?, postProcessor: PostProcessor?,
return Converter(elementToConvert, settings, conversionScope, referenceSearcher, lazyResolveSessionGetter, postProcessor, CommonState(), PersonalState(null)) usageProcessingsCollector: (UsageProcessing) -> Unit): Converter {
return Converter(elementToConvert, settings, conversionScope, referenceSearcher, lazyResolveSessionGetter, postProcessor, CommonState(usageProcessingsCollector), PersonalState(null))
} }
} }
@@ -75,22 +75,19 @@ class Converter private(private val elementToConvert: PsiElement,
private fun createDefaultCodeConverter() = CodeConverter(this, DefaultExpressionConverter(), DefaultStatementConverter(), null) private fun createDefaultCodeConverter() = CodeConverter(this, DefaultExpressionConverter(), DefaultStatementConverter(), null)
public trait IntermediateResult { public trait IntermediateResult {
fun finishConversion(usageProcessings: Collection<UsageProcessing>): String fun finishConversion(usageProcessings: Map<PsiElement, UsageProcessing>): String
val usageProcessings: Collection<UsageProcessing>
} }
public fun convert(): IntermediateResult? { public fun convert(): IntermediateResult? {
val element = convertTopElement(elementToConvert) ?: return null val element = convertTopElement(elementToConvert) ?: return null
return object: IntermediateResult { return object: IntermediateResult {
override fun finishConversion(usageProcessings: Collection<UsageProcessing>): String { override fun finishConversion(usageProcessings: Map<PsiElement, UsageProcessing>): String {
unfoldDeferredElements(usageProcessings) unfoldDeferredElements(usageProcessings)
val builder = CodeBuilder(elementToConvert) val builder = CodeBuilder(elementToConvert)
builder.append(element) builder.append(element)
return builder.result return builder.result
} }
override val usageProcessings: Collection<UsageProcessing> = commonState.usageProcessings
} }
} }
@@ -108,7 +105,7 @@ class Converter private(private val elementToConvert: PsiElement,
else -> null else -> null
} }
private fun unfoldDeferredElements(usageProcessings: Collection<UsageProcessing>) { private fun unfoldDeferredElements(usageProcessings: Map<PsiElement, UsageProcessing>) {
val codeConverter = createDefaultCodeConverter().withSpecialExpressionConverter(UsageProcessingExpressionConverter(usageProcessings)) val codeConverter = createDefaultCodeConverter().withSpecialExpressionConverter(UsageProcessingExpressionConverter(usageProcessings))
// we use loop with index because new deferred elements can be added during unfolding // we use loop with index because new deferred elements can be added during unfolding
@@ -128,7 +125,7 @@ class Converter private(private val elementToConvert: PsiElement,
} }
public fun addUsageProcessing(processing: UsageProcessing) { public fun addUsageProcessing(processing: UsageProcessing) {
commonState.usageProcessings.add(processing) commonState.usageProcessingsCollector(processing)
} }
public fun addPostUnfoldDeferredElementsAction(action: () -> Unit) { public fun addPostUnfoldDeferredElementsAction(action: () -> Unit) {
@@ -28,6 +28,7 @@ import java.util.ArrayList
import org.jetbrains.jet.j2k.usageProcessing.UsageProcessing import org.jetbrains.jet.j2k.usageProcessing.UsageProcessing
import org.jetbrains.jet.lang.psi.JetElement import org.jetbrains.jet.lang.psi.JetElement
import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer
import java.util.HashMap
public trait ConversionScope { public trait ConversionScope {
public fun contains(element: PsiElement): Boolean public fun contains(element: PsiElement): Boolean
@@ -53,12 +54,16 @@ public class JavaToKotlinConverter(private val project: Project,
public fun elementsToKotlin(psiElementsAndProcessors: List<Pair<PsiElement, PostProcessor?>>): List<String> { public fun elementsToKotlin(psiElementsAndProcessors: List<Pair<PsiElement, PostProcessor?>>): List<String> {
try { try {
val intermediateResults = ArrayList<Converter.IntermediateResult?>(psiElementsAndProcessors.size) val intermediateResults = ArrayList<Converter.IntermediateResult?>(psiElementsAndProcessors.size)
val usageProcessings = ArrayList<UsageProcessing>() val usageProcessings = HashMap<PsiElement, UsageProcessing>()
val usageProcessingCollector: (UsageProcessing) -> Unit = { usageProcessing ->
assert(!usageProcessings.containsKey(usageProcessing.targetElement))
{ "Duplicated UsageProcessing for target element ${usageProcessing.targetElement}" }
usageProcessings.put(usageProcessing.targetElement, usageProcessing)
}
for ((psiElement, postProcessor) in psiElementsAndProcessors) { for ((psiElement, postProcessor) in psiElementsAndProcessors) {
val converter = Converter.create(psiElement, settings, conversionScope, referenceSearcher, lazyResolveSessionGetter, postProcessor) val converter = Converter.create(psiElement, settings, conversionScope, referenceSearcher, lazyResolveSessionGetter, postProcessor, usageProcessingCollector)
val result = converter.convert() val result = converter.convert()
intermediateResults.add(result) intermediateResults.add(result)
result?.usageProcessings?.let { usageProcessings.addAll(it) }
} }
val results = ArrayList<String>(psiElementsAndProcessors.size) val results = ArrayList<String>(psiElementsAndProcessors.size)
@@ -38,22 +38,20 @@ trait ExternalCodeProcessor {
fun processUsage(reference: PsiReference) fun processUsage(reference: PsiReference)
} }
class UsageProcessingExpressionConverter(processings: Collection<UsageProcessing>) : SpecialExpressionConverter { class UsageProcessingExpressionConverter(val processings: Map<PsiElement, UsageProcessing>) : SpecialExpressionConverter {
private val targetToProcessing = processings.toMap { it.targetElement } // we assume that there will be no more than one processing for one target element
override fun convertExpression(expression: PsiExpression, codeConverter: CodeConverter): Expression? { override fun convertExpression(expression: PsiExpression, codeConverter: CodeConverter): Expression? {
if (targetToProcessing.isEmpty()) return null if (processings.isEmpty()) return null
when (expression) { when (expression) {
is PsiReferenceExpression -> { is PsiReferenceExpression -> {
val target = expression.resolve() as? PsiVariable ?: return null val target = expression.resolve() as? PsiVariable ?: return null
val processor = targetToProcessing[target]?.convertedCodeProcessor ?: return null val processor = processings[target]?.convertedCodeProcessor ?: return null
return processor.convertVariableUsage(expression, codeConverter) return processor.convertVariableUsage(expression, codeConverter)
} }
is PsiMethodCallExpression -> { is PsiMethodCallExpression -> {
val target = expression.getMethodExpression().resolve() as? PsiMethod ?: return null val target = expression.getMethodExpression().resolve() as? PsiMethod ?: return null
val processor = targetToProcessing[target]?.convertedCodeProcessor ?: return null val processor = processings[target]?.convertedCodeProcessor ?: return null
return processor.convertMethodUsage(expression, codeConverter) return processor.convertMethodUsage(expression, codeConverter)
} }
@@ -1317,6 +1317,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/tests/testData/fileOrElement/dropAccessors"), Pattern.compile("^(.+)\\.java$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/tests/testData/fileOrElement/dropAccessors"), Pattern.compile("^(.+)\\.java$"), true);
} }
@TestMetadata("AnonymousClass.java")
public void testAnonymousClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/tests/testData/fileOrElement/dropAccessors/AnonymousClass.java");
doTest(fileName);
}
@TestMetadata("CannotDropOnlySetter.java") @TestMetadata("CannotDropOnlySetter.java")
public void testCannotDropOnlySetter() throws Exception { public void testCannotDropOnlySetter() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/tests/testData/fileOrElement/dropAccessors/CannotDropOnlySetter.java"); String fileName = JetTestUtils.navigationMetadata("j2k/tests/testData/fileOrElement/dropAccessors/CannotDropOnlySetter.java");
@@ -1317,6 +1317,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/tests/testData/fileOrElement/dropAccessors"), Pattern.compile("^(.+)\\.java$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/tests/testData/fileOrElement/dropAccessors"), Pattern.compile("^(.+)\\.java$"), true);
} }
@TestMetadata("AnonymousClass.java")
public void testAnonymousClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/tests/testData/fileOrElement/dropAccessors/AnonymousClass.java");
doTest(fileName);
}
@TestMetadata("CannotDropOnlySetter.java") @TestMetadata("CannotDropOnlySetter.java")
public void testCannotDropOnlySetter() throws Exception { public void testCannotDropOnlySetter() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/tests/testData/fileOrElement/dropAccessors/CannotDropOnlySetter.java"); String fileName = JetTestUtils.navigationMetadata("j2k/tests/testData/fileOrElement/dropAccessors/CannotDropOnlySetter.java");
@@ -0,0 +1,14 @@
public class X {
void foo() {
Runnable runnable = new Runnable() {
int f = 10;
int getValue() { return f; }
@Override
public void run() {
System.out.println(getValue());
}
};
}
}
@@ -0,0 +1,11 @@
public class X {
fun foo() {
val runnable = object : Runnable {
var value = 10
override fun run() {
System.out.println(value)
}
}
}
}