From 599aa9435fad1749697b1ca5ebc1c91600edf0c5 Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Mon, 3 Dec 2018 19:04:13 +0300 Subject: [PATCH] New J2K: Move postProcessingRegistrar to a NewJ2KPostProcessingRegistrarImpl and revert old one --- .../kotlin/idea/j2k/J2kPostProcessor.kt | 9 +- .../kotlin/idea/j2k/NewJ2kPostProcessings.kt | 572 ++++++++++++++++++ ...tNewJavaToKotlinConverterSingleFileTest.kt | 3 +- 3 files changed, 580 insertions(+), 4 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/j2k/NewJ2kPostProcessings.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessor.kt index 2ade5ea82a0..00f3a19512a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessor.kt @@ -40,7 +40,10 @@ import org.jetbrains.kotlin.psi.psiUtil.elementsInRange import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics import java.util.* -class J2kPostProcessor(private val formatCode: Boolean) : PostProcessor { +class J2kPostProcessor( + private val formatCode: Boolean, + private val postProcessingRegistrar: J2KPostProcessingRegistrar = J2KPostProcessingRegistrarImpl +) : PostProcessor { override fun insertImport(file: KtFile, fqName: FqName) { ApplicationManager.getApplication().invokeAndWait { runWriteAction { @@ -122,11 +125,11 @@ class J2kPostProcessor(private val formatCode: Boolean) : PostProcessor { super.visitElement(element) if (rangeResult == RangeFilterResult.PROCESS) { - J2KPostProcessingRegistrar.processings.forEach { processing -> + postProcessingRegistrar.processings.forEach { processing -> val action = processing.createAction(element, diagnostics) if (action != null) { availableActions.add(ActionData(element, action, - J2KPostProcessingRegistrar.priority(processing), + postProcessingRegistrar.priority(processing), processing.writeActionNeeded)) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/j2k/NewJ2kPostProcessings.kt b/idea/src/org/jetbrains/kotlin/idea/j2k/NewJ2kPostProcessings.kt new file mode 100644 index 00000000000..55c9d5ed43a --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/j2k/NewJ2kPostProcessings.kt @@ -0,0 +1,572 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.j2k + +import com.intellij.codeInsight.actions.OptimizeImportsProcessor +import com.intellij.codeInspection.* +import com.intellij.codeInspection.ex.LocalInspectionToolWrapper +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiElement +import com.intellij.psi.search.LocalSearchScope +import com.intellij.psi.search.searches.ReferencesSearch +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.DiagnosticFactory +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.idea.analysis.analyzeInContext +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.idea.core.replaced +import org.jetbrains.kotlin.idea.core.setVisibility +import org.jetbrains.kotlin.idea.inspections.* +import org.jetbrains.kotlin.idea.inspections.branchedTransformations.IfThenToSafeAccessInspection +import org.jetbrains.kotlin.idea.inspections.conventionNameCalls.ReplaceGetOrSetInspection +import org.jetbrains.kotlin.idea.intentions.* +import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnAsymmetricallyIntention +import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnIntention +import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IfThenToElvisIntention +import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isTrivialStatementBody +import org.jetbrains.kotlin.idea.quickfix.ChangeVariableMutabilityFix +import org.jetbrains.kotlin.idea.quickfix.QuickFixActionBase +import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix +import org.jetbrains.kotlin.idea.quickfix.RemoveUselessCastFix +import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.idea.util.getResolutionScope +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getType +import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.types.isNullable +import org.jetbrains.kotlin.types.typeUtil.makeNotNullable +import org.jetbrains.kotlin.utils.mapToIndex +import java.util.* + +object NewJ2KPostProcessingRegistrarImpl: J2KPostProcessingRegistrar { + private val _processings = ArrayList() + + override val processings: Collection + get() = _processings + + private val processingsToPriorityMap = HashMap() + + override fun priority(processing: J2kPostProcessing): Int = processingsToPriorityMap[processing]!! + + init { + _processings.add(RemoveExplicitTypeArgumentsProcessing()) + _processings.add(RemoveRedundantOverrideVisibilityProcessing()) + registerInspectionBasedProcessing(MoveLambdaOutsideParenthesesInspection()) + _processings.add(FixObjectStringConcatenationProcessing()) + _processings.add(ConvertToStringTemplateProcessing()) + _processings.add(UsePropertyAccessSyntaxProcessing()) + _processings.add(UninitializedVariableReferenceFromInitializerToThisReferenceProcessing()) + _processings.add(UnresolvedVariableReferenceFromInitializerToThisReferenceProcessing()) + _processings.add(RemoveRedundantSamAdaptersProcessing()) + _processings.add(RemoveRedundantCastToNullableProcessing()) + registerInspectionBasedProcessing(ReplacePutWithAssignmentInspection()) + _processings.add(UseExpressionBodyProcessing()) + registerInspectionBasedProcessing(UnnecessaryVariableInspection()) + registerGeneralInspectionBasedProcessing(RedundantModalityModifierInspection()) + registerGeneralInspectionBasedProcessing(RedundantVisibilityModifierInspection()) + registerGeneralInspectionBasedProcessing(RedundantExplicitTypeInspection()) + registerGeneralInspectionBasedProcessing(RedundantUnitReturnTypeInspection()) + registerGeneralInspectionBasedProcessing(RedundantGetterInspection()) + registerGeneralInspectionBasedProcessing(RedundantSetterInspection()) + + _processings.add(RemoveExplicitPropertyType()) + _processings.add(RemoveRedundantNullability()) + + registerGeneralInspectionBasedProcessing(CanBeValInspection(ignoreNotUsedVals = false)) + + registerIntentionBasedProcessing(FoldInitializerAndIfToElvisIntention()) + registerIntentionBasedProcessing(RemoveEmptyClassBodyIntention()) + + registerIntentionBasedProcessing(FoldIfToReturnIntention()) { it.then.isTrivialStatementBody() && it.`else`.isTrivialStatementBody() } + registerIntentionBasedProcessing(FoldIfToReturnAsymmetricallyIntention()) { it.then.isTrivialStatementBody() && (KtPsiUtil.skipTrailingWhitespacesAndComments(it) as KtReturnExpression).returnedExpression.isTrivialStatementBody() } + + registerInspectionBasedProcessing(IfThenToSafeAccessInspection()) + registerIntentionBasedProcessing(IfThenToElvisIntention()) + registerInspectionBasedProcessing(SimplifyNegatedBinaryExpressionInspection()) + registerInspectionBasedProcessing(ReplaceGetOrSetInspection()) + registerIntentionBasedProcessing(AddOperatorModifierIntention()) + registerIntentionBasedProcessing(ObjectLiteralToLambdaIntention()) + registerIntentionBasedProcessing(AnonymousFunctionToLambdaIntention()) + registerIntentionBasedProcessing(RemoveUnnecessaryParenthesesIntention()) + registerIntentionBasedProcessing(DestructureIntention()) + registerInspectionBasedProcessing(SimplifyAssertNotNullInspection()) + registerIntentionBasedProcessing(RemoveRedundantCallsOfConversionMethodsIntention()) + registerGeneralInspectionBasedProcessing(LiftReturnOrAssignmentInspection()) + + _processings.add(object : J2kPostProcessing { + override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? = + { + OptimizeImportsProcessor(element.project, element.containingKtFile).run() + PsiDocumentManager.getInstance(element.project).commitAllDocuments() + } + + override val writeActionNeeded: Boolean = true + }) + + registerDiagnosticBasedProcessing(Errors.USELESS_CAST) { element, _ -> + val expression = RemoveUselessCastFix.invoke(element) + + val variable = expression.parent as? KtProperty + if (variable != null && expression == variable.initializer && variable.isLocal) { + val ref = ReferencesSearch.search(variable, LocalSearchScope(variable.containingFile)).findAll().singleOrNull() + if (ref != null && ref.element is KtSimpleNameExpression) { + ref.element.replace(expression) + variable.delete() + } + } + } + + registerDiagnosticBasedProcessing(Errors.REDUNDANT_PROJECTION) { _, diagnostic -> + val fix = RemoveModifierFix.createRemoveProjectionFactory(true).createActions(diagnostic).single() as RemoveModifierFix + fix.invoke() + } + + registerDiagnosticBasedProcessing(Errors.NON_FINAL_MEMBER_IN_FINAL_CLASS) { _, diagnostic -> + val fix = + RemoveModifierFix + .createRemoveModifierFromListOwnerFactory(KtTokens.OPEN_KEYWORD) + .createActions(diagnostic).single() as RemoveModifierFix + fix.invoke() + } + registerDiagnosticBasedProcessing(Errors.NON_FINAL_MEMBER_IN_OBJECT) { _, diagnostic -> + val fix = + RemoveModifierFix + .createRemoveModifierFromListOwnerFactory(KtTokens.OPEN_KEYWORD) + .createActions(diagnostic).single() as RemoveModifierFix + fix.invoke() + } + + registerDiagnosticBasedProcessingFactory( + Errors.VAL_REASSIGNMENT, Errors.CAPTURED_VAL_INITIALIZATION, Errors.CAPTURED_MEMBER_VAL_INITIALIZATION + ) { + element: KtSimpleNameExpression, _: Diagnostic -> + val property = element.mainReference.resolve() as? KtProperty + if (property == null) { + null + } + else { + { + if (!property.isVar) { + property.valOrVarKeyword.replace(KtPsiFactory(element.project).createVarKeyword()) + } + } + } + } + + registerDiagnosticBasedProcessing(Errors.UNNECESSARY_NOT_NULL_ASSERTION) { element, _ -> + val exclExclExpr = element.parent as KtUnaryExpression + val baseExpression = exclExclExpr.baseExpression!! + val context = baseExpression.analyze(BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS) + if (context.diagnostics.forElement(element).any { it.factory == Errors.UNNECESSARY_NOT_NULL_ASSERTION }) { + exclExclExpr.replace(baseExpression) + } + } + + processingsToPriorityMap.putAll(_processings.mapToIndex()) + } + + private inline fun > registerIntentionBasedProcessing( + intention: TIntention, + noinline additionalChecker: (TElement) -> Boolean = { true } + ) { + _processings.add(object : J2kPostProcessing { + // Intention can either need or not need write action + override val writeActionNeeded = intention.startInWriteAction() + + override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? { + if (!TElement::class.java.isInstance(element)) return null + val tElement = element as TElement + if (intention.applicabilityRange(tElement) == null) return null + if (!additionalChecker(tElement)) return null + return { + if (intention.applicabilityRange(tElement) != null) { // check availability of the intention again because something could change + intention.applyTo(element, null) + } + } + } + }) + } + + private inline fun registerGeneralInspectionBasedProcessing( + inspection: TInspection, + acceptInformationLevel: Boolean = false + ) { + _processings.add(object : J2kPostProcessing { + override val writeActionNeeded = false + + fun QuickFix.applyFixSmart(project: Project, descriptor: D) { + if (descriptor is ProblemDescriptor) { + if (this is IntentionWrapper) { + @Suppress("NOT_YET_SUPPORTED_IN_INLINE") + fun applySelfTargetingIntention(action: SelfTargetingIntention) { + val target = action.getTarget(descriptor.psiElement.startOffset, descriptor.psiElement.containingFile) ?: return + if (!action.isApplicableTo(target, descriptor.psiElement.startOffset)) return + action.applyTo(target, null) + } + + @Suppress("NOT_YET_SUPPORTED_IN_INLINE") + fun applyQuickFixActionBase(action: QuickFixActionBase) { + if (!action.isAvailable(project, null, descriptor.psiElement.containingFile)) return + action.invoke(project, null, descriptor.psiElement.containingFile) + } + + + @Suppress("NOT_YET_SUPPORTED_IN_INLINE") + fun applyIntention() { + val action = this.action + when (action) { + is SelfTargetingIntention<*> -> applySelfTargetingIntention(action as SelfTargetingIntention) + is QuickFixActionBase<*> -> applyQuickFixActionBase(action) + } + } + + + if (this.startInWriteAction()) { + ApplicationManager.getApplication().runWriteAction(::applyIntention) + } else { + applyIntention() + } + + } + } + + ApplicationManager.getApplication().runWriteAction { + this.applyFix(project, descriptor) + } + } + + override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? { + val holder = ProblemsHolder(InspectionManager.getInstance(element.project), element.containingFile, false) + val visitor = inspection.buildVisitor( + holder, + false, + LocalInspectionToolSession(element.containingFile, 0, element.containingFile.endOffset) + ) + element.accept(visitor) + if (!holder.hasResults()) return null + return { + holder.results.clear() + element.accept(visitor) + if (holder.hasResults()) { + holder.results + .filter { acceptInformationLevel || it.highlightType != ProblemHighlightType.INFORMATION } + .forEach { it.fixes?.firstOrNull()?.applyFixSmart(element.project, it) } + } + } + } + }) + } + + private inline fun + > registerInspectionBasedProcessing( + + inspection: TInspection, + acceptInformationLevel: Boolean = false + ) { + _processings.add(object : J2kPostProcessing { + // Inspection can either need or not need write action + override val writeActionNeeded = inspection.startFixInWriteAction + + private fun isApplicable(element: TElement): Boolean { + if (!inspection.isApplicable(element)) return false + return acceptInformationLevel || inspection.inspectionHighlightType(element) != ProblemHighlightType.INFORMATION + } + + override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? { + if (!TElement::class.java.isInstance(element)) return null + val tElement = element as TElement + if (!isApplicable(tElement)) return null + return { + if (isApplicable(tElement)) { // check availability of the inspection again because something could change + inspection.applyTo(inspection.inspectionTarget(tElement)) + } + } + } + }) + } + + private inline fun registerDiagnosticBasedProcessing( + vararg diagnosticFactory: DiagnosticFactory<*>, + crossinline fix: (TElement, Diagnostic) -> Unit + ) { + registerDiagnosticBasedProcessingFactory(*diagnosticFactory) { element: TElement, diagnostic: Diagnostic -> { fix(element, diagnostic) } } + } + + private inline fun registerDiagnosticBasedProcessingFactory( + vararg diagnosticFactory: DiagnosticFactory<*>, + crossinline fixFactory: (TElement, Diagnostic) -> (() -> Unit)? + ) { + _processings.add(object : J2kPostProcessing { + // ??? + override val writeActionNeeded = true + + override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? { + if (!TElement::class.java.isInstance(element)) return null + val diagnostic = diagnostics.forElement(element).firstOrNull { it.factory in diagnosticFactory } ?: return null + return fixFactory(element as TElement, diagnostic) + } + }) + } + + private class RemoveExplicitPropertyType : J2kPostProcessing { + override val writeActionNeeded = true + + override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? { + if (element !is KtProperty) return null + + fun check(element: KtProperty): Boolean { + val initializer = element.initializer ?: return false + val withoutExpectedType = initializer.analyzeInContext(initializer.getResolutionScope()) + val descriptor = element.resolveToDescriptorIfAny() as? CallableDescriptor ?: return false + return when (withoutExpectedType.getType(initializer)) { + descriptor.returnType, + descriptor.returnType?.makeNotNullable() -> true + else -> false + } + } + + if (!check(element)) { + return null + } else { + return { + if (element.isValid && check(element)) { + element.typeReference = null + } + } + } + } + } + + + private class RemoveRedundantNullability : J2kPostProcessing { + override val writeActionNeeded: Boolean = true + + override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? { + if (element !is KtProperty) return null + + fun check(element: KtProperty): Boolean { + if (!element.isLocal) return false + val typeReference = element.typeReference + if (typeReference == null || typeReference.typeElement !is KtNullableType) return false + + return ReferencesSearch.search(element, element.useScope).findAll().mapNotNull { ref -> + val parent = (ref.element.parent as? KtExpression)?.asAssignment() + parent?.takeIf { it.left == ref.element } + }.all { + val right = it.right + val withoutExpectedType = right?.analyzeInContext(element.getResolutionScope()) + withoutExpectedType?.getType(right)?.isNullable() == false + } + } + + if (!check(element)) { + return null + } else { + return { + val typeElement = element.typeReference?.typeElement + if (element.isValid && check(element) && typeElement != null && typeElement is KtNullableType) { + typeElement.replace(typeElement.innerType!!) + } + } + } + } + } + + private class RemoveExplicitTypeArgumentsProcessing : J2kPostProcessing { + override val writeActionNeeded = true + + override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? { + if (element !is KtTypeArgumentList || !RemoveExplicitTypeArgumentsIntention.isApplicableTo(element, approximateFlexible = true)) return null + + return { + if (RemoveExplicitTypeArgumentsIntention.isApplicableTo(element, approximateFlexible = true)) { + element.delete() + } + } + } + } + + private class RemoveRedundantOverrideVisibilityProcessing : J2kPostProcessing { + override val writeActionNeeded = true + + override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? { + if (element !is KtCallableDeclaration || !element.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return null + val modifier = element.visibilityModifierType() ?: return null + return { element.setVisibility(modifier) } + } + } + + private class ConvertToStringTemplateProcessing : J2kPostProcessing { + override val writeActionNeeded = true + + private val intention = ConvertToStringTemplateIntention() + + override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? { + if (element is KtBinaryExpression && intention.isApplicableTo(element) && ConvertToStringTemplateIntention.shouldSuggestToConvert(element)) { + return { intention.applyTo(element, null) } + } + else { + return null + } + } + } + + private class UsePropertyAccessSyntaxProcessing : J2kPostProcessing { + override val writeActionNeeded = true + + private val intention = UsePropertyAccessSyntaxIntention() + + override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? { + if (element !is KtCallExpression) return null + val propertyName = intention.detectPropertyNameToUse(element) ?: return null + return { intention.applyTo(element, propertyName, reformat = true) } + } + } + + private class RemoveRedundantSamAdaptersProcessing : J2kPostProcessing { + override val writeActionNeeded = true + + override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? { + if (element !is KtCallExpression) return null + + val expressions = RedundantSamConstructorInspection.samConstructorCallsToBeConverted(element) + if (expressions.isEmpty()) return null + + return { + RedundantSamConstructorInspection.samConstructorCallsToBeConverted(element) + .forEach { RedundantSamConstructorInspection.replaceSamConstructorCall(it) } + } + } + } + + private class UseExpressionBodyProcessing : J2kPostProcessing { + override val writeActionNeeded = true + + override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? { + if (element !is KtPropertyAccessor) return null + + val inspection = UseExpressionBodyInspection(convertEmptyToUnit = false) + if (!inspection.isActiveFor(element)) return null + + return { + if (inspection.isActiveFor(element)) { + inspection.simplify(element, false) + } + } + } + } + + private class RemoveRedundantCastToNullableProcessing : J2kPostProcessing { + override val writeActionNeeded = true + + override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? { + if (element !is KtBinaryExpressionWithTypeRHS) return null + + val context = element.analyze() + val leftType = context.getType(element.left) ?: return null + val rightType = context.get(BindingContext.TYPE, element.right) ?: return null + + if (!leftType.isMarkedNullable && rightType.isMarkedNullable) { + return { + val type = element.right?.typeElement as? KtNullableType + type?.replace(type.innerType!!) + } + } + + return null + } + } + + private class FixObjectStringConcatenationProcessing : J2kPostProcessing { + override val writeActionNeeded = true + + override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? { + if (element !is KtBinaryExpression || + element.operationToken != KtTokens.PLUS || + diagnostics.forElement(element.operationReference).none { + it.factory == Errors.UNRESOLVED_REFERENCE_WRONG_RECEIVER + || it.factory == Errors.NONE_APPLICABLE + }) + return null + + val bindingContext = element.analyze() + val rightType = element.right?.getType(bindingContext) ?: return null + + if (KotlinBuiltIns.isString(rightType)) { + return { + val factory = KtPsiFactory(element) + element.left!!.replace(factory.buildExpression { + appendFixedText("(") + appendExpression(element.left) + appendFixedText(").toString()") + }) + } + } + return null + } + } + + private class UninitializedVariableReferenceFromInitializerToThisReferenceProcessing : J2kPostProcessing { + override val writeActionNeeded = true + + 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 val writeActionNeeded = true + + 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/tests/org/jetbrains/kotlin/j2k/AbstractNewJavaToKotlinConverterSingleFileTest.kt b/j2k/tests/org/jetbrains/kotlin/j2k/AbstractNewJavaToKotlinConverterSingleFileTest.kt index 62fde2f1071..916abde6b3a 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/AbstractNewJavaToKotlinConverterSingleFileTest.kt +++ b/j2k/tests/org/jetbrains/kotlin/j2k/AbstractNewJavaToKotlinConverterSingleFileTest.kt @@ -20,6 +20,7 @@ import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.project.Project import org.jetbrains.kotlin.idea.j2k.IdeaNewJavaToKotlinServices import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor +import org.jetbrains.kotlin.idea.j2k.NewJ2KPostProcessingRegistrarImpl import org.jetbrains.kotlin.psi.KtPsiFactory import java.io.File @@ -28,7 +29,7 @@ abstract class AbstractNewJavaToKotlinConverterSingleFileTest : AbstractJavaToKo override fun fileToKotlin(text: String, settings: ConverterSettings, project: Project): String { val file = createJavaFile(text) val factory = KtPsiFactory(project, true) - val postProcessor = J2kPostProcessor(true) + val postProcessor = J2kPostProcessor(true, NewJ2KPostProcessingRegistrarImpl) return NewJavaToKotlinConverter(project, settings, IdeaNewJavaToKotlinServices).filesToKotlin(listOf(file)).map {