diff --git a/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessings.kt b/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessings.kt index 10f2dee59e5..7c56744149d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessings.kt +++ b/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessings.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.idea.core.setVisibility import org.jetbrains.kotlin.idea.inspections.RedundantSamConstructorInspection import org.jetbrains.kotlin.idea.intentions.* @@ -36,6 +37,9 @@ import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getChildOfType +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.psi.psiUtil.isAncestor import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getType @@ -66,6 +70,8 @@ object J2KPostProcessingRegistrar { _processings.add(FixObjectStringConcatenationProcessing()) _processings.add(ConvertToStringTemplateProcessing()) _processings.add(UsePropertyAccessSyntaxProcessing()) + _processings.add(UninitializedVariableReferenceFromInitializerToThisReferenceProcessing()) + _processings.add(UnresolvedVariableReferenceFromInitializerToThisReferenceProcessing()) _processings.add(RemoveRedundantSamAdaptersProcessing()) _processings.add(RemoveRedundantCastToNullableProcessing()) @@ -278,4 +284,39 @@ object J2KPostProcessingRegistrar { return null } } + + private class UninitializedVariableReferenceFromInitializerToThisReferenceProcessing : J2kPostProcessing { + override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? { + if (element !is KtSimpleNameExpression || diagnostics.forElement(element).none { it.factory == Errors.UNINITIALIZED_VARIABLE }) return null + + val resolved = element.mainReference.resolve() ?: return null + if (resolved.isAncestor(element, strict = true)) { + if (resolved is KtVariableDeclaration && resolved.hasInitializer()) { + val anonymousObject = element.getParentOfType(true) ?: return null + if (resolved.initializer!!.getChildOfType() == anonymousObject) { + return { element.replaced(KtPsiFactory(element).createThisExpression()) } + } + } + } + + return null + } + } + + private class UnresolvedVariableReferenceFromInitializerToThisReferenceProcessing : J2kPostProcessing { + override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? { + if (element !is KtSimpleNameExpression || diagnostics.forElement(element).none { it.factory == Errors.UNRESOLVED_REFERENCE }) return null + + val anonymousObject = element.getParentOfType(true) ?: return null + + val variable = anonymousObject.getParentOfType(true) ?: return null + + if (variable.nameAsName == element.getReferencedNameAsName() && + variable.initializer?.getChildOfType() == anonymousObject) { + return { element.replaced(KtPsiFactory(element).createThisExpression()) } + } + + return null + } + } } \ No newline at end of file diff --git a/j2k/src/org/jetbrains/kotlin/j2k/CodeConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/CodeConverter.kt index b3bbc635c5d..cd298f19ae1 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/CodeConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/CodeConverter.kt @@ -150,30 +150,32 @@ class CodeConverter( } fun convertedExpressionType(expression: PsiExpression, expectedType: PsiType): Type { - val convertedExpression = convertExpression(expression) - val actualType = expression.type ?: return ErrorType() - var resultType = typeConverter.convertType(actualType, if (convertedExpression.isNullable) Nullability.Nullable else Nullability.NotNull) + with(converter.codeConverterForType) { + val convertedExpression = convertExpression(expression) + val actualType = expression.type ?: return ErrorType() + var resultType = typeConverter.convertType(actualType, if (convertedExpression.isNullable) Nullability.Nullable else Nullability.NotNull) - if (actualType is PsiPrimitiveType && resultType.isNullable || - expectedType is PsiPrimitiveType && actualType is PsiClassType) { - resultType = resultType.toNotNullType() - } - - if (needConversion(actualType, expectedType)) { - val expectedTypeStr = expectedType.canonicalText - - val willConvert = if (convertedExpression is LiteralExpression - || expression is PsiPrefixExpression && expression.isLiteralWithSign() ) - expectedTypeStr == "float" || expectedTypeStr == "double" - else - PRIMITIVE_TYPE_CONVERSIONS[expectedTypeStr] != null - - if (willConvert) { - resultType = typeConverter.convertType(expectedType, Nullability.NotNull) + if (actualType is PsiPrimitiveType && resultType.isNullable || + expectedType is PsiPrimitiveType && actualType is PsiClassType) { + resultType = resultType.toNotNullType() } - } - return resultType + if (needConversion(actualType, expectedType)) { + val expectedTypeStr = expectedType.canonicalText + + val willConvert = if (convertedExpression is LiteralExpression + || expression is PsiPrefixExpression && expression.isLiteralWithSign()) + expectedTypeStr == "float" || expectedTypeStr == "double" + else + PRIMITIVE_TYPE_CONVERSIONS[expectedTypeStr] != null + + if (willConvert) { + resultType = typeConverter.convertType(expectedType, Nullability.NotNull) + } + } + + return resultType + } } private fun PsiPrefixExpression.isLiteralWithSign() diff --git a/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt b/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt index 1cdeff64c25..a628fc7f87f 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt @@ -66,8 +66,13 @@ class Converter private constructor( } } + private fun withCommonState(state: CommonState) = Converter(elementToConvert, settings, inConversionScope, services, state) + private fun createDefaultCodeConverter() = CodeConverter(this, DefaultExpressionConverter(), DefaultStatementConverter(), null) + /* special code converter for type, based on this with detached deferred elements list, to prevent recursive deferred elements */ + val codeConverterForType by lazy { withCommonState(CommonState {}).createDefaultCodeConverter() } + data class IntermediateResult( val codeGenerator: (Map>) -> Result, val parseContext: ParseContext @@ -489,15 +494,16 @@ class Converter private constructor( fun shouldDeclareVariableType(variable: PsiVariable, type: Type, canChangeType: Boolean): Boolean { assert(inConversionScope(variable)) + val codeConverter = codeConverterForType val initializer = variable.initializer if (initializer == null || initializer.isNullLiteral()) return true if (initializer.type is PsiPrimitiveType && type is PrimitiveType) { - if (createDefaultCodeConverter().convertedExpressionType(initializer, variable.type) != type) { + if (codeConverter.convertedExpressionType(initializer, variable.type) != type) { return true } } - val initializerType = createDefaultCodeConverter().convertedExpressionType(initializer, variable.type) + val initializerType = codeConverter.convertedExpressionType(initializer, variable.type) // do not add explicit type when initializer is not resolved, let user add it if really needed if (initializerType is ErrorType) return false diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt index d581cfa891d..3c86275039a 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt @@ -566,15 +566,6 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter { val qualifier = expression.qualifierExpression var identifier = Identifier.withNoPrototype(referenceName, isNullable) - - - expression.getContainingClass()?.getParentOfType(false)?.let { - if (it == expression.qualifierExpression?.reference?.resolve()) { - result = QualifiedExpression(ThisExpression(Identifier.Empty).assignNoPrototype(), identifier, null) - return - } - } - if (qualifier != null && qualifier.type is PsiArrayType && referenceName == "length") { identifier = Identifier.withNoPrototype("size", isNullable) } @@ -639,7 +630,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter { if (converter.shouldDeclareVariableType(target, converter.typeConverter.convertVariableType(target), canChangeType)) return false // if variable type won't be specified then check nullability of the initializer - return codeConverter.convertExpression(target.initializer).isNullable + return converter.codeConverterForType.convertExpression(target.initializer).isNullable } override fun visitSuperExpression(expression: PsiSuperExpression) { diff --git a/j2k/testData/fileOrElement/anonymousClass/kt-13146.java b/j2k/testData/fileOrElement/anonymousClass/kt-13146.java index e5bb4583d91..b18eaf484f5 100644 --- a/j2k/testData/fileOrElement/anonymousClass/kt-13146.java +++ b/j2k/testData/fileOrElement/anonymousClass/kt-13146.java @@ -15,3 +15,18 @@ public class Test2 { } }; } + +public class Handler { + public void postDelayed(Runnable r, long time) {} +} + +public class Test3 { + private Handler handler = new Handler(); + + private Runnable someRunnable = new Runnable() { + @Override + public void run() { + handler.postDelayed(someRunnable, 1000); + } + }; +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/anonymousClass/kt-13146.kt b/j2k/testData/fileOrElement/anonymousClass/kt-13146.kt index 6014d4f9ab8..08e16ce7db2 100644 --- a/j2k/testData/fileOrElement/anonymousClass/kt-13146.kt +++ b/j2k/testData/fileOrElement/anonymousClass/kt-13146.kt @@ -13,3 +13,17 @@ class Test2 { } } } + +class Handler { + fun postDelayed(r: Runnable, time: Long) {} +} + +class Test3 { + private val handler = Handler() + + private val someRunnable = object : Runnable { + override fun run() { + handler.postDelayed(this, 1000) + } + } +} \ No newline at end of file