New J2K: make post-processing aware of other files which are being converted

Before post-processing was able to handle only one converting file at once
So, some conversions (like (field, getter, setter) to Kotlin property)
was not able to work when converting class hierarchy was split into
multiple files.
Also, inferring nullability for a set of files was broken

#KT-19569 fixed
#KT-34266 fixed
#KT-32518 fixed
This commit is contained in:
Ilya Kirillov
2019-10-11 00:33:10 +03:00
parent 999918d499
commit b350515237
29 changed files with 498 additions and 165 deletions
@@ -32,8 +32,7 @@ import org.jetbrains.kotlin.idea.core.util.range
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.j2k.ConverterContext
import org.jetbrains.kotlin.j2k.PostProcessor
import org.jetbrains.kotlin.j2k.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
@@ -61,11 +60,15 @@ class J2kPostProcessor(private val formatCode: Boolean) : PostProcessor {
}
override fun doAdditionalProcessing(
file: KtFile,
target: JKPostProcessingTarget,
converterContext: ConverterContext?,
rangeMarker: RangeMarker?,
onPhaseChanged: ((Int, String) -> Unit)?
) =
) {
val (file, rangeMarker) = when (target) {
is JKPieceOfCodePostProcessingTarget -> target.file to target.rangeMarker
is JKMultipleFilesPostProcessingTarget -> target.files.single() to null
}
runBlocking(EDT.ModalityStateElement(ModalityState.defaultModalityState())) {
do {
var modificationStamp: Long? = file.modificationStamp
@@ -109,7 +112,7 @@ class J2kPostProcessor(private val formatCode: Boolean) : PostProcessor {
}
}
}
}
private data class ActionData(val element: KtElement, val action: () -> Unit, val priority: Int, val writeActionNeeded: Boolean)
@@ -57,7 +57,6 @@ import org.jetbrains.kotlin.idea.util.isRunningInCidrIde
import org.jetbrains.kotlin.j2k.*
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.UserDataProperty
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.io.File
import java.io.IOException
import java.util.*
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.j2k
import com.intellij.openapi.editor.RangeMarker
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.idea.util.application.runReadAction
@@ -29,14 +30,19 @@ class AfterConversionPass(val project: Project, val postProcessor: PostProcessor
range: TextRange?,
onPhaseChanged: ((Int, String) -> Unit)? = null
) {
val rangeMarker = if (range != null) {
val document = kotlinFile.viewProvider.document!!
val marker = runReadAction { document.createRangeMarker(range.startOffset, range.endOffset) }
marker.isGreedyToLeft = true
marker.isGreedyToRight = true
marker
} else null
postProcessor.doAdditionalProcessing(kotlinFile, converterContext, rangeMarker, onPhaseChanged)
postProcessor.doAdditionalProcessing(
when {
range != null -> JKPieceOfCodePostProcessingTarget(kotlinFile, range.toRangeMarker(kotlinFile))
else -> JKMultipleFilesPostProcessingTarget(listOf(kotlinFile))
},
converterContext,
onPhaseChanged
)
}
}
fun TextRange.toRangeMarker(file: KtFile): RangeMarker =
runReadAction { file.viewProvider.document!!.createRangeMarker(startOffset, endOffset) }.apply {
isGreedyToLeft = true
isGreedyToRight = true
}
@@ -26,12 +26,15 @@ import com.intellij.openapi.util.Computable
import com.intellij.psi.*
import com.intellij.psi.impl.source.DummyHolder
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.core.util.range
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.j2k.ast.Element
import org.jetbrains.kotlin.j2k.usageProcessing.ExternalCodeProcessor
import org.jetbrains.kotlin.j2k.usageProcessing.UsageProcessing
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import java.util.*
@@ -43,13 +46,37 @@ interface PostProcessor {
val phasesCount: Int
fun doAdditionalProcessing(
file: KtFile,
target: JKPostProcessingTarget,
converterContext: ConverterContext?,
rangeMarker: RangeMarker?,
onPhaseChanged: ((Int, String) -> Unit)?
)
}
sealed class JKPostProcessingTarget
data class JKPieceOfCodePostProcessingTarget(
val file: KtFile,
val rangeMarker: RangeMarker
) : JKPostProcessingTarget()
data class JKMultipleFilesPostProcessingTarget(
val files: List<KtFile>
) : JKPostProcessingTarget()
fun JKPostProcessingTarget.elements() = when (this) {
is JKPieceOfCodePostProcessingTarget -> runReadAction {
val range = rangeMarker.range ?: return@runReadAction emptyList()
file.elementsInRange(range)
}
is JKMultipleFilesPostProcessingTarget -> files
}
fun JKPostProcessingTarget.files() = when (this) {
is JKPieceOfCodePostProcessingTarget -> listOf(file)
is JKMultipleFilesPostProcessingTarget -> files
}
enum class ParseContext {
TOP_LEVEL,
CODE_BLOCK
@@ -270,7 +297,7 @@ interface WithProgressProcessor {
processItem: (TInputItem) -> TOutputItem
): List<TOutputItem>
fun updateState(fileIndex: Int, phase: Int, description: String)
fun updateState(fileIndex: Int?, phase: Int, description: String)
fun <T> process(action: () -> T): T
}
@@ -318,7 +345,7 @@ class OldWithProgressProcessor(private val progress: ProgressIndicator?, private
throw AbstractMethodError("Should not be called for old J2K")
}
override fun updateState(fileIndex: Int, phase: Int, description: String) {
override fun updateState(fileIndex: Int?, phase: Int, description: String) {
throw AbstractMethodError("Should not be called for old J2K")
}
}
@@ -22,7 +22,9 @@ import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isTrivialSta
import org.jetbrains.kotlin.idea.quickfix.*
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.j2k.ConverterContext
import org.jetbrains.kotlin.j2k.JKPostProcessingTarget
import org.jetbrains.kotlin.j2k.PostProcessor
import org.jetbrains.kotlin.j2k.files
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
@@ -42,23 +44,23 @@ class NewJ2kPostProcessor : PostProcessor {
override val phasesCount = processings.size
override fun doAdditionalProcessing(
file: KtFile,
target: JKPostProcessingTarget,
converterContext: ConverterContext?,
rangeMarker: RangeMarker?,
onPhaseChanged: ((Int, String) -> Unit)?
) {
for ((i, group) in processings.withIndex()) {
onPhaseChanged?.invoke(i + 1, group.description)
for (processing in group.processings) {
try {
processing.runProcessing(file, rangeMarker, converterContext as NewJ2kConverterContext)
processing.runProcessing(target, converterContext as NewJ2kConverterContext)
} catch (e: ProcessCanceledException) {
throw e
} catch (t: Throwable) {
LOG.error(t)
} finally {
commitFile(file)
target.files().forEach(::commitFile)
}
}
}
@@ -0,0 +1,42 @@
/*
* Copyright 2010-2019 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.postProcessing
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import org.jetbrains.kotlin.psi.KtElement
internal abstract class JKInMemoryFilesSearcher {
abstract fun search(element: KtElement): Iterable<PsiReference>
companion object {
fun create(files: List<PsiElement>) = when {
files.size == 1 -> JKSingleFileInMemoryFilesSearcher(files.single())
else -> JKMultipleFilesInMemoryFilesSearcher(files)
}
}
}
internal class JKSingleFileInMemoryFilesSearcher(private val scopeElement: PsiElement) : JKInMemoryFilesSearcher() {
override fun search(element: KtElement): Iterable<PsiReference> =
ReferencesSearch.search(element, LocalSearchScope(scopeElement))
}
// it does not cover all cases e.g, just-changed reference
// maybe the solution is to do searching manually
// firstly by-text and then resolving
internal class JKMultipleFilesInMemoryFilesSearcher(private val scopeElements: List<PsiElement>) : JKInMemoryFilesSearcher() {
override fun search(element: KtElement): Iterable<PsiReference> {
val result = mutableListOf<PsiReference>()
for (scopeElement in scopeElements) {
result += ReferencesSearch.search(element, LocalSearchScope(scopeElement))
}
return result
}
}
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.nj2k.postProcessing
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.RangeMarker
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
@@ -15,6 +16,7 @@ import org.jetbrains.kotlin.idea.core.util.range
import org.jetbrains.kotlin.idea.quickfix.AddExclExclCallFix
import org.jetbrains.kotlin.idea.quickfix.KotlinIntentionActionsFactory
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.psi.KtElement
@@ -23,7 +25,7 @@ import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class DiagnosticBasedPostProcessingGroup(diagnosticBasedProcessings: List<DiagnosticBasedProcessing>) : ProcessingGroup {
class DiagnosticBasedPostProcessingGroup(diagnosticBasedProcessings: List<DiagnosticBasedProcessing>) : FileBasedPostProcessing() {
constructor(vararg diagnosticBasedProcessings: DiagnosticBasedProcessing) : this(diagnosticBasedProcessings.toList())
private val diagnosticToFix =
@@ -33,8 +35,9 @@ class DiagnosticBasedPostProcessingGroup(diagnosticBasedProcessings: List<Diagno
list.map { it.second }
}
override fun runProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
val diagnostics = runReadAction { analyzeFileRange(file, rangeMarker).all() }
override fun runProcessing(file: KtFile, allFiles: List<KtFile>, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
val resolutionFacade = runReadAction { KotlinCacheService.getInstance(converterContext.project).getResolutionFacade(allFiles) }
val diagnostics = runReadAction { analyzeFileRange(file, rangeMarker, resolutionFacade).all() }
runUndoTransparentActionInEdt(inWriteAction = true) {
for (diagnostic in diagnostics) {
val range = rangeMarker?.range ?: file.textRange
@@ -49,7 +52,7 @@ class DiagnosticBasedPostProcessingGroup(diagnosticBasedProcessings: List<Diagno
}
}
private fun analyzeFileRange(file: KtFile, rangeMarker: RangeMarker?): Diagnostics {
private fun analyzeFileRange(file: KtFile, rangeMarker: RangeMarker?, resolutionFacade: ResolutionFacade): Diagnostics {
val elements = when {
rangeMarker == null -> listOf(file)
rangeMarker.isValid -> file.elementsInRange(rangeMarker.range!!).filterIsInstance<KtElement>()
@@ -57,7 +60,7 @@ class DiagnosticBasedPostProcessingGroup(diagnosticBasedProcessings: List<Diagno
}
return if (elements.isNotEmpty())
file.getResolutionFacade().analyzeWithAllCompilerChecks(elements).bindingContext.diagnostics
resolutionFacade.analyzeWithAllCompilerChecks(elements).bindingContext.diagnostics
else Diagnostics.EMPTY
}
}
@@ -18,15 +18,13 @@ import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.utils.mapToIndex
import java.util.*
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf
class InspectionLikeProcessingGroup(
private val runSingleTime: Boolean = false,
private val acceptNonKtElements: Boolean = false,
private val processings: List<InspectionLikeProcessing>
) : ProcessingGroup {
) : FileBasedPostProcessing() {
constructor(vararg processings: InspectionLikeProcessing) : this(
runSingleTime = false,
acceptNonKtElements = false,
@@ -35,8 +33,7 @@ class InspectionLikeProcessingGroup(
private val processingsToPriorityMap = processings.mapToIndex()
fun priority(processing: InspectionLikeProcessing): Int = processingsToPriorityMap.getValue(processing)
override fun runProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
override fun runProcessing(file: KtFile, allFiles: List<KtFile>, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
do {
var modificationStamp: Long? = file.modificationStamp
val elementToActions = runReadAction {
@@ -7,27 +7,35 @@ package org.jetbrains.kotlin.nj2k.postProcessing
import com.intellij.openapi.editor.RangeMarker
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.core.util.range
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.j2k.JKMultipleFilesPostProcessingTarget
import org.jetbrains.kotlin.j2k.JKPieceOfCodePostProcessingTarget
import org.jetbrains.kotlin.j2k.JKPostProcessingTarget
import org.jetbrains.kotlin.j2k.elements
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
interface GeneralPostProcessing {
fun runProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext)
fun runProcessing(target: JKPostProcessingTarget, converterContext: NewJ2kConverterContext)
}
abstract class FileBasedPostProcessing : GeneralPostProcessing {
final override fun runProcessing(target: JKPostProcessingTarget, converterContext: NewJ2kConverterContext) = when (target) {
is JKPieceOfCodePostProcessingTarget ->
runProcessing(target.file, listOf(target.file), target.rangeMarker, converterContext)
is JKMultipleFilesPostProcessingTarget ->
target.files.forEach { file ->
runProcessing(file, target.files, rangeMarker = null, converterContext = converterContext)
}
}
abstract fun runProcessing(file: KtFile, allFiles: List<KtFile>, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext)
}
abstract class ElementsBasedPostProcessing : GeneralPostProcessing {
final override fun runProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
val elements = runReadAction {
rangeMarker?.let { marker ->
val range = marker.range ?: return@let emptyList()
file.elementsInRange(range)
} ?: listOf(file)
}
runProcessing(elements, converterContext)
final override fun runProcessing(target: JKPostProcessingTarget, converterContext: NewJ2kConverterContext) {
runProcessing(target.elements(), converterContext)
}
abstract fun runProcessing(elements: List<PsiElement>, converterContext: NewJ2kConverterContext)
@@ -8,9 +8,9 @@ package org.jetbrains.kotlin.nj2k.postProcessing.processings
import com.intellij.psi.PsiElement
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.OverridingMethodsSearch
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.refactoring.rename.RenamePsiElementProcessor
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
@@ -22,17 +22,21 @@ import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
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.util.CommentSaver
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.nj2k.*
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.asGetterName
import org.jetbrains.kotlin.nj2k.asSetterName
import org.jetbrains.kotlin.nj2k.externalCodeProcessing.JKFakeFieldData
import org.jetbrains.kotlin.nj2k.externalCodeProcessing.JKFieldData
import org.jetbrains.kotlin.nj2k.externalCodeProcessing.JKMethodData
import org.jetbrains.kotlin.nj2k.externalCodeProcessing.NewExternalCodeProcessing
import org.jetbrains.kotlin.nj2k.fqNameWithoutCompanions
import org.jetbrains.kotlin.nj2k.postProcessing.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
@@ -51,16 +55,59 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.utils.mapToIndex
class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing() {
override fun runProcessing(elements: List<PsiElement>, converterContext: NewJ2kConverterContext) {
val ktElements = elements.filterIsInstance<KtElement>()
val resolutionFacade = runReadAction {
KotlinCacheService.getInstance(converterContext.project).getResolutionFacade(ktElements)
}
ConvertGettersAndSettersToPropertyStatefulProcessing(
resolutionFacade,
ktElements,
converterContext.externalCodeProcessor
).runProcessing()
}
}
private class ConvertGettersAndSettersToPropertyStatefulProcessing(
private val resolutionFacade: ResolutionFacade,
private val elements: List<KtElement>,
private val externalCodeUpdater: NewExternalCodeProcessing
) {
private val searcher = JKInMemoryFilesSearcher.create(elements)
fun runProcessing() {
val collectingState = CollectingState()
val classesWithPropertiesData = runReadAction {
elements
.descendantsOfType<KtClassOrObject>()
.sortedByInheritance()
.map { klass ->
klass to klass.collectPropertiesData(collectingState)
}
}
if (classesWithPropertiesData.isEmpty()) return
runUndoTransparentActionInEdt(inWriteAction = true) {
for ((klass, propertiesData) in classesWithPropertiesData) {
convertClass(klass, propertiesData)
}
}
}
private fun KtNamedFunction.hasOverrides(): Boolean =
toLightMethods().singleOrNull()?.let { lightMethod ->
OverridingMethodsSearch.search(lightMethod).findFirst()
} != null
if (OverridingMethodsSearch.search(lightMethod).findFirst() != null) return@let true
if (elements.size == 1) return@let false
elements.any { element ->
OverridingMethodsSearch.search(lightMethod, LocalSearchScope(element), true).findFirst() != null
}
} == true
private fun KtNamedFunction.hasSuperFunction(): Boolean =
resolveToDescriptorIfAny()?.original?.overriddenDescriptors?.isNotEmpty() == true
resolveToDescriptorIfAny(resolutionFacade)?.original?.overriddenDescriptors?.isNotEmpty() == true
private fun KtNamedFunction.hasInvalidSuperDescriptors(): Boolean =
resolveToDescriptorIfAny()
resolveToDescriptorIfAny(resolutionFacade)
?.original
?.overriddenDescriptors
?.any { it.isJavaDescriptor || it is FunctionDescriptor } == true
@@ -216,7 +263,7 @@ class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing
}
private fun KtElement.usages() =
ReferencesSearch.search(this, LocalSearchScope(containingKtFile))
searcher.search(this)
private fun <T : KtExpression> T.withReplacedExpressionInBody(
from: KtElement,
@@ -249,7 +296,7 @@ class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing
val sorted = mutableListOf<KtClassOrObject>()
val visited = Array(size) { false }
val descriptors = map { it.resolveToDescriptorIfAny()!! }
val descriptors = map { it.resolveToDescriptorIfAny(resolutionFacade)!! }
val descriptorToIndex = descriptors.mapToIndex()
val outers = descriptors.map { descriptor ->
descriptor.superClassAndSuperInterfaces().mapNotNull { descriptorToIndex[it] }
@@ -273,25 +320,6 @@ class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing
return sorted
}
override fun runProcessing(elements: List<PsiElement>, converterContext: NewJ2kConverterContext) {
val collectingState = CollectingState()
val classesWithPropertiesData = runReadAction {
elements
.descendantsOfType<KtClassOrObject>()
.sortedByInheritance()
.map { klass ->
klass to klass.collectPropertiesData(collectingState)
}
}
if (classesWithPropertiesData.isEmpty()) return
runUndoTransparentActionInEdt(inWriteAction = true) {
for ((klass, propertiesData) in classesWithPropertiesData) {
convertClass(klass, propertiesData, converterContext.externalCodeProcessor)
}
}
}
private fun causesNameConflictInCurrentDeclarationAndItsParents(name: String, declaration: DeclarationDescriptor?): Boolean =
when (declaration) {
is ClassDescriptor -> {
@@ -310,27 +338,11 @@ class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing
}
private fun calculatePropertyType(getter: KtNamedFunction?, setter: KtNamedFunction?): KotlinType? {
val getterType = getter?.resolveToDescriptorIfAny()?.returnType
val setterType = setter?.resolveToDescriptorIfAny()?.valueParameters?.singleOrNull()?.type
val getterType = getter?.resolveToDescriptorIfAny(resolutionFacade)?.returnType
val setterType = setter?.resolveToDescriptorIfAny(resolutionFacade)?.valueParameters?.singleOrNull()?.type
return calculatePropertyType(getterType, setterType)
}
private fun calculatePropertyTypeBasedOnSuperDescriptors(getter: KtNamedFunction?, setter: KtNamedFunction?): KotlinType? {
val superGetterType = getter
?.resolveToDescriptorIfAny()
?.overriddenDescriptors
?.firstOrNull()
?.returnType
val superSetterType = setter
?.resolveToDescriptorIfAny()
?.overriddenDescriptors
?.firstOrNull()
?.valueParameters
?.singleOrNull()
?.type
return calculatePropertyType(superGetterType, superSetterType)
}
private fun KtClassOrObject.collectPropertiesData(collectingState: CollectingState): List<PropertyData> {
return declarations
.asSequence()
@@ -356,7 +368,7 @@ class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing
val name = realGetter?.name ?: realSetter?.name!!
val superDeclarationOwner = (realGetter?.function ?: realSetter?.function)
?.resolveToDescriptorIfAny()
?.resolveToDescriptorIfAny(resolutionFacade)
?.overriddenDescriptors
?.firstOrNull()
?.findPsi()
@@ -378,7 +390,7 @@ class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing
klass: KtClassOrObject,
factory: KtPsiFactory
): List<PropertyWithAccessors> {
val classDescriptor = klass.resolveToDescriptorIfAny() ?: return emptyList()
val classDescriptor = klass.resolveToDescriptorIfAny(resolutionFacade) ?: return emptyList()
val variablesDescriptorsMap =
(listOfNotNull(classDescriptor.getSuperClassNotAny()) + classDescriptor.getSuperInterfaces())
@@ -462,7 +474,7 @@ class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing
val getter = realGetter ?: when {
realProperty?.property?.resolveToDescriptorIfAny()?.overriddenDescriptors?.any {
realProperty?.property?.resolveToDescriptorIfAny(resolutionFacade)?.overriddenDescriptors?.any {
it.safeAs<VariableDescriptor>()?.isVar == true
} == true -> FakeGetter(name, null, "")
@@ -487,7 +499,7 @@ class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing
realProperty?.property?.isVar == true ->
FakeSetter(name, null, "")
realProperty?.property?.resolveToDescriptorIfAny()?.overriddenDescriptors?.any {
realProperty?.property?.resolveToDescriptorIfAny(resolutionFacade)?.overriddenDescriptors?.any {
it.safeAs<VariableDescriptor>()?.isVar == true
} == true
|| variablesDescriptorsMap[name]?.isVar == true ->
@@ -523,7 +535,7 @@ class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing
}
private fun KtProperty.renameTo(newName: String, factory: KtPsiFactory) {
for (usage in usages().toList()) {
for (usage in usages()) {
val element = usage.element
val isBackingField = element is KtNameReferenceExpression
&& element.text == KtTokens.FIELD_KEYWORD.value
@@ -540,8 +552,7 @@ class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing
private fun convertClass(
klass: KtClassOrObject,
propertiesData: List<PropertyData>,
externalCodeUpdater: NewExternalCodeProcessing
propertiesData: List<PropertyData>
) {
val factory = KtPsiFactory(klass)
val accessors = propertiesData.filterGettersAndSetters(klass, factory)
@@ -5,9 +5,8 @@
package org.jetbrains.kotlin.nj2k.postProcessing.processings
import com.intellij.openapi.editor.RangeMarker
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.util.range
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
@@ -20,19 +19,16 @@ import org.jetbrains.kotlin.nj2k.inference.common.collectors.CommonConstraintsCo
import org.jetbrains.kotlin.nj2k.inference.common.collectors.FunctionConstraintsCollector
import org.jetbrains.kotlin.nj2k.inference.mutability.*
import org.jetbrains.kotlin.nj2k.inference.nullability.*
import org.jetbrains.kotlin.nj2k.postProcessing.GeneralPostProcessing
import org.jetbrains.kotlin.nj2k.postProcessing.ElementsBasedPostProcessing
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
abstract class InferenceProcessing : GeneralPostProcessing {
final override fun runProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
val elements = if (rangeMarker != null) {
val range = rangeMarker.range ?: return
runReadAction { file.elementsInRange(range) }.filterIsInstance<KtElement>()
} else listOf(file)
val resolutionFacade = runReadAction { file.getResolutionFacade() }
createInferenceFacade(resolutionFacade, converterContext).runOn(elements)
abstract class InferenceProcessing : ElementsBasedPostProcessing() {
override fun runProcessing(elements: List<PsiElement>, converterContext: NewJ2kConverterContext) {
val kotlinElements = elements.filterIsInstance<KtElement>()
val resolutionFacade = runReadAction {
KotlinCacheService.getInstance(converterContext.project).getResolutionFacade(kotlinElements)
}
createInferenceFacade(resolutionFacade, converterContext).runOn(kotlinElements)
}
abstract fun createInferenceFacade(
@@ -10,12 +10,13 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.nj2k.JKImportStorage
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.postProcessing.FileBasedPostProcessing
import org.jetbrains.kotlin.nj2k.postProcessing.GeneralPostProcessing
import org.jetbrains.kotlin.nj2k.postProcessing.runUndoTransparentActionInEdt
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtQualifiedExpression
class ShortenReferenceProcessing : GeneralPostProcessing {
class ShortenReferenceProcessing : FileBasedPostProcessing() {
private val filter = filter@{ element: PsiElement ->
when (element) {
is KtQualifiedExpression -> when {
@@ -26,7 +27,7 @@ class ShortenReferenceProcessing : GeneralPostProcessing {
}
}
override fun runProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
override fun runProcessing(file: KtFile, allFiles: List<KtFile>, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
runUndoTransparentActionInEdt(inWriteAction = false) {
if (rangeMarker != null) {
if (rangeMarker.isValid) {
@@ -13,16 +13,19 @@ import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.codeStyle.CodeStyleManager
import org.jetbrains.kotlin.idea.core.util.range
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.j2k.JKPostProcessingTarget
import org.jetbrains.kotlin.j2k.elements
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.asLabel
import org.jetbrains.kotlin.nj2k.postProcessing.FileBasedPostProcessing
import org.jetbrains.kotlin.nj2k.postProcessing.GeneralPostProcessing
import org.jetbrains.kotlin.nj2k.postProcessing.runUndoTransparentActionInEdt
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
class FormatCodeProcessing : GeneralPostProcessing {
override fun runProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
class FormatCodeProcessing : FileBasedPostProcessing() {
override fun runProcessing(file: KtFile, allFiles: List<KtFile>, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
val codeStyleManager = CodeStyleManager.getInstance(file.project)
runUndoTransparentActionInEdt(inWriteAction = true) {
if (rangeMarker != null) {
@@ -38,28 +41,30 @@ class FormatCodeProcessing : GeneralPostProcessing {
class ClearUnknownLabelsProcessing : GeneralPostProcessing {
override fun runProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
override fun runProcessing(target: JKPostProcessingTarget, converterContext: NewJ2kConverterContext) {
val comments = mutableListOf<PsiComment>()
runUndoTransparentActionInEdt(inWriteAction = true) {
file.accept(object : PsiElementVisitor() {
override fun visitElement(element: PsiElement) {
element.acceptChildren(this)
}
override fun visitComment(comment: PsiComment) {
if (comment.text.asLabel() != null) {
comments += comment
target.elements().forEach { element ->
element.accept(object : PsiElementVisitor() {
override fun visitElement(element: PsiElement) {
element.acceptChildren(this)
}
}
})
override fun visitComment(comment: PsiComment) {
if (comment.text.asLabel() != null) {
comments += comment
}
}
})
}
comments.forEach { it.delete() }
}
}
}
class OptimizeImportsProcessing : GeneralPostProcessing {
override fun runProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
class OptimizeImportsProcessing : FileBasedPostProcessing() {
override fun runProcessing(file: KtFile, allFiles: List<KtFile>, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
val elements = runReadAction {
when {
rangeMarker != null && rangeMarker.isValid -> file.elementsInRange(rangeMarker.range!!)
@@ -17,10 +17,8 @@
package org.jetbrains.kotlin.nj2k
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.module.Module
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
@@ -28,7 +26,6 @@ import com.intellij.openapi.util.Computable
import com.intellij.psi.*
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.j2k.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.nj2k.conversions.JKResolver
@@ -68,36 +65,27 @@ class NewJavaToKotlinConverter(
elementsToKotlin(files, withProgressProcessor)
})
val texts = results.mapIndexed { i, result ->
try {
val kotlinFile = ApplicationManager.getApplication().runReadAction(Computable {
KtPsiFactory(project).createFileWithLightClassSupport("dummy.kt", result!!.text, files[i])
})
ApplicationManager.getApplication().invokeAndWait {
CommandProcessor.getInstance().runUndoTransparentAction {
runWriteAction {
kotlinFile.addImports(result!!.importsToAdd)
}
}
val kotlinFiles = results.mapIndexed { i, result ->
runUndoTransparentActionInEdt(inWriteAction = true) {
val javaFile = files[i]
KtPsiFactory(project).createFileWithLightClassSupport(
javaFile.name.replace(".java", ".kt"),
result!!.text,
files[i]
).apply {
addImports(result.importsToAdd)
}
AfterConversionPass(project, postProcessor).run(
kotlinFile,
context,
range = null,
onPhaseChanged = { phase, description ->
withProgressProcessor.updateState(i, phase + 1, description)
}
)
kotlinFile.text
} catch (e: ProcessCanceledException) {
throw e
} catch (t: Throwable) {
LOG.error(t)
result!!.text
}
}
FilesResult(texts, externalCodeProcessing)
postProcessor.doAdditionalProcessing(
JKMultipleFilesPostProcessingTarget(kotlinFiles),
context
) { phase, description ->
withProgressProcessor.updateState(fileIndex = null, phase = phase + 1, description = description)
}
FilesResult(kotlinFiles.map { it.text }, externalCodeProcessing)
}
}
@@ -217,12 +205,13 @@ class NewJ2kWithProgressProcessor(
val DEFAULT = NewJ2kWithProgressProcessor(null, null, 0)
}
override fun updateState(fileIndex: Int, phase: Int, description: String) {
override fun updateState(fileIndex: Int?, phase: Int, description: String) {
progress?.checkCanceled()
progress?.fraction = phase / phasesCount.toDouble()
progress?.text = "$description - phase $phase of $phasesCount"
if (files != null && files.isNotEmpty()) {
progress?.text2 = files[fileIndex].virtualFile.presentableUrl
progress?.text2 = when {
files != null && files.isNotEmpty() && fileIndex != null -> files[fileIndex].virtualFile.presentableUrl
else -> ""
}
}
@@ -7,11 +7,14 @@ package org.jetbrains.kotlin.nj2k
import com.intellij.codeInsight.generation.GenerateEqualsHelper.getEqualsSignature
import com.intellij.lang.jvm.JvmClassKind
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.command.CommandProcessor
import com.intellij.psi.*
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.InheritanceUtil
import com.intellij.psi.util.MethodSignatureUtil
import com.intellij.psi.util.PsiUtil
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.j2k.ClassKind
import org.jetbrains.kotlin.j2k.ReferenceSearcher
import org.jetbrains.kotlin.j2k.isNullLiteral
@@ -141,3 +144,16 @@ val KtDeclaration.fqNameWithoutCompanions
.filter { it.safeAs<KtObjectDeclaration>()?.isCompanion() != true && it.name != null }
.toList()
.foldRight(containingKtFile.packageFqName) { container, acc -> acc.child(Name.identifier(container.name!!)) }
internal fun <T> runUndoTransparentActionInEdt(inWriteAction: Boolean, action: () -> T): T {
var result: T? = null
ApplicationManager.getApplication().invokeAndWait {
CommandProcessor.getInstance().runUndoTransparentAction {
result = when {
inWriteAction -> runWriteAction(action)
else -> action()
}
}
}
return result!!
}
@@ -0,0 +1,31 @@
package test;
public class B {
public String getFromB1() {
return "";
}
public String getFromB2() {
return "";
}
public void setFromB2(String value) {
}
public String getFromB3() {
return "";
}
public void setFromB3(String value) {
}
public String getFromB4() {
return "";
}
public void setFromB4(String value) {
}
public void setFromB5(String value) {
}
}
@@ -0,0 +1,20 @@
package test
open class B {
val fromB1: String
get() = ""
var fromB2: String?
get() = ""
set(value) {}
var fromB3: String?
get() = ""
set(value) {}
var fromB4: String?
get() = ""
set(value) {}
open fun setFromB5(value: String?) {}
}
@@ -0,0 +1,57 @@
package test;
public abstract class C extends B implements I {
private final int mySomething1;
private int mySomething6;
C(int something1) {
mySomething1 = something1;
}
@Override
public int getSomething1() {
return mySomething1;
}
@Override
public int getSomething2() {
return 0;
}
@Override
public int getSomething3() {
return 0;
}
@Override
public void setSomething3(int value) {
}
@Override
public int getSomething4() {
return 0;
}
@Override
public void setSomething5(int value) {
}
public int getSomething6() {
return mySomething6;
}
@Override
public void setSomething6(int value) {
mySomething6 = value;
}
public String getFromB5() {
return "";
}
@Override
public void setFromB5(String value) {
super.setFromB5(value);
}
}
@@ -0,0 +1,34 @@
package test
abstract class C internal constructor(override val something1: Int) : B(), I {
private var mySomething6 = 0
override val something2: Int
get() = 0
override var something3: Int
get() = 0
set(value) {}
override fun getSomething4(): Int {
return 0
}
override fun setSomething5(value: Int) {}
fun getSomething6(): Int {
return mySomething6
}
override fun setSomething6(value: Int) {
mySomething6 = value
}
fun getFromB5(): String {
return ""
}
override fun setFromB5(value: String?) {
super.setFromB5(value)
}
}
@@ -0,0 +1,21 @@
package test;
public interface I {
int getSomething1();
int getSomething2();
int getSomething3();
void setSomething3(int value);
int getSomething4();
void setSomething4(int value);
int getSomething5();
void setSomething5(int value);
void setSomething6(int value);
}
@@ -0,0 +1,13 @@
package test
interface I {
val something1: Int
val something2: Int
var something3: Int
fun getSomething4(): Int
fun setSomething4(value: Int)
fun getSomething5(): Int
fun setSomething5(value: Int)
fun setSomething6(value: Int)
}
@@ -0,0 +1,7 @@
package test;
public interface I1 extends I {
void setSomething1(int value);
int getSomething6();
}
@@ -0,0 +1,6 @@
package test
interface I1 : I {
fun setSomething1(value: Int)
val something6: Int
}
@@ -0,0 +1,5 @@
package test;
public interface IUser {
String getName();
}
@@ -0,0 +1,5 @@
package test
interface IUser {
val name: String
}
@@ -0,0 +1,14 @@
package test;
public class UserImpl implements IUser {
private final String name;
public UserImpl(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
}
@@ -0,0 +1,3 @@
package test
class UserImpl(override val name: String) : IUser
@@ -28,9 +28,11 @@ abstract class AbstractNewJavaToKotlinConverterMultiFileTest : AbstractJavaToKot
psiManager.findFile(virtualFile) as PsiJavaFile
}
val externalFiles = File(dirPath + File.separator + "external").listFiles { _, name ->
name.endsWith(".java") || name.endsWith(".kt")
}!!
val externalFiles = File(dirPath + File.separator + "external")
.takeIf { it.exists() }
?.listFiles { _, name ->
name.endsWith(".java") || name.endsWith(".kt")
}.orEmpty()
val externalPsiFiles = externalFiles.map { file ->
val virtualFile = addFile(file, "test")
@@ -34,6 +34,11 @@ public class NewJavaToKotlinConverterMultiFileTestGenerated extends AbstractNewJ
runTest("nj2k/testData/multiFile/AnnotationWithArrayParameter/");
}
@TestMetadata("DetectPropertiesMultipleFiles")
public void testDetectPropertiesMultipleFiles() throws Exception {
runTest("nj2k/testData/multiFile/DetectPropertiesMultipleFiles/");
}
@TestMetadata("FieldToProperty")
public void testFieldToProperty() throws Exception {
runTest("nj2k/testData/multiFile/FieldToProperty/");
@@ -49,6 +54,11 @@ public class NewJavaToKotlinConverterMultiFileTestGenerated extends AbstractNewJ
runTest("nj2k/testData/multiFile/GetterAndSetterUsages/");
}
@TestMetadata("InterfaceWithGetterInOtherFile")
public void testInterfaceWithGetterInOtherFile() throws Exception {
runTest("nj2k/testData/multiFile/InterfaceWithGetterInOtherFile/");
}
@TestMetadata("KT11952")
public void testKT11952() throws Exception {
runTest("nj2k/testData/multiFile/KT11952/");