New J2K: implement mutability inference in post-processing

#KT-24293 fixed
#KT-19603 fixed
This commit is contained in:
Ilya Kirillov
2019-09-02 18:56:50 +03:00
parent c28515be59
commit f61bb5aa39
81 changed files with 1263 additions and 231 deletions
@@ -143,6 +143,7 @@ import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.inference.common.AbstractCommonConstraintCollectorTest
import org.jetbrains.kotlin.nj2k.inference.mutability.AbstractMutabilityInferenceTest
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
@@ -1062,6 +1063,9 @@ fun main(args: Array<String>) {
testClass<AbstractNullabilityInferenceTest> {
model("inference/nullability")
}
testClass<AbstractMutabilityInferenceTest> {
model("inference/mutability")
}
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
model("copyPaste", pattern = """^([^\.]+)\.java$""")
}
@@ -161,6 +161,7 @@ import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.inference.common.AbstractCommonConstraintCollectorTest
import org.jetbrains.kotlin.nj2k.inference.mutability.AbstractMutabilityInferenceTest
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
@@ -1005,6 +1006,9 @@ fun main(args: Array<String>) {
testClass<AbstractNullabilityInferenceTest> {
model("inference/nullability")
}
testClass<AbstractMutabilityInferenceTest> {
model("inference/mutability")
}
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
model("copyPaste", pattern = """^([^\.]+)\.java$""")
}
@@ -145,6 +145,7 @@ import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.inference.common.AbstractCommonConstraintCollectorTest
import org.jetbrains.kotlin.nj2k.inference.mutability.AbstractMutabilityInferenceTest
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
@@ -969,6 +970,9 @@ fun main(args: Array<String>) {
testClass<AbstractNullabilityInferenceTest> {
model("inference/nullability")
}
testClass<AbstractMutabilityInferenceTest> {
model("inference/mutability")
}
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
model("copyPaste", pattern = """^([^\.]+)\.java$""")
}
@@ -145,6 +145,7 @@ import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.inference.common.AbstractCommonConstraintCollectorTest
import org.jetbrains.kotlin.nj2k.inference.mutability.AbstractMutabilityInferenceTest
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
@@ -965,6 +966,9 @@ fun main(args: Array<String>) {
testClass<AbstractNullabilityInferenceTest> {
model("inference/nullability")
}
testClass<AbstractMutabilityInferenceTest> {
model("inference/mutability")
}
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
model("copyPaste", pattern = """^([^\.]+)\.java$""")
}
@@ -145,6 +145,7 @@ import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.inference.common.AbstractCommonConstraintCollectorTest
import org.jetbrains.kotlin.nj2k.inference.mutability.AbstractMutabilityInferenceTest
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
@@ -965,6 +966,9 @@ fun main(args: Array<String>) {
testClass<AbstractNullabilityInferenceTest> {
model("inference/nullability")
}
testClass<AbstractMutabilityInferenceTest> {
model("inference/mutability")
}
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
model("copyPaste", pattern = """^([^\.]+)\.java$""")
}
@@ -88,9 +88,7 @@ open class BoundTypeCalculatorImpl(
valueParameters.map { parameter ->
parameter.typeReference?.typeElement?.let { typeElement ->
inferenceContext.typeElementToTypeVariable[typeElement]
}?.let { typeVariable ->
typeVariable.asBoundType()
} ?: return null
}?.asBoundType() ?: return null
}
} else {
descriptor.valueParameters.map { parameter ->
@@ -220,7 +218,12 @@ open class BoundTypeCalculatorImpl(
)
is TypeParameterDescriptor -> {
val containingDeclaration = target.containingDeclaration
val containingDeclaration = target.containingDeclaration.let { container ->
when (container) {
is TypeAliasDescriptor -> container.classDescriptor
else -> container
} ?: container
}
when {
containingDeclaration == call?.candidateDescriptor?.original -> {
val returnTypeVariable = inferenceContext.typeElementToTypeVariable[
@@ -240,7 +243,7 @@ open class BoundTypeCalculatorImpl(
contextBoundType?.isReferenceToClass == true ->
contextBoundType.typeParameters.getOrNull(target.index)?.boundType
// `this` or `super` call case
// `this`, `super`, or constructor call cases
containingDeclaration == call?.candidateDescriptor.safeAs<ConstructorDescriptor>()?.constructedClass -> {
val returnTypeVariable = inferenceContext.typeElementToTypeVariable[
call?.call?.typeArguments?.getOrNull(target.index)?.typeReference?.typeElement
@@ -54,8 +54,15 @@ class LiteralBound private constructor(val state: State) : ConstraintBound() {
}
}
fun State.constraintBound(): LiteralBound = when (this) {
fun State.constraintBound(): LiteralBound? = when (this) {
State.LOWER -> LiteralBound.LOWER
State.UPPER -> LiteralBound.UPPER
State.UNKNOWN -> LiteralBound.UNKNOWN
State.UNUSED -> null
}
val ConstraintBound.isUnused
get() = when (this) {
is TypeVariableBound -> typeVariable.state == State.UNUSED
is LiteralBound -> state == State.UNUSED
}
@@ -40,7 +40,11 @@ class ConstraintBuilder(
fun KtExpression.isTheSameTypeAs(other: State, priority: ConstraintPriority) {
boundType().label.safeAs<TypeVariableLabel>()?.typeVariable?.let { typeVariable ->
constraints += EqualsConstraint(typeVariable.constraintBound(), other.constraintBound(), priority)
constraints += EqualsConstraint(
typeVariable.constraintBound(),
other.constraintBound() ?: return@let,
priority
)
}
}
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
abstract class ContextCollector(private val resolutionFacade: ResolutionFacade) {
@@ -48,11 +47,7 @@ abstract class ContextCollector(private val resolutionFacade: ResolutionFacade)
val typeElement = typeElement ?: return null
val classReference = classReference() ?: NoClassReference
val state = when {
classReference.descriptor?.defaultType?.isUnit() == true -> State.LOWER
defaultState != null -> defaultState
else -> classReference.getState(typeElement)
}
val state = defaultState ?: classReference.getState(typeElement)
val typeArguments =
if (classReference is DescriptorClassReference) {
typeElement.typeArgumentsAsTypes.zip(
@@ -65,22 +60,15 @@ abstract class ContextCollector(private val resolutionFacade: ResolutionFacade)
}
} else emptyList()
return if (state == null) {
BoundTypeImpl(
GenericLabel(classReference),
typeArguments
)
} else {
val typeVariable = TypeElementBasedTypeVariable(
classReference,
typeArguments,
typeElement.toData() ?: return null,
owner,
state
)
typeElementToTypeVariable[typeElement] = typeVariable
typeVariable.asBoundType()
}
val typeVariable = TypeElementBasedTypeVariable(
classReference,
typeArguments,
typeElement.toData() ?: return null,
owner,
state
)
typeElementToTypeVariable[typeElement] = typeVariable
return typeVariable.asBoundType()
}
fun KotlinType.toBoundType(): BoundType? {
@@ -97,24 +85,18 @@ abstract class ContextCollector(private val resolutionFacade: ResolutionFacade)
}
} else emptyList()
return if (state == null) {
BoundTypeImpl(
GenericLabel(classReference),
typeArguments
)
} else {
val typeVariable = TypeBasedTypeVariable(
classReference,
typeArguments,
this,
state
)
typeBasedTypeVariables += typeVariable
typeVariable.asBoundType()
}
val typeVariable = TypeBasedTypeVariable(
classReference,
typeArguments,
this,
state
)
typeBasedTypeVariables += typeVariable
return typeVariable.asBoundType()
}
val substitutors = mutableMapOf<ClassDescriptor, ClassSubstitutor>()
val substitutors = mutableMapOf<ClassDescriptor, SuperTypesSubstitutor>()
fun isOrAsExpression(typeReference: KtTypeReference) {
val typeElement = typeReference.typeElement ?: return
@@ -160,7 +142,7 @@ abstract class ContextCollector(private val resolutionFacade: ResolutionFacade)
val descriptor =
expression.resolveToDescriptorIfAny(resolutionFacade) ?: return@forEachDescendantOfType
substitutors[descriptor] =
ClassSubstitutor.createFromKtClass(expression, resolutionFacade) ?: return@forEachDescendantOfType
SuperTypesSubstitutor.createFromKtClass(expression, resolutionFacade) ?: return@forEachDescendantOfType
for (typeParameter in expression.typeParameters) {
val typeVariable = typeParameter.resolveToDescriptorIfAny(resolutionFacade)
?.safeAs<TypeParameterDescriptor>()
@@ -206,5 +188,5 @@ abstract class ContextCollector(private val resolutionFacade: ResolutionFacade)
)
}
abstract fun ClassReference.getState(typeElement: KtTypeElement?): State?
abstract fun ClassReference.getState(typeElement: KtTypeElement?): State
}
@@ -35,6 +35,7 @@ class DebugPrinter(private val inferenceContext: InferenceContext) {
State.LOWER -> "L"
State.UPPER -> "U"
State.UNKNOWN -> "?"
State.UNUSED -> "$"
}
fun BoundType.asString(): String = buildString {
@@ -71,11 +72,10 @@ class DebugPrinter(private val inferenceContext: InferenceContext) {
private class Namer(inferenceContext: InferenceContext) {
val names = inferenceContext.typeVariables.mapIndexed { index, typeVariable ->
private val names = inferenceContext.typeVariables.mapIndexed { index, typeVariable ->
typeVariable to "T$index"
}.toMap()
fun name(typeVariable: TypeVariable): String =
names.getValue(typeVariable)
}
@@ -19,7 +19,7 @@ data class InferenceContext(
val typeElementToTypeVariable: Map<KtTypeElement, TypeVariable>,
val declarationToTypeVariable: Map<KtNamedDeclaration, TypeVariable>,
val declarationDescriptorToTypeVariable: Map<DeclarationDescriptor, TypeVariable>,
val classSubstitutions: Map<ClassDescriptor, ClassSubstitutor>
val superTypesSubstitutions: Map<ClassDescriptor, SuperTypesSubstitutor>
) {
fun isInConversionScope(childCandidate: PsiElement) = when (childCandidate) {
@@ -25,7 +25,9 @@ internal class Solver(
println()
println("type variables:")
for (typeVariable in inferenceContext.typeVariables) {
println("${typeVariable.name} := ${typeVariable.state}")
if (typeVariable.state != State.UNUSED) {
println("${typeVariable.name} := ${typeVariable.state}")
}
}
println("---------------\n")
}
@@ -33,6 +35,7 @@ internal class Solver(
fun solveConstraints(constraints: List<Constraint>) {
val mutableConstraints = constraints.toMutableList()
mutableConstraints.filterOutConstraintsWithUnusedState()
var currentStep = ConstraintPriority.values().first()
var i = 0
@@ -144,11 +147,11 @@ internal class Solver(
val (lower, upper) = constraint
if (lower is TypeVariableBound && lower.typeVariable.isFixed) {
somethingChanged = true
constraint.subtype = lower.typeVariable.state.constraintBound()
constraint.subtype = lower.typeVariable.state.constraintBound() ?: continue
}
if (upper is TypeVariableBound && upper.typeVariable.isFixed) {
somethingChanged = true
constraint.supertype = upper.typeVariable.state.constraintBound()
constraint.supertype = upper.typeVariable.state.constraintBound() ?: continue
}
}
}
@@ -190,5 +193,14 @@ internal class Solver(
}
return null
}
private fun MutableList<Constraint>.filterOutConstraintsWithUnusedState() {
removeIf { constraint ->
when (constraint) {
is SubtypeConstraint -> constraint.subtype.isUnused || constraint.supertype.isUnused
is EqualsConstraint -> constraint.left.isUnused || constraint.right.isUnused
}
}
}
}
@@ -6,7 +6,14 @@
package org.jetbrains.kotlin.nj2k.inference.common
enum class State {
// lower state in the terms of subtyping relation,
// e.g., for nullability this is not null type as T <: T?
// for mutability this is MutableCollection as MutableCollection <: Collection
LOWER,
// the same as with lower but upper
UPPER,
UNKNOWN
// the type variable state is needed to be calculated
UNKNOWN,
// we don't need to infer state of that type variable
UNUSED
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.nj2k.inference.common
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
abstract class StateUpdater {
@@ -14,12 +15,14 @@ abstract class StateUpdater {
val deepComparator = Comparator<TypeElementBasedTypeVariable> { o1, o2 ->
if (o1.typeElement.typeElement.isAncestor(o2.typeElement.typeElement)) 1 else -1
}
for (typeVariable in inferenceContext
.typeVariables
val typeVariablesSortedByDeep = inferenceContext.typeVariables
.filterIsInstance<TypeElementBasedTypeVariable>()
.sortedWith(deepComparator)
) {
typeVariable.updateState()
runWriteAction {
for (typeVariable in typeVariablesSortedByDeep) {
typeVariable.updateState()
}
}
}
@@ -14,12 +14,12 @@ import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtTypeElement
import org.jetbrains.kotlin.resolve.BindingContext
class ClassSubstitutor(private val superTypes: Map<ClassifierDescriptor, Map<TypeParameterDescriptor, KtTypeElement>>) {
class SuperTypesSubstitutor(private val superTypes: Map<ClassifierDescriptor, Map<TypeParameterDescriptor, KtTypeElement>>) {
operator fun get(klass: ClassDescriptor, typeParameter: TypeParameterDescriptor): KtTypeElement? =
superTypes[klass]?.get(typeParameter)
companion object {
fun createFromKtClass(klass: KtClassOrObject, resolutionFacade: ResolutionFacade): ClassSubstitutor? {
fun createFromKtClass(klass: KtClassOrObject, resolutionFacade: ResolutionFacade): SuperTypesSubstitutor? {
val superTypes =
klass.superTypeListEntries.map { superType ->
val typeReference = superType.typeReference ?: return null
@@ -31,7 +31,7 @@ class ClassSubstitutor(private val superTypes: Map<ClassifierDescriptor, Map<Typ
parameter to (argument.typeElement ?: return null)
}.toMap()
}.toMap()
return ClassSubstitutor(superTypes)
return SuperTypesSubstitutor(superTypes)
}
}
}
@@ -10,10 +10,7 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.nj2k.inference.common.BoundTypeCalculator
import org.jetbrains.kotlin.nj2k.inference.common.ConstraintBuilder
import org.jetbrains.kotlin.nj2k.inference.common.ConstraintPriority
import org.jetbrains.kotlin.nj2k.inference.common.InferenceContext
import org.jetbrains.kotlin.nj2k.inference.common.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.asAssignment
import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunction
@@ -108,8 +105,9 @@ class CommonConstraintsCollector : ConstraintsCollector() {
contextBoundType = loopRangeBoundType,
inferenceContext = inferenceContext
) ?: return
loopParameterTypeVariable.isSubtypeOf(
boundType.typeParameters.firstOrNull()?.boundType ?: return,
boundType.typeParameters.firstOrNull()?.boundType?.isSubtypeOf(
loopParameterTypeVariable.asBoundType(),
ConstraintPriority.ASSIGNMENT
)
}
@@ -131,5 +129,6 @@ class CommonConstraintsCollector : ConstraintsCollector() {
?.firstOrNull { it.valueParameters.isEmpty() }
?.original
?.returnType
}
}
@@ -34,7 +34,7 @@ class FunctionConstraintsCollector(
val classDescriptor = ktClass.resolveToDescriptorIfAny(resolutionFacade) ?: return
val superFunctions = superFunctionsProvider.provideSuperFunctionDescriptors(element) ?: return
val substitutor = inferenceContext.classSubstitutions[classDescriptor] ?: return
val substitutor = inferenceContext.superTypesSubstitutions[classDescriptor] ?: return
for (superFunction in superFunctions) {
val superClass = superFunction.containingDeclaration as? ClassDescriptor ?: continue
@@ -68,7 +68,7 @@ class FunctionConstraintsCollector(
typeElement: KtTypeElement,
superType: KotlinType,
superTypeElement: KtTypeElement?,
substitutor: ClassSubstitutor,
substitutor: SuperTypesSubstitutor,
superClass: ClassDescriptor,
inferenceContext: InferenceContext
) {
@@ -7,14 +7,10 @@ package org.jetbrains.kotlin.nj2k.inference.common
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.nj2k.*
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtTypeElement
import org.jetbrains.kotlin.psi.KtTypeProjection
import org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
fun PsiElement.getLabel(): JKElementInfoLabel? =
@@ -33,12 +29,12 @@ fun PsiElement.elementInfo(converterContext: NewJ2kConverterContext): List<JKEle
}
private fun KtTypeReference.hasUnknownLabel(context: NewJ2kConverterContext, unknownLabel: JKElementInfo) =
inline fun KtTypeReference.hasUnknownLabel(context: NewJ2kConverterContext, isUnknownLabel: (JKTypeInfo) -> Boolean) =
getLabel()?.let { label ->
unknownLabel in context.elementsInfoStorage.getInfoForLabel(label).orEmpty()
context.elementsInfoStorage.getInfoForLabel(label)?.any { it.safeAs<JKTypeInfo>()?.let(isUnknownLabel) == true }
} ?: false
fun KtTypeElement.hasUnknownLabel(context: NewJ2kConverterContext, unknownLabel: JKElementInfo) =
inline fun KtTypeElement.hasUnknownLabel(context: NewJ2kConverterContext, isUnknownLabel: (JKTypeInfo) -> Boolean) =
parent
?.safeAs<KtTypeReference>()
?.hasUnknownLabel(context, unknownLabel) == true
?.hasUnknownLabel(context, isUnknownLabel) == true
@@ -0,0 +1,36 @@
/*
* 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.inference.mutability
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
class CallResolver(private val fqNames: Set<FqName>) {
private val shortNames = fqNames.map { it.shortName().identifier }.toSet()
private fun CallableDescriptor.isMutatorCall(): Boolean {
if (fqNameOrNull() in fqNames) return true
return overriddenDescriptors.any { it.isMutatorCall() }
}
fun isNeededCall(expression: KtExpression, resolutionFacade: ResolutionFacade): Boolean {
val shortName = when (expression) {
is KtCallExpression -> expression.calleeExpression?.text
is KtReferenceExpression -> expression.text
else -> null
} ?: return false
if (shortName !in shortNames) return false
val call = expression.resolveToCall(resolutionFacade) ?: return false
return call.candidateDescriptor.isMutatorCall()
}
}
@@ -0,0 +1,47 @@
/*
* 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.inference.mutability
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.nj2k.inference.common.BoundType
import org.jetbrains.kotlin.nj2k.inference.common.BoundTypeCalculatorImpl
import org.jetbrains.kotlin.nj2k.inference.common.BoundTypeEnhancer
import org.jetbrains.kotlin.nj2k.inference.common.InferenceContext
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class MutabilityBoundTypeCalculator(
private val resolutionFacade: ResolutionFacade,
enhancer: BoundTypeEnhancer
) : BoundTypeCalculatorImpl(resolutionFacade, enhancer) {
private val callResolver = CallResolver(PRESERVING_MUTABILITY_FQ_NAMES)
override fun interceptCalculateBoundType(inferenceContext: InferenceContext, expression: KtExpression): BoundType? {
return when (expression) {
is KtQualifiedExpression -> {
val selector = expression.selectorExpression ?: return null
if (callResolver.isNeededCall(selector, resolutionFacade))
expression.receiverExpression.boundType(inferenceContext)
else null
}
else -> null
}
}
companion object {
private val PRESERVING_MUTABILITY_FQ_NAMES = setOf(
FQ_NAMES.collection.child(Name.identifier("iterator")),
FQ_NAMES.list.child(Name.identifier("listIterator")),
FQ_NAMES.map.child(Name.identifier("entries")),
FQ_NAMES.map.child(Name.identifier("values"))
)
}
}
@@ -0,0 +1,36 @@
/*
* 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.inference.mutability
import org.jetbrains.kotlin.nj2k.inference.common.*
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isFlexible
class MutabilityBoundTypeEnhancer : BoundTypeEnhancer() {
override fun enhance(
expression: KtExpression,
boundType: BoundType,
inferenceContext: InferenceContext
): BoundType = boundType
override fun enhanceKotlinType(
type: KotlinType,
boundType: BoundType,
allowLowerEnhancement: Boolean,
inferenceContext: InferenceContext
): BoundType {
if (type.isFlexible()) return boundType
val fqName = type.constructor.declarationDescriptor?.fqNameOrNull()
val enhancement = when {
fqName in MutabilityStateUpdater.mutableToImmutable && allowLowerEnhancement -> State.LOWER
fqName in MutabilityStateUpdater.immutableToMutable -> State.UPPER
else -> null
}
return boundType.enhanceWith(enhancement)
}
}
@@ -0,0 +1,19 @@
/*
* 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.inference.mutability
import org.jetbrains.kotlin.nj2k.inference.common.*
class MutabilityConstraintBoundProvider : ConstraintBoundProviderImpl() {
override fun BoundTypeLabel.constraintBound(): ConstraintBound? = when (this) {
is TypeVariableLabel -> typeVariable.constraintBound()
is TypeParameterLabel -> null
is GenericLabel -> null
StarProjectionLabel -> null
NullLiteralLabel -> null
LiteralLabel -> null
}
}
@@ -0,0 +1,72 @@
/*
* 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.inference.mutability
import com.intellij.psi.CommonClassNames
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.nj2k.inference.common.*
import org.jetbrains.kotlin.nj2k.inference.common.collectors.ConstraintsCollector
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class MutabilityConstraintsCollector : ConstraintsCollector() {
private val callResolver = CallResolver(MUTATOR_CALL_FQ_NAMES)
override fun ConstraintBuilder.collectConstraints(
element: KtElement,
boundTypeCalculator: BoundTypeCalculator,
inferenceContext: InferenceContext,
resolutionFacade: ResolutionFacade
) {
when (element) {
is KtQualifiedExpression -> {
val callExpression = element.selectorExpression?.safeAs<KtCallExpression>() ?: return
if (callResolver.isNeededCall(callExpression, resolutionFacade)) {
element.receiverExpression.isTheSameTypeAs(State.LOWER, ConstraintPriority.USE_AS_RECEIVER)
}
}
}
}
companion object {
private val MUTATOR_CALL_FQ_NAMES = setOf(
FQ_NAMES.mutableCollection.child(Name.identifier("add")),
FQ_NAMES.mutableCollection.child(Name.identifier("addAll")),
FQ_NAMES.mutableCollection.child(Name.identifier("remove")),
FQ_NAMES.mutableCollection.child(Name.identifier("removeAll")),
FQ_NAMES.mutableCollection.child(Name.identifier("retainAll")),
FQ_NAMES.mutableCollection.child(Name.identifier("clear")),
FQ_NAMES.mutableCollection.child(Name.identifier("removeIf")),
FQ_NAMES.mutableList.child(Name.identifier("addAll")),
FQ_NAMES.mutableList.child(Name.identifier("set")),
FQ_NAMES.mutableMap.child(Name.identifier("put")),
FQ_NAMES.mutableMap.child(Name.identifier("remove")),
FQ_NAMES.mutableMap.child(Name.identifier("putAll")),
FQ_NAMES.mutableMap.child(Name.identifier("clear")),
FQ_NAMES.mutableMap.child(Name.identifier("putIfAbsent")),
FQ_NAMES.mutableMap.child(Name.identifier("replace")),
FQ_NAMES.mutableMap.child(Name.identifier("replaceAll")),
FQ_NAMES.mutableMap.child(Name.identifier("computeIfAbsent")),
FQ_NAMES.mutableMap.child(Name.identifier("computeIfPresent")),
FQ_NAMES.mutableMap.child(Name.identifier("compute")),
FQ_NAMES.mutableMap.child(Name.identifier("merge")),
FQ_NAMES.mutableMapEntry.child(Name.identifier("setValue")),
FQ_NAMES.mutableIterator.child(Name.identifier("remove")),
FQ_NAMES.mutableListIterator.child(Name.identifier("set")),
FQ_NAMES.mutableListIterator.child(Name.identifier("add"))
)
}
}
@@ -0,0 +1,25 @@
/*
* 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.inference.mutability
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.inference.common.ClassReference
import org.jetbrains.kotlin.nj2k.inference.common.ContextCollector
import org.jetbrains.kotlin.nj2k.inference.common.State
import org.jetbrains.kotlin.nj2k.inference.common.hasUnknownLabel
import org.jetbrains.kotlin.psi.KtTypeElement
class MutabilityContextCollector(
resolutionFacade: ResolutionFacade,
private val converterContext: NewJ2kConverterContext
) : ContextCollector(resolutionFacade) {
override fun ClassReference.getState(typeElement: KtTypeElement?): State = when {
typeElement == null -> State.UNUSED
typeElement.hasUnknownLabel(converterContext) { it.unknownMutability } -> State.UNKNOWN
else -> State.UNUSED
}
}
@@ -0,0 +1,15 @@
/*
* 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.inference.mutability
import org.jetbrains.kotlin.nj2k.inference.common.DefaultStateProvider
import org.jetbrains.kotlin.nj2k.inference.common.State
import org.jetbrains.kotlin.nj2k.inference.common.TypeVariable
class MutabilityDefaultStateProvider : DefaultStateProvider() {
override fun defaultStateFor(typeVariable: TypeVariable): State =
State.UPPER
}
@@ -0,0 +1,68 @@
/*
* 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.inference.mutability
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.nj2k.inference.common.State
import org.jetbrains.kotlin.nj2k.inference.common.StateUpdater
import org.jetbrains.kotlin.nj2k.inference.common.TypeElementBasedTypeVariable
import org.jetbrains.kotlin.psi.KtNullableType
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtTypeElement
import org.jetbrains.kotlin.psi.KtUserType
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.SimpleType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class MutabilityStateUpdater : StateUpdater() {
override fun TypeElementBasedTypeVariable.updateState() = when (state) {
State.LOWER -> changeState(toMutable = true)
State.UPPER -> changeState(toMutable = false)
else -> Unit
}
companion object {
fun TypeElementBasedTypeVariable.changeState(toMutable: Boolean) {
changeState(typeElement.typeElement, typeElement.type, toMutable)
}
fun changeState(typeElement: KtTypeElement, type: KotlinType, toMutable: Boolean) {
if (type !is SimpleType) return
val userTypeElement = when (typeElement) {
is KtUserType -> typeElement
is KtNullableType -> typeElement.innerType as? KtUserType
else -> null
} ?: return
val initialFqName = type.constructor
.declarationDescriptor
.safeAs<ClassDescriptor>()
?.fqNameOrNull()
?: return
val newFqName = when {
!toMutable && initialFqName in mutableToImmutable -> mutableToImmutable[initialFqName]
toMutable && initialFqName in immutableToMutable -> immutableToMutable[initialFqName]
else -> null
} ?: return
val factory = KtPsiFactory(typeElement)
userTypeElement.referenceExpression?.replace(factory.createSimpleName(newFqName.shortName().identifier))
userTypeElement.qualifier.safeAs<KtUserType>()?.replace(factory.createType(newFqName.parent().asString()).typeElement ?: return)
}
val mutableToImmutable = mapOf(
KotlinBuiltIns.FQ_NAMES.mutableIterator to KotlinBuiltIns.FQ_NAMES.iterator,
KotlinBuiltIns.FQ_NAMES.mutableCollection to KotlinBuiltIns.FQ_NAMES.collection,
KotlinBuiltIns.FQ_NAMES.mutableList to KotlinBuiltIns.FQ_NAMES.list,
KotlinBuiltIns.FQ_NAMES.mutableListIterator to KotlinBuiltIns.FQ_NAMES.listIterator,
KotlinBuiltIns.FQ_NAMES.mutableSet to KotlinBuiltIns.FQ_NAMES.set,
KotlinBuiltIns.FQ_NAMES.mutableMap to KotlinBuiltIns.FQ_NAMES.map,
KotlinBuiltIns.FQ_NAMES.mutableMapEntry to KotlinBuiltIns.FQ_NAMES.mapEntry
)
val immutableToMutable = mutableToImmutable.map { (key, value) -> value to key }.toMap()
}
}
@@ -30,19 +30,17 @@ class NullabilityBoundTypeEnhancer(private val resolutionFacade: ResolutionFacad
expression: KtExpression,
boundType: BoundType,
inferenceContext: InferenceContext
): BoundType {
return when {
expression.isNullExpression() ->
WithForcedStateBoundType(boundType, State.UPPER)
expression is KtCallExpression -> enhanceCallExpression(expression, boundType, inferenceContext)
expression is KtQualifiedExpression && expression.selectorExpression is KtCallExpression ->
enhanceCallExpression(expression.selectorExpression as KtCallExpression, boundType, inferenceContext)
expression is KtNameReferenceExpression ->
boundType.enhanceWith(expression.smartCastEnhancement())
expression is KtLambdaExpression ->
WithForcedStateBoundType(boundType, State.LOWER)
else -> boundType
}
): BoundType = when {
expression.isNullExpression() ->
WithForcedStateBoundType(boundType, State.UPPER)
expression is KtCallExpression -> enhanceCallExpression(expression, boundType, inferenceContext)
expression is KtQualifiedExpression && expression.selectorExpression is KtCallExpression ->
enhanceCallExpression(expression.selectorExpression as KtCallExpression, boundType, inferenceContext)
expression is KtNameReferenceExpression ->
boundType.enhanceWith(expression.smartCastEnhancement())
expression is KtLambdaExpression ->
WithForcedStateBoundType(boundType, State.LOWER)
else -> boundType
}
private fun enhanceCallExpression(
@@ -7,20 +7,19 @@ package org.jetbrains.kotlin.nj2k.inference.nullability
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.UnknownNullability
import org.jetbrains.kotlin.nj2k.inference.common.*
import org.jetbrains.kotlin.psi.KtNullableType
import org.jetbrains.kotlin.psi.KtTypeElement
import org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.types.typeUtil.isUnit
class NullabilityContextCollector(
resolutionFacade: ResolutionFacade,
private val converterContext: NewJ2kConverterContext
) : ContextCollector(resolutionFacade) {
override fun ClassReference.getState(typeElement: KtTypeElement?): State? = when {
override fun ClassReference.getState(typeElement: KtTypeElement?): State = when {
descriptor?.defaultType?.isUnit() == true -> State.LOWER
typeElement == null -> State.UNKNOWN
typeElement.hasUnknownLabel(converterContext, UnknownNullability) -> State.UNKNOWN
typeElement.hasUnknownLabel(converterContext) { it.unknownNullability } -> State.UNKNOWN
typeElement is KtNullableType -> State.UPPER
else -> State.LOWER
}
@@ -5,23 +5,18 @@
package org.jetbrains.kotlin.nj2k.inference.nullability
import org.jetbrains.kotlin.nj2k.inference.common.*
import org.jetbrains.kotlin.nj2k.inference.common.State
import org.jetbrains.kotlin.nj2k.inference.common.StateUpdater
import org.jetbrains.kotlin.nj2k.inference.common.TypeElementBasedTypeVariable
import org.jetbrains.kotlin.psi.KtNullableType
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtTypeElement
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
class NullabilityStateUpdater : StateUpdater() {
override fun TypeElementBasedTypeVariable.updateState() = when (state) {
State.LOWER -> changeState(toNullable = false)
State.UPPER -> changeState(toNullable = true)
State.UNKNOWN -> {
if (typeElement is TypeParameterElementData) {
changeState(toNullable = false)
} else {
changeState(toNullable = true)
}
}
else -> Unit
}
private fun TypeElementBasedTypeVariable.changeState(toNullable: Boolean) {
@@ -245,8 +245,9 @@ private val processings: List<NamedPostProcessingGroup> = listOf(
),
runSingleTime = true
),
nullabilityProcessing,
clearUndefinedLabelsProcessing
NullabilityInferenceProcessing(),
MutabilityInferenceProcessing(),
clearUnknownLabelsProcessing
)
),
NamedPostProcessingGroup(
@@ -255,7 +256,7 @@ private val processings: List<NamedPostProcessingGroup> = listOf(
),
NamedPostProcessingGroup(
"Shortening fully-qualified references",
listOf(shortenReferencesProcessing)
listOf(ShortenReferenceProcessing())
),
NamedPostProcessingGroup(
"Converting POJOs to data classes",
@@ -285,7 +286,7 @@ private val processings: List<NamedPostProcessingGroup> = listOf(
"Optimizing imports",
listOf(
optimizeImportsProcessing,
shortenReferencesProcessing
ShortenReferenceProcessing()
)
),
NamedPostProcessingGroup(
@@ -0,0 +1,94 @@
/*
* 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.processings
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.editor.RangeMarker
import kotlinx.coroutines.withContext
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.util.EDT
import org.jetbrains.kotlin.idea.core.util.range
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.nj2k.inference.common.BoundTypeCalculatorImpl
import org.jetbrains.kotlin.nj2k.inference.common.ByInfoSuperFunctionsProvider
import org.jetbrains.kotlin.nj2k.inference.common.ConstraintsCollectorAggregator
import org.jetbrains.kotlin.nj2k.inference.common.InferenceFacade
import org.jetbrains.kotlin.nj2k.inference.common.collectors.CallExpressionConstraintCollector
import org.jetbrains.kotlin.nj2k.inference.common.collectors.CommonConstraintsCollector
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.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
abstract class InferenceProcessing : GeneralPostProcessing {
final override suspend 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() }
withContext(EDT) {
CommandProcessor.getInstance().runUndoTransparentAction {
createInferenceFacade(resolutionFacade, converterContext).runOn(elements)
}
}
}
abstract fun createInferenceFacade(
resolutionFacade: ResolutionFacade,
converterContext: NewJ2kConverterContext
): InferenceFacade
}
class NullabilityInferenceProcessing : InferenceProcessing() {
override fun createInferenceFacade(
resolutionFacade: ResolutionFacade,
converterContext: NewJ2kConverterContext
): InferenceFacade = InferenceFacade(
NullabilityContextCollector(resolutionFacade, converterContext),
ConstraintsCollectorAggregator(
resolutionFacade,
NullabilityConstraintBoundProvider(),
listOf(
CommonConstraintsCollector(),
CallExpressionConstraintCollector(),
FunctionConstraintsCollector(ByInfoSuperFunctionsProvider(resolutionFacade, converterContext)),
NullabilityConstraintsCollector()
)
),
BoundTypeCalculatorImpl(resolutionFacade, NullabilityBoundTypeEnhancer(resolutionFacade)),
NullabilityStateUpdater(),
NullabilityDefaultStateProvider()
)
}
class MutabilityInferenceProcessing : InferenceProcessing() {
override fun createInferenceFacade(
resolutionFacade: ResolutionFacade,
converterContext: NewJ2kConverterContext
): InferenceFacade = InferenceFacade(
MutabilityContextCollector(resolutionFacade, converterContext),
ConstraintsCollectorAggregator(
resolutionFacade,
MutabilityConstraintBoundProvider(),
listOf(
CommonConstraintsCollector(),
CallExpressionConstraintCollector(),
FunctionConstraintsCollector(ByInfoSuperFunctionsProvider(resolutionFacade, converterContext)),
MutabilityConstraintsCollector()
)
),
MutabilityBoundTypeCalculator(resolutionFacade, MutabilityBoundTypeEnhancer()),
MutabilityStateUpdater(),
MutabilityDefaultStateProvider()
)
}
@@ -0,0 +1,41 @@
/*
* 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.processings
import com.intellij.openapi.editor.RangeMarker
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.nj2k.ImportStorage
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.postProcessing.SimplePostProcessing
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtQualifiedExpression
class ShortenReferenceProcessing : SimplePostProcessing() {
private val filter = filter@{ element: PsiElement ->
when (element) {
is KtQualifiedExpression -> {
val fqName = element.selectorExpression?.mainReference?.resolve()?.getKotlinFqName()
?: return@filter ShortenReferences.FilterResult.PROCESS
if (ImportStorage.isImportNeeded(fqName)) ShortenReferences.FilterResult.PROCESS
else ShortenReferences.FilterResult.SKIP
}
else -> ShortenReferences.FilterResult.PROCESS
}
}
override fun applySimpleProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
if (rangeMarker != null) {
if (rangeMarker.isValid) {
ShortenReferences.DEFAULT.process(file, rangeMarker.startOffset, rangeMarker.endOffset, filter)
}
} else {
ShortenReferences.DEFAULT.process(file, filter)
}
}
}
@@ -10,29 +10,12 @@ import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.codeStyle.CodeStyleManager
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.core.util.range
import org.jetbrains.kotlin.idea.formatter.commitAndUnblockDocument
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.nj2k.ImportStorage
import org.jetbrains.kotlin.nj2k.asLabel
import org.jetbrains.kotlin.nj2k.inference.common.BoundTypeCalculatorImpl
import org.jetbrains.kotlin.nj2k.inference.common.ByInfoSuperFunctionsProvider
import org.jetbrains.kotlin.nj2k.inference.common.ConstraintsCollectorAggregator
import org.jetbrains.kotlin.nj2k.inference.common.InferenceFacade
import org.jetbrains.kotlin.nj2k.inference.common.collectors.CallExpressionConstraintCollector
import org.jetbrains.kotlin.nj2k.inference.common.collectors.CommonConstraintsCollector
import org.jetbrains.kotlin.nj2k.inference.common.collectors.FunctionConstraintsCollector
import org.jetbrains.kotlin.nj2k.inference.nullability.NullabilityBoundTypeEnhancer
import org.jetbrains.kotlin.nj2k.inference.nullability.NullabilityConstraintsCollector
import org.jetbrains.kotlin.nj2k.inference.nullability.NullabilityContextCollector
import org.jetbrains.kotlin.nj2k.inference.nullability.NullabilityStateUpdater
import org.jetbrains.kotlin.nj2k.postProcessing.postProcessing
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
val formatCodeProcessing =
postProcessing { file, rangeMarker, _ ->
@@ -47,33 +30,7 @@ val formatCodeProcessing =
}
}
val nullabilityProcessing =
postProcessing { file, rangeMarker, converterContext ->
val resolutionFacade = file.getResolutionFacade()
val inferenceFacade = InferenceFacade(
NullabilityContextCollector(resolutionFacade, converterContext),
ConstraintsCollectorAggregator(
resolutionFacade,
NullabilityConstraintBoundProvider(),
listOf(
CommonConstraintsCollector(),
CallExpressionConstraintCollector(),
FunctionConstraintsCollector(ByInfoSuperFunctionsProvider(resolutionFacade, converterContext)),
NullabilityConstraintsCollector()
)
),
BoundTypeCalculatorImpl(resolutionFacade, NullabilityBoundTypeEnhancer(resolutionFacade)),
NullabilityStateUpdater(),
NullabilityDefaultStateProvider()
)
val elements = if (rangeMarker != null) {
file.elementsInRange(rangeMarker.range ?: return@postProcessing).filterIsInstance<KtElement>()
} else listOf(file)
inferenceFacade.runOn(elements)
}
val clearUndefinedLabelsProcessing =
val clearUnknownLabelsProcessing =
postProcessing { file, _, _ ->
file.clearUndefinedLabels()
}
@@ -94,23 +51,6 @@ private fun KtFile.clearUndefinedLabels() {
comments.forEach { it.delete() }
}
val shortenReferencesProcessing =
postProcessing { file, rangeMarker, _ ->
val filter = filter@{ element: PsiElement ->
if (element !is KtQualifiedExpression) return@filter ShortenReferences.FilterResult.PROCESS
val fqName = element.selectorExpression?.mainReference?.resolve()?.getKotlinFqName()
?: return@filter ShortenReferences.FilterResult.PROCESS
if (ImportStorage.isImportNeeded(fqName)) ShortenReferences.FilterResult.PROCESS
else ShortenReferences.FilterResult.SKIP
}
if (rangeMarker != null) {
if (rangeMarker.isValid) {
ShortenReferences.DEFAULT.process(file, rangeMarker.startOffset, rangeMarker.endOffset, filter)
}
} else {
ShortenReferences.DEFAULT.process(file, filter)
}
}
val optimizeImportsProcessing =
postProcessing { file, rangeMarker, _ ->
@@ -0,0 +1,110 @@
/*
* 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.
*/
import java.io.File
private val JK_ROOT = File("./nj2k/src/org/jetbrains/kotlin/nj2k/tree")
private val JK_OUT_ROOT = File(JK_ROOT, "visitors")
private val JK_TREE_FILES = listOf(
"declarations.kt",
"elements.kt",
"expressions.kt",
"modifiers.kt",
"statements.kt"
).map { File(JK_ROOT, it) }
val elementRegex = """(class|object)\s+(JK[\w]+)(\([\w\s:,<>=\(\)\\]+\))?\s*:\s*(JK[a-zA-Z]+)""".toRegex()
data class InterfaceData(val name: String, val extends: String?)
fun File.interfaceNames() =
sequenceOf(this)
.map { it.readText() }
.flatMap { elementRegex.findAll(it) }
.map { match ->
InterfaceData(
match.groupValues[2],
match.groupValues[4].let { if (it == "JKAnnotationMemberValue") "JKTreeElement" else it }
)
}.toList()
fun String.safeVarName() = when (this) {
"class" -> "klass"
else -> this
}
fun genVisitors(
interfaceData: List<InterfaceData>,
visitorName: String
) {
val pkg = "package org.jetbrains.kotlin.nj2k.tree.visitors"
File(JK_OUT_ROOT, "$visitorName.kt").writeText(buildString {
appendln(pkg)
appendln()
appendln("import org.jetbrains.kotlin.nj2k.tree.*")
appendln()
appendln("abstract class $visitorName {")
interfaceData.joinTo(this, separator = "\n") { (name, ext) ->
val nameWithoutPrefix = name.removePrefix("JK")
val argName = nameWithoutPrefix.decapitalize().safeVarName()
val generifyCall = if (name != "JKTreeElement") "= visit${ext?.removePrefix("JK") ?: error(name)}($argName)" else ""
val modifier = if (name == "JKTreeElement") "abstract" else "open"
"""
| $modifier fun visit$nameWithoutPrefix($argName: $name) $generifyCall
""".trimMargin()
}
appendln()
appendln("}")
})
File(JK_OUT_ROOT, "${visitorName}WithCommentsPrinting.kt").writeText(buildString {
appendln(pkg)
appendln()
appendln("import org.jetbrains.kotlin.nj2k.tree.*")
appendln()
appendln("abstract class ${visitorName}WithCommentsPrinting : $visitorName() {")
appendln(
"""
| abstract fun printLeftNonCodeElements(element: JKNonCodeElementsListOwner)
| abstract fun printRightNonCodeElements(element: JKNonCodeElementsListOwner)
|
""".trimMargin()
)
interfaceData.joinTo(this, separator = "\n\n") { (name, ext) ->
val nameWithoutPrefix = name.removePrefix("JK")
val argName = nameWithoutPrefix.decapitalize().safeVarName()
val arg = "$argName: $name"
val rawVisitSuffix = "Raw"
val generifyCall = if (name != "JKTreeElement") "= visit${ext!!.removePrefix("JK")}$rawVisitSuffix($argName)" else ""
val modifier = if (name == "JKTreeElement") "abstract" else "open"
"""
| final override fun visit$nameWithoutPrefix($arg) {
| printLeftNonCodeElements($argName)
| visit$nameWithoutPrefix$rawVisitSuffix($argName)
| printRightNonCodeElements($argName)
| }
|
| $modifier fun visit$nameWithoutPrefix$rawVisitSuffix($arg) $generifyCall
""".trimMargin()
}
appendln()
appendln("}")
})
}
fun main() {
genVisitors(JK_TREE_FILES.flatMap { it.interfaceNames() }, "JKVisitor")
}
@@ -6,7 +6,8 @@
package org.jetbrains.kotlin.nj2k
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.utils.SmartList
import kotlin.random.Random
interface JKElementInfo
@@ -16,8 +17,23 @@ data class InternalSuperFunctionInfo(val label: JKElementInfoLabel) : SuperFunct
data class FunctionInfo(val superFunctions: List<SuperFunctionInfo>) : JKElementInfo
object UnknownNullability : JKElementInfo
object UnknownMutability : JKElementInfo
enum class JKTypeInfo(val unknownNullability: Boolean, val unknownMutability: Boolean) : JKElementInfo {
KNOWN_NULLABILITY_KNOWN_MUTABILITY(false, false),
UNKNOWN_NULLABILITY_KNOWN_MUTABILITY(true, false),
KNOWN_NULLABILITY_UNKNOWN_MUTABILITY(false, true),
UNKNOWN_NULLABILITY_UNKNOWN_MUTABILITY(true, true)
;
companion object {
operator fun invoke(unknownNullability: Boolean, unknownMutability: Boolean) = when {
!unknownNullability && !unknownMutability -> KNOWN_NULLABILITY_KNOWN_MUTABILITY
unknownNullability && !unknownMutability -> UNKNOWN_NULLABILITY_KNOWN_MUTABILITY
!unknownNullability && unknownMutability -> KNOWN_NULLABILITY_UNKNOWN_MUTABILITY
else -> UNKNOWN_NULLABILITY_UNKNOWN_MUTABILITY
}
}
}
inline class JKElementInfoLabel(val label: String) {
fun render(): String = "/*@@$label@@*/"
@@ -31,22 +47,18 @@ fun String.asLabel(): JKElementInfoLabel? =
JKElementInfoLabel.LABEL_REGEX.matchEntire(this)?.groupValues?.getOrNull(1)?.let { JKElementInfoLabel(it) }
class JKElementInfoStorage {
private val labelToInfo = mutableMapOf<JKElementInfoLabel, List<JKElementInfo>>()
private val labelToInfo = mutableMapOf<JKElementInfoLabel, MutableList<JKElementInfo>>()
private val elementToLabel = mutableMapOf<Any, JKElementInfoLabel>()
fun getOrCreateInfoForElement(element: Any): JKElementInfoLabel =
elementToLabel.getOrPut(element) { JKElementInfoLabel(createRandomString()) }
fun getOrCreateInfoForLabel(label: JKElementInfoLabel): List<JKElementInfo> =
labelToInfo.getOrPut(label) { emptyList() }
fun getInfoForLabel(label: JKElementInfoLabel): List<JKElementInfo>? =
labelToInfo[label]
fun addEntry(element: Any, info: JKElementInfo) {
val label = elementToLabel.getOrPut(element) { JKElementInfoLabel(createRandomString()) }
labelToInfo[label] = labelToInfo[label].orEmpty() + info
labelToInfo.getOrPut(label) { SmartList() } += info
elementToLabel[element] = label
}
@@ -54,11 +66,8 @@ class JKElementInfoStorage {
private val charPool = ('a'..'z').toList()
private const val generatedStringLength = 6
private fun createRandomString(): String {
return (1..generatedStringLength)
.map { kotlin.random.Random.nextInt(0, charPool.size) }
.map(charPool::get)
.joinToString("")
private fun createRandomString() = (1..generatedStringLength).joinToString("") {
charPool[Random.nextInt(0, charPool.size)].toString()
}
}
}
@@ -6,22 +6,22 @@
package org.jetbrains.kotlin.nj2k.conversions
import com.intellij.psi.PsiMethod
import com.intellij.psi.impl.light.LightMethod
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaMethodDescriptor
import org.jetbrains.kotlin.j2k.ast.Nullability
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
import org.jetbrains.kotlin.nj2k.*
import org.jetbrains.kotlin.nj2k.symbols.JKUniverseMethodSymbol
import org.jetbrains.kotlin.nj2k.tree.*
import org.jetbrains.kotlin.nj2k.tree.impl.JKCapturedType
import org.jetbrains.kotlin.nj2k.tree.impl.psi
import org.jetbrains.kotlin.nj2k.types.JKParametrizedType
import org.jetbrains.kotlin.nj2k.types.JKStarProjectionType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class AddElementsInfoConversion(private val context: NewJ2kConverterContext) : RecursiveApplicableConversionBase() {
class AddElementsInfoConversion(context: NewJ2kConverterContext) : RecursiveApplicableConversionBase(context) {
override fun applyToElement(element: JKTreeElement): JKTreeElement {
when (element) {
is JKTypeElement -> addInfoForTypeElement(element)
@@ -33,14 +33,10 @@ class AddElementsInfoConversion(private val context: NewJ2kConverterContext) : R
private fun addInfoForTypeElement(typeElement: JKTypeElement) {
typeElement.type.forAllInnerTypes { type ->
if (type.nullability == Nullability.Default
|| type.safeAs<JKCapturedType>()?.wildcardType is JKStarProjectionType
) {
context.elementsInfoStorage.addEntry(type, UnknownNullability)
}
if (type.isCollectionType) {
context.elementsInfoStorage.addEntry(type, UnknownMutability)
}
val hasUnknownNullability =
type.nullability == Nullability.Default || type.safeAs<JKCapturedType>()?.wildcardType is JKStarProjectionType
val hasUnknownMutability = type.isCollectionType
context.elementsInfoStorage.addEntry(type, JKTypeInfo(hasUnknownNullability, hasUnknownMutability))
}
}
@@ -48,7 +44,7 @@ class AddElementsInfoConversion(private val context: NewJ2kConverterContext) : R
val superMethods = function.superMethods() ?: return
val superDescriptorsInfo = superMethods.map { superDescriptor ->
val superPsi = superDescriptor.original.findPsi()
when (val symbol = context.symbolProvider.symbolsByPsi[superPsi]) {
when (val symbol = symbolProvider.symbolsByPsi[superPsi]) {
is JKUniverseMethodSymbol ->
InternalSuperFunctionInfo(context.elementsInfoStorage.getOrCreateInfoForElement(symbol.target))
else -> ExternalSuperFunctionInfo(superDescriptor)
@@ -59,8 +55,8 @@ class AddElementsInfoConversion(private val context: NewJ2kConverterContext) : R
private fun JKMethod.superMethods(): Collection<FunctionDescriptor>? {
val psiMethod = psi<PsiMethod>() ?: return null
psiMethod.getJavaMethodDescriptor()?.let { descriptor ->
return descriptor.overriddenDescriptors
psiMethod.getJavaMethodDescriptor()?.let {
return it.overriddenDescriptors
}
return psiMethod.findSuperMethods().mapNotNull { superMethod ->
when (superMethod) {
@@ -112,6 +112,7 @@ class TypeMappingConversion(val context: NewJ2kConverterContext) : RecursiveAppl
private fun JKClassSymbol.mapClassSymbol(typeElement: JKTypeElement?): JKClassSymbol {
if (this is JKUniverseClassSymbol) return this
if (typeElement?.parentOfType<JKInheritanceInfo>() != null) return this
val newFqName = kotlinCollectionClassName()
?: kotlinStandardType()
?: fqName
+2 -2
View File
@@ -1,5 +1,5 @@
import java.io.File
internal fun foo(file: File?): List<String> {
return emptyList()
internal fun foo(file: File?): List<String?>? {
return emptyList<String>()
}
@@ -1,7 +1,7 @@
import java.io.File
class C {
private fun memberFun(file: File?) {}
private fun memberFun(file: File) {}
companion object {
@JvmStatic
+1 -1
View File
@@ -1,5 +1,5 @@
package to
fun foo(): String {
fun foo(): String? {
return ""
}
@@ -1,6 +1,6 @@
package to
@SomeAnnotation
fun foo(): String {
fun foo(): String? {
return ""
}
+3 -3
View File
@@ -23,12 +23,12 @@ class A {
return JetRefactoringUtil.formatPsiMethod(element as PsiMethod, true, false)
}
protected fun getDimensionServiceKey(): String {
protected fun getDimensionServiceKey(): String? {
return "#org.jetbrains.kotlin.idea.refactoring.safeDelete.KotlinOverridingDialog"
}
fun getSelected(): ArrayList<UsageInfo> {
val result: ArrayList<UsageInfo> = ArrayList<UsageInfo?>()
fun getSelected(): ArrayList<UsageInfo>? {
val result: ArrayList<UsageInfo> = ArrayList<UsageInfo>()
for (i in 0 until myChecked.length) {
if (myChecked.get(i)) {
result.add(myOverridingMethods.get(i))
+1 -1
View File
@@ -3,5 +3,5 @@ import java.util.ArrayList
fun foo() {
bar(ArrayList<String?>())
bar(ArrayList<String>())
}
@@ -2,4 +2,4 @@
import java.util.ArrayList
fun foo() = ArrayList<String?>()
fun foo() = ArrayList<String>()
+1 -1
View File
@@ -3,5 +3,5 @@ import java.util.ArrayList
fun foo() {
bar(/*comment*/ArrayList<String?>())
bar(/*comment*/ArrayList<String>())
}
@@ -5,7 +5,7 @@ import java.util.ArrayList
class JavaClass {
internal fun foo(file: File?, target: List<String?>?) {
internal fun foo(file: File?, target: MutableList<String>?) {
val list = ArrayList<String>()
if (file != null) {
list.add(file.name)
+1 -1
View File
@@ -7,4 +7,4 @@ fun test() {
//T0 <: T1 due to 'INITIALIZER'
//T2 <: T4 due to 'INITIALIZER'
//T3 <: T1 due to 'ASSIGNMENT'
//T1 <: T3 due to 'ASSIGNMENT'
+5
View File
@@ -0,0 +1,5 @@
class Test {
val x: /*T2@*/List</*T1@*/Int> = ArrayList</*T0@*/Int>()/*ArrayList<T0@Int>*/
}
//T1 := T0 due to 'INITIALIZER'
@@ -0,0 +1,8 @@
fun a(
l0: /*T1@*/MutableIterator</*T0@*/Int>
) {
l0/*T1@MutableIterator<T0@Int>*/.remove()
}
//T0 <: T0 due to 'RECEIVER_PARAMETER'
//T1 := LOWER due to 'USE_AS_RECEIVER'
+7
View File
@@ -0,0 +1,7 @@
import java.util.ArrayList
fun a() {
val list: /*T2@*/List</*T1@*/Int> = ArrayList</*T0@*/Int>()/*ArrayList<T0@Int>*/
}
//T1 := T0 due to 'INITIALIZER'
@@ -0,0 +1,30 @@
fun a(
l0: /*T1@*/MutableCollection</*T0@*/Int>,
l1: /*T3@*/MutableCollection</*T2@*/Int>,
l2: /*T5@*/MutableCollection</*T4@*/Int>,
l3: /*T7@*/MutableCollection</*T6@*/Int>,
l4: /*T9@*/MutableCollection</*T8@*/Int>
) {
l0/*T1@MutableCollection<T0@Int>*/.add(1/*LIT*/)
l1/*T3@MutableCollection<T2@Int>*/.addAll(l1/*T3@MutableCollection<T2@Int>*/)
l2/*T5@MutableCollection<T4@Int>*/.clear()
l3/*T7@MutableCollection<T6@Int>*/.removeAll(l1/*T3@MutableCollection<T2@Int>*/)
l4/*T9@MutableCollection<T8@Int>*/.retainAll(l1/*T3@MutableCollection<T2@Int>*/)
}
//T0 := T0 due to 'RECEIVER_PARAMETER'
//T1 := LOWER due to 'USE_AS_RECEIVER'
//T2 := T2 due to 'RECEIVER_PARAMETER'
//T2 := T2 due to 'PARAMETER'
//T3 <: UPPER due to 'PARAMETER'
//T3 := LOWER due to 'USE_AS_RECEIVER'
//T4 := T4 due to 'RECEIVER_PARAMETER'
//T5 := LOWER due to 'USE_AS_RECEIVER'
//T6 := T6 due to 'RECEIVER_PARAMETER'
//T6 := T2 due to 'PARAMETER'
//T3 <: UPPER due to 'PARAMETER'
//T7 := LOWER due to 'USE_AS_RECEIVER'
//T8 := T8 due to 'RECEIVER_PARAMETER'
//T8 := T2 due to 'PARAMETER'
//T3 <: UPPER due to 'PARAMETER'
//T9 := LOWER due to 'USE_AS_RECEIVER'
+8
View File
@@ -0,0 +1,8 @@
class Nya {
private val hashMap: /*T6@*/Map</*T3@*/String, /*T5@*/List</*T4@*/Int>> =
HashMap</*T0@*/String, /*T2@*/List</*T1@*/Int>>()/*HashMap<T0@String, T2@MutableList<T1@Int>>*/
}
//T3 := T0 due to 'INITIALIZER'
//T4 := T1 due to 'INITIALIZER'
//T5 := T2 due to 'INITIALIZER'
+10
View File
@@ -0,0 +1,10 @@
fun a(l0: /*T1@*/MutableList</*T0@*/Int>) {
val iterator: /*T3@*/MutableIterator</*T2@*/Int> = l0/*T1@MutableList<T0@Int>*/.iterator()/*T1@MutableList<T0@Int>*/
iterator/*T3@MutableIterator<T2@Int>*/.remove()
}
//T0 := T0 due to 'RECEIVER_PARAMETER'
//T2 := T0 due to 'INITIALIZER'
//T1 <: T3 due to 'INITIALIZER'
//T2 <: T2 due to 'RECEIVER_PARAMETER'
//T3 := LOWER due to 'USE_AS_RECEIVER'
+4
View File
@@ -0,0 +1,4 @@
class A {
val l: /*T1@*/List</*T0@*/Int>
}
@@ -0,0 +1,16 @@
fun a(
l0: /*T1@*/MutableListIterator</*T0@*/Int>,
l1: /*T3@*/MutableListIterator</*T2@*/Int>,
l2: /*T5@*/MutableListIterator</*T4@*/Int>
) {
l0/*T1@MutableListIterator<T0@Int>*/.add(1/*LIT*/)
l1/*T3@MutableListIterator<T2@Int>*/.remove()
l2/*T5@MutableListIterator<T4@Int>*/.set(1/*LIT*/)
}
//T0 := T0 due to 'RECEIVER_PARAMETER'
//T1 := LOWER due to 'USE_AS_RECEIVER'
//T2 := T2 due to 'RECEIVER_PARAMETER'
//T3 := LOWER due to 'USE_AS_RECEIVER'
//T4 := T4 due to 'RECEIVER_PARAMETER'
//T5 := LOWER due to 'USE_AS_RECEIVER'
+46
View File
@@ -0,0 +1,46 @@
fun a(
l0: /*T1@*/MutableList</*T0@*/Int>,
l1: /*T3@*/MutableList</*T2@*/Int>,
l2: /*T5@*/MutableList</*T4@*/Int>,
l3: /*T7@*/MutableList</*T6@*/Int>,
l4: /*T9@*/MutableList</*T8@*/Int>,
l5: /*T11@*/MutableList</*T10@*/Int>,
l6: /*T13@*/MutableList</*T12@*/Int>,
l7: /*T15@*/MutableList</*T14@*/Int>
) {
l0/*T1@MutableList<T0@Int>*/.add(1/*LIT*/)
l1/*T3@MutableList<T2@Int>*/.addAll(1/*LIT*/, l1/*T3@MutableList<T2@Int>*/)
l2/*T5@MutableList<T4@Int>*/.addAll(l1/*T3@MutableList<T2@Int>*/)
l3/*T7@MutableList<T6@Int>*/.clear()
l4/*T9@MutableList<T8@Int>*/.remove(1/*LIT*/)
l5/*T11@MutableList<T10@Int>*/.removeAll(l1/*T3@MutableList<T2@Int>*/)
l5/*T11@MutableList<T10@Int>*/.removeAt(0/*LIT*/)
l6/*T13@MutableList<T12@Int>*/.retainAll(l1/*T3@MutableList<T2@Int>*/)
l7/*T15@MutableList<T14@Int>*/.set(0/*LIT*/, 0/*LIT*/)
}
//T0 := T0 due to 'RECEIVER_PARAMETER'
//T1 := LOWER due to 'USE_AS_RECEIVER'
//T2 := T2 due to 'RECEIVER_PARAMETER'
//T2 := T2 due to 'PARAMETER'
//T3 <: UPPER due to 'PARAMETER'
//T3 := LOWER due to 'USE_AS_RECEIVER'
//T4 := T4 due to 'RECEIVER_PARAMETER'
//T4 := T2 due to 'PARAMETER'
//T3 <: UPPER due to 'PARAMETER'
//T5 := LOWER due to 'USE_AS_RECEIVER'
//T6 := T6 due to 'RECEIVER_PARAMETER'
//T7 := LOWER due to 'USE_AS_RECEIVER'
//T8 := T8 due to 'RECEIVER_PARAMETER'
//T9 := LOWER due to 'USE_AS_RECEIVER'
//T10 := T10 due to 'RECEIVER_PARAMETER'
//T10 := T2 due to 'PARAMETER'
//T3 <: UPPER due to 'PARAMETER'
//T11 := LOWER due to 'USE_AS_RECEIVER'
//T10 := T10 due to 'RECEIVER_PARAMETER'
//T12 := T12 due to 'RECEIVER_PARAMETER'
//T12 := T2 due to 'PARAMETER'
//T3 <: UPPER due to 'PARAMETER'
//T13 := LOWER due to 'USE_AS_RECEIVER'
//T14 := T14 due to 'RECEIVER_PARAMETER'
//T15 := LOWER due to 'USE_AS_RECEIVER'
@@ -0,0 +1,15 @@
import java.util.ArrayList
fun test() {
val list: /*T4@*/List</*T3@*/MutableList</*T2@*/Int>> = ArrayList</*T1@*/MutableList</*T0@*/Int>>()/*ArrayList<T1@MutableList<T0@Int>>*/
for(l: /*T6@*/MutableList</*T5@*/Int> in list/*T4@MutableList<T3@MutableList<T2@Int>>*/) {
l/*T6@MutableList<T5@Int>*/.add(42/*LIT*/)
}
}
//T2 := T0 due to 'INITIALIZER'
//T3 := T1 due to 'INITIALIZER'
//T5 := T5 due to 'RECEIVER_PARAMETER'
//T6 := LOWER due to 'USE_AS_RECEIVER'
//T5 := T2 due to 'ASSIGNMENT'
//T3 <: T6 due to 'ASSIGNMENT'
+14
View File
@@ -0,0 +1,14 @@
import java.util.ArrayList
fun a() {
val list: /*T4@*/List</*T3@*/MutableList</*T2@*/Int>> = ArrayList</*T1@*/MutableList</*T0@*/Int>>()/*ArrayList<T1@MutableList<T0@Int>>*/
list/*T4@MutableList<T3@MutableList<T2@Int>>*/.get(0/*LIT*/)/*T3@MutableList<T2@Int>*/.add(1/*LIT*/)
}
//T2 := T0 due to 'INITIALIZER'
//T3 := T1 due to 'INITIALIZER'
//T2 := T2 due to 'RECEIVER_PARAMETER'
//T3 := T3 due to 'RECEIVER_PARAMETER'
//T4 <: UPPER due to 'RECEIVER_PARAMETER'
//T2 := T2 due to 'RECEIVER_PARAMETER'
//T3 := LOWER due to 'USE_AS_RECEIVER'
@@ -0,0 +1,9 @@
fun a(
l0: /*T2@*/MutableMap.MutableEntry</*T0@*/Int, /*T1@*/String>
) {
l0/*T2@MutableEntry<T0@Int, T1@String>*/.setValue(1/*LIT*/, "nya")
}
//T0 := T0 due to 'RECEIVER_PARAMETER'
//T1 := T1 due to 'RECEIVER_PARAMETER'
//T2 := LOWER due to 'USE_AS_RECEIVER'
+27
View File
@@ -0,0 +1,27 @@
fun a(
l0: /*T2@*/MutableMap</*T0@*/Int, /*T1@*/String>,
l1: /*T5@*/MutableMap</*T3@*/Int, /*T4@*/String>,
l2: /*T8@*/MutableMap</*T6@*/Int, /*T7@*/String>,
l3: /*T11@*/MutableMap</*T9@*/Int, /*T10@*/String>
) {
l0/*T2@MutableMap<T0@Int, T1@String>*/.put(1)
l1/*T5@MutableMap<T3@Int, T4@String>*/.remove(1/*LIT*/, l1)
l2/*T8@MutableMap<T6@Int, T7@String>*/.putAll(l1/*T5@MutableMap<T3@Int, T4@String>*/)
l3/*T11@MutableMap<T9@Int, T10@String>*/.clear(1)
}
//T0 := T0 due to 'RECEIVER_PARAMETER'
//T1 := T1 due to 'RECEIVER_PARAMETER'
//T2 := LOWER due to 'USE_AS_RECEIVER'
//T3 := T3 due to 'RECEIVER_PARAMETER'
//T4 := T4 due to 'RECEIVER_PARAMETER'
//T5 := LOWER due to 'USE_AS_RECEIVER'
//T6 := T6 due to 'RECEIVER_PARAMETER'
//T7 := T7 due to 'RECEIVER_PARAMETER'
//T6 := T3 due to 'PARAMETER'
//T7 := T4 due to 'PARAMETER'
//T5 <: UPPER due to 'PARAMETER'
//T8 := LOWER due to 'USE_AS_RECEIVER'
//T9 := T9 due to 'RECEIVER_PARAMETER'
//T10 := T10 due to 'RECEIVER_PARAMETER'
//T11 := LOWER due to 'USE_AS_RECEIVER'
+30
View File
@@ -0,0 +1,30 @@
fun a(
l0: /*T1@*/MutableSet</*T0@*/Int>,
l1: /*T3@*/MutableSet</*T2@*/Int>,
l2: /*T5@*/MutableSet</*T4@*/Int>,
l3: /*T7@*/MutableSet</*T6@*/Int>,
l4: /*T9@*/MutableSet</*T8@*/Int>
) {
l0/*T1@MutableSet<T0@Int>*/.add(1/*LIT*/)
l1/*T3@MutableSet<T2@Int>*/.addAll(l1/*T3@MutableSet<T2@Int>*/)
l2/*T5@MutableSet<T4@Int>*/.clear()
l3/*T7@MutableSet<T6@Int>*/.removeAll(l1/*T3@MutableSet<T2@Int>*/)
l4/*T9@MutableSet<T8@Int>*/.retainAll(l1/*T3@MutableSet<T2@Int>*/)
}
//T0 := T0 due to 'RECEIVER_PARAMETER'
//T1 := LOWER due to 'USE_AS_RECEIVER'
//T2 := T2 due to 'RECEIVER_PARAMETER'
//T2 := T2 due to 'PARAMETER'
//T3 <: UPPER due to 'PARAMETER'
//T3 := LOWER due to 'USE_AS_RECEIVER'
//T4 := T4 due to 'RECEIVER_PARAMETER'
//T5 := LOWER due to 'USE_AS_RECEIVER'
//T6 := T6 due to 'RECEIVER_PARAMETER'
//T6 := T2 due to 'PARAMETER'
//T3 <: UPPER due to 'PARAMETER'
//T7 := LOWER due to 'USE_AS_RECEIVER'
//T8 := T8 due to 'RECEIVER_PARAMETER'
//T8 := T2 due to 'PARAMETER'
//T3 <: UPPER due to 'PARAMETER'
//T9 := LOWER due to 'USE_AS_RECEIVER'
+1 -1
View File
@@ -4,5 +4,5 @@ fun test(a: /*T1@*/List</*T0@*/Int?>) {
}
}
//T2 <: T0 due to 'ASSIGNMENT'
//T0 <: T2 due to 'ASSIGNMENT'
//T1 := LOWER due to 'USE_AS_RECEIVER'
+5 -5
View File
@@ -1,4 +1,4 @@
fun bar(map: /*T2@*/HashMap</*T0@*/String?, /*T1@*/Int>, list1: /*T4@*/List</*T3@*/Int?>, list2: /*T6@*/List</*T5@*/String?>) {
fun bar(map: /*T2@*/HashMap</*T0@*/String?, /*T1@*/Int>, list1: /*T4@*/List</*T3@*/Int>, list2: /*T6@*/List</*T5@*/String?>) {
for (entry: /*T9@*/MutableMap.MutableEntry</*T7@*/String?, /*T8@*/Int> in map/*T2@HashMap<T0@String, T1@Int>*/.entries/*MutableSet<MutableEntry<T0@String, T1@Int>>*/) {
val value: /*T10@*/Int = entry/*T9@MutableEntry<T7@String, T8@Int>*/.value/*T8@Int*/
if (entry/*T9@MutableEntry<T7@String, T8@Int>*/.key/*T7@String*/ == null/*LIT*/) {
@@ -21,11 +21,11 @@ fun bar(map: /*T2@*/HashMap</*T0@*/String?, /*T1@*/Int>, list1: /*T4@*/List</*T3
//T9 := LOWER due to 'USE_AS_RECEIVER'
//T7 := UPPER due to 'COMPARE_WITH_NULL'
//T10 := LOWER due to 'USE_AS_RECEIVER'
//T0 := T7 due to 'ASSIGNMENT'
//T1 := T8 due to 'ASSIGNMENT'
//T7 := T0 due to 'ASSIGNMENT'
//T8 := T1 due to 'ASSIGNMENT'
//T11 := LOWER due to 'USE_AS_RECEIVER'
//T11 <: T3 due to 'ASSIGNMENT'
//T3 <: T11 due to 'ASSIGNMENT'
//T4 := LOWER due to 'USE_AS_RECEIVER'
//T12 := UPPER due to 'COMPARE_WITH_NULL'
//T12 <: T5 due to 'ASSIGNMENT'
//T5 <: T12 due to 'ASSIGNMENT'
//T6 := LOWER due to 'USE_AS_RECEIVER'
+9 -8
View File
@@ -18,19 +18,19 @@ internal open class B {
open val fromB1: String
get() = ""
open var fromB2: String
open var fromB2: String?
get() = ""
set(value) {}
open var fromB3: String
open var fromB3: String?
get() = ""
set(value) {}
open var fromB4: String
open var fromB4: String?
get() = ""
set(value) {}
open fun setFromB5(value: String) {}
open fun setFromB5(value: String?) {}
}
internal abstract class C(override val something1: Int) : B(), I {
@@ -59,19 +59,19 @@ internal abstract class C(override val something1: Int) : B(), I {
override val fromB1: String
get() = super.fromB1
override var fromB2: String
override var fromB2: String?
get() = super.fromB2
set(value) {
super.fromB2 = value
}
override var fromB3: String
override var fromB3: String?
get() = super.fromB3
set(fromB3) {
super.fromB3 = fromB3
}
override var fromB4: String
override var fromB4: String?
get() = super.fromB4
set(value) {
super.fromB4 = value
@@ -81,7 +81,8 @@ internal abstract class C(override val something1: Int) : B(), I {
return ""
}
override fun setFromB5(value: String) {
override fun setFromB5(value: String?) {
super.setFromB5(value)
}
}
@@ -0,0 +1,8 @@
public class Nya {
private int i;
public Nya(int i) {
/*comment before*/
this/*1*/./*2*/i/*3*/ = /*4*/i /* comment after*/; // and after again
}
}
@@ -0,0 +1 @@
class Nya /*comment before*/(/*1*//*3*/ /*4*/private val i: Int)
+1 -1
View File
@@ -1,5 +1,5 @@
internal class A {
var list: List<String?>? = null
var list: List<String>? = null
fun foo() {
for (e in list!!) {
println(e)
+1 -1
View File
@@ -3,6 +3,6 @@ import java.util.ArrayList
internal object A {
@JvmStatic
fun main(args: Array<String>) {
List::class.java.isAssignableFrom(ArrayList::class.java)
MutableList::class.java.isAssignableFrom(ArrayList::class.java)
}
}
+2 -1
View File
@@ -1,4 +1,5 @@
internal class X(private val list: MutableList<Y>) {
internal class X(private val list: List<Y>) {
internal inner class Y
}
@@ -1,4 +1,3 @@
// ERROR: Operator call corresponds to a dot-qualified call 'value.plus(1)' which is not allowed on a nullable receiver 'value'.
import java.util.HashMap
internal enum class E { A, B, C }
@@ -15,7 +14,7 @@ internal class A {
val i = map.entries.iterator().next().key + 1
}
fun bar(list: MutableList<String?>, map: HashMap<String?, Int?>) {
fun bar(list: MutableList<String?>, map: HashMap<String, Int>) {
val c = "a"[0]
val b = 10.toByte()
val i = 10.1.toInt()
@@ -0,0 +1,17 @@
import java.util.Iterator;
public class TestMutableIterator implements Iterator<String> {
@Override
public boolean hasNext() {
return false;
}
@Override
public String next() {
return null;
}
@Override
public void remove() {
}
}
@@ -0,0 +1,11 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
List<List<Integer>> list = new ArrayList<>();
public void test() {
list.get(0).add(1)
}
}
@@ -0,0 +1,8 @@
import java.util.ArrayList
class Test {
internal var list: List<List<Int>> = ArrayList()
fun test() {
list[0].add(1)
}
}
@@ -0,0 +1,12 @@
import java.util.ArrayList;
import java.util.List;
public class Owner {
public List<String> list = new ArrayList<>();
}
public class Updater {
public void update(Owner owner) {
owner.list.add("");
}
}
@@ -0,0 +1,11 @@
import java.util.ArrayList
class Owner {
var list: MutableList<String> = ArrayList()
}
class Updater {
fun update(owner: Owner) {
owner.list.add("")
}
}
@@ -1376,6 +1376,11 @@ public class NewJavaToKotlinConverterSingleFileTestGenerated extends AbstractNew
runTest("nj2k/testData/newJ2k/detectProperties/CannotDropOnlySetter.java");
}
@TestMetadata("commentInInitStatement.java")
public void testCommentInInitStatement() throws Exception {
runTest("nj2k/testData/newJ2k/detectProperties/commentInInitStatement.java");
}
@TestMetadata("Comments.java")
public void testComments() throws Exception {
runTest("nj2k/testData/newJ2k/detectProperties/Comments.java");
@@ -3628,6 +3633,16 @@ public class NewJavaToKotlinConverterSingleFileTestGenerated extends AbstractNew
runTest("nj2k/testData/newJ2k/mutableCollections/Iterator2.java");
}
@TestMetadata("listOfMutableList.java")
public void testListOfMutableList() throws Exception {
runTest("nj2k/testData/newJ2k/mutableCollections/listOfMutableList.java");
}
@TestMetadata("mutableListInOtherClass.java")
public void testMutableListInOtherClass() throws Exception {
runTest("nj2k/testData/newJ2k/mutableCollections/mutableListInOtherClass.java");
}
@TestMetadata("Overrides.java")
public void testOverrides() throws Exception {
runTest("nj2k/testData/newJ2k/mutableCollections/Overrides.java");
@@ -17,7 +17,7 @@ abstract class AbstractCommonConstraintCollectorTest : AbstractConstraintCollect
override fun createInferenceFacade(resolutionFacade: ResolutionFacade): InferenceFacade =
InferenceFacade(
object : ContextCollector(resolutionFacade) {
override fun ClassReference.getState(typeElement: KtTypeElement?): State? =
override fun ClassReference.getState(typeElement: KtTypeElement?): State =
State.UNKNOWN
},
ConstraintsCollectorAggregator(
@@ -134,6 +134,11 @@ public class CommonConstraintCollectorTestGenerated extends AbstractCommonConstr
runTest("nj2k/testData/inference/common/memberCall.kt");
}
@TestMetadata("newExpression.kt")
public void testNewExpression() throws Exception {
runTest("nj2k/testData/inference/common/newExpression.kt");
}
@TestMetadata("returnFromLambda.kt")
public void testReturnFromLambda() throws Exception {
runTest("nj2k/testData/inference/common/returnFromLambda.kt");
@@ -0,0 +1,83 @@
/*
* 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.inference.mutability
import com.intellij.openapi.application.runWriteAction
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.formatter.commitAndUnblockDocument
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.nj2k.descriptorByFileDirective
import org.jetbrains.kotlin.nj2k.inference.AbstractConstraintCollectorTest
import org.jetbrains.kotlin.nj2k.inference.common.*
import org.jetbrains.kotlin.nj2k.inference.common.collectors.CallExpressionConstraintCollector
import org.jetbrains.kotlin.nj2k.inference.common.collectors.CommonConstraintsCollector
import org.jetbrains.kotlin.nj2k.inference.common.collectors.FunctionConstraintsCollector
import org.jetbrains.kotlin.psi.KtConstructorCalleeExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtTypeElement
import org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
import java.io.File
abstract class AbstractMutabilityInferenceTest : AbstractConstraintCollectorTest() {
override fun createInferenceFacade(resolutionFacade: ResolutionFacade): InferenceFacade {
val typeEnhancer = MutabilityBoundTypeEnhancer()
return InferenceFacade(
object : ContextCollector(resolutionFacade) {
override fun ClassReference.getState(typeElement: KtTypeElement?) =
when (descriptor?.fqNameOrNull()) {
in MutabilityStateUpdater.mutableToImmutable -> State.UNKNOWN
in MutabilityStateUpdater.immutableToMutable -> State.UNKNOWN
else -> State.UNUSED
}
},
ConstraintsCollectorAggregator(
resolutionFacade,
MutabilityConstraintBoundProvider(),
listOf(
CommonConstraintsCollector(),
CallExpressionConstraintCollector(),
FunctionConstraintsCollector(ResolveSuperFunctionsProvider(resolutionFacade)),
MutabilityConstraintsCollector()
)
),
MutabilityBoundTypeCalculator(resolutionFacade, typeEnhancer),
MutabilityStateUpdater(),
MutabilityDefaultStateProvider(),
renderDebugTypes = true,
printDebugConstraints = true
)
}
override fun KtFile.afterInference(): Unit = runWriteAction {
commitAndUnblockDocument()
ShortenReferences.DEFAULT.process(this)
}
override fun KtFile.prepareFile() = runWriteAction {
fun KtTypeReference.updateMutability() {
MutabilityStateUpdater.changeState(
typeElement ?: return,
analyze()[BindingContext.TYPE, this]!!,
toMutable = true
)
for (typeArgument in typeElement!!.typeArgumentsAsTypes) {
typeArgument.updateMutability()
}
}
for (typeReference in collectDescendantsOfType<KtTypeReference>()) {
if (typeReference.parent is KtConstructorCalleeExpression) continue
typeReference.updateMutability()
}
deleteComments()
}
override fun getProjectDescriptor() =
descriptorByFileDirective(File(testDataPath, fileName()), isAllFilesPresentInTest())
}
@@ -0,0 +1,96 @@
/*
* 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.inference.mutability;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("nj2k/testData/inference/mutability")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class MutabilityInferenceTestGenerated extends AbstractMutabilityInferenceTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInMutability() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("nj2k/testData/inference/mutability"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("arrayList.kt")
public void testArrayList() throws Exception {
runTest("nj2k/testData/inference/mutability/arrayList.kt");
}
@TestMetadata("collectionMutableCalls.kt")
public void testCollectionMutableCalls() throws Exception {
runTest("nj2k/testData/inference/mutability/collectionMutableCalls.kt");
}
@TestMetadata("covariance.kt")
public void testCovariance() throws Exception {
runTest("nj2k/testData/inference/mutability/covariance.kt");
}
@TestMetadata("iteratorCall.kt")
public void testIteratorCall() throws Exception {
runTest("nj2k/testData/inference/mutability/iteratorCall.kt");
}
@TestMetadata("IteratorMutableCalls.kt")
public void testIteratorMutableCalls() throws Exception {
runTest("nj2k/testData/inference/mutability/IteratorMutableCalls.kt");
}
@TestMetadata("list.kt")
public void testList() throws Exception {
runTest("nj2k/testData/inference/mutability/list.kt");
}
@TestMetadata("listIteratorMutableCalls.kt")
public void testListIteratorMutableCalls() throws Exception {
runTest("nj2k/testData/inference/mutability/listIteratorMutableCalls.kt");
}
@TestMetadata("listMutableCalls.kt")
public void testListMutableCalls() throws Exception {
runTest("nj2k/testData/inference/mutability/listMutableCalls.kt");
}
@TestMetadata("listOfListsForEach.kt")
public void testListOfListsForEach() throws Exception {
runTest("nj2k/testData/inference/mutability/listOfListsForEach.kt");
}
@TestMetadata("listOfMutableList.kt")
public void testListOfMutableList() throws Exception {
runTest("nj2k/testData/inference/mutability/listOfMutableList.kt");
}
@TestMetadata("mapEntryMutableCalls.kt")
public void testMapEntryMutableCalls() throws Exception {
runTest("nj2k/testData/inference/mutability/mapEntryMutableCalls.kt");
}
@TestMetadata("mapMutableCalls.kt")
public void testMapMutableCalls() throws Exception {
runTest("nj2k/testData/inference/mutability/mapMutableCalls.kt");
}
@TestMetadata("setMutableCalls.kt")
public void testSetMutableCalls() throws Exception {
runTest("nj2k/testData/inference/mutability/setMutableCalls.kt");
}
}
@@ -26,7 +26,7 @@ abstract class AbstractNullabilityInferenceTest : AbstractConstraintCollectorTes
val typeEnhancer = NullabilityBoundTypeEnhancer(resolutionFacade)
return InferenceFacade(
object : ContextCollector(resolutionFacade) {
override fun ClassReference.getState(typeElement: KtTypeElement?): State? =
override fun ClassReference.getState(typeElement: KtTypeElement?): State =
State.UNKNOWN
},
ConstraintsCollectorAggregator(