New J2K: separate nullability inference from common one & nullability bug fixes

It will be needed for structure mutability inference

#KT-21467 fixed
#KT-32609 fixed
#KT-32572 fixed
#KT-24677 fixed
This commit is contained in:
Ilya Kirillov
2019-07-25 00:31:50 +03:00
parent d7960caf89
commit 2bd5a1f196
234 changed files with 3940 additions and 1983 deletions
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* 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.generators.tests
@@ -154,8 +143,9 @@ import org.jetbrains.kotlin.kapt3.test.AbstractClassFileToSourceStubConverterTes
import org.jetbrains.kotlin.kapt3.test.AbstractKotlinKaptContextTest
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.AbstractNullabilityAnalysisTest
import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.inference.common.AbstractCommonConstraintCollectorTest
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
@@ -987,15 +977,18 @@ fun main(args: Array<String>) {
testClass<AbstractNewJavaToKotlinConverterSingleFileTest> {
model("newJ2k", pattern = """^([^\.]+)\.java$""")
}
testClass<AbstractCommonConstraintCollectorTest> {
model("inference/common")
}
testClass<AbstractNullabilityInferenceTest> {
model("inference/nullability")
}
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
model("copyPaste", pattern = """^([^\.]+)\.java$""")
}
testClass<AbstractTextNewJavaToKotlinCopyPasteConversionTest> {
model("copyPastePlainText", pattern = """^([^\.]+)\.txt$""")
}
testClass<AbstractNullabilityAnalysisTest> {
model("nullabilityAnalysis")
}
}
testGroup("jps-plugin/jps-tests/test", "jps-plugin/testData") {
@@ -159,8 +159,9 @@ import org.jetbrains.kotlin.kapt3.test.AbstractClassFileToSourceStubConverterTes
import org.jetbrains.kotlin.kapt3.test.AbstractKotlinKaptContextTest
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.AbstractNullabilityAnalysisTest
import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.inference.common.AbstractCommonConstraintCollectorTest
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
@@ -992,15 +993,18 @@ fun main(args: Array<String>) {
testClass<AbstractNewJavaToKotlinConverterSingleFileTest> {
model("newJ2k", pattern = """^([^\.]+)\.java$""")
}
testClass<AbstractCommonConstraintCollectorTest> {
model("inference/common")
}
testClass<AbstractNullabilityInferenceTest> {
model("inference/nullability")
}
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
model("copyPaste", pattern = """^([^\.]+)\.java$""")
}
testClass<AbstractTextNewJavaToKotlinCopyPasteConversionTest> {
model("copyPastePlainText", pattern = """^([^\.]+)\.txt$""")
}
testClass<AbstractNullabilityAnalysisTest> {
model("nullabilityAnalysis")
}
}
testGroup("jps-plugin/jps-tests/test", "jps-plugin/testData") {
@@ -143,8 +143,9 @@ import org.jetbrains.kotlin.kapt3.test.AbstractClassFileToSourceStubConverterTes
import org.jetbrains.kotlin.kapt3.test.AbstractKotlinKaptContextTest
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.AbstractNullabilityAnalysisTest
import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.inference.common.AbstractCommonConstraintCollectorTest
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
@@ -952,15 +953,18 @@ fun main(args: Array<String>) {
testClass<AbstractNewJavaToKotlinConverterSingleFileTest> {
model("newJ2k", pattern = """^([^\.]+)\.java$""")
}
testClass<AbstractCommonConstraintCollectorTest> {
model("inference/common")
}
testClass<AbstractNullabilityInferenceTest> {
model("inference/nullability")
}
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
model("copyPaste", pattern = """^([^\.]+)\.java$""")
}
testClass<AbstractTextNewJavaToKotlinCopyPasteConversionTest> {
model("copyPastePlainText", pattern = """^([^\.]+)\.txt$""")
}
testClass<AbstractNullabilityAnalysisTest> {
model("nullabilityAnalysis")
}
}
/* There is no jps in AS
@@ -143,8 +143,9 @@ import org.jetbrains.kotlin.kapt3.test.AbstractClassFileToSourceStubConverterTes
import org.jetbrains.kotlin.kapt3.test.AbstractKotlinKaptContextTest
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.AbstractNullabilityAnalysisTest
import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.inference.common.AbstractCommonConstraintCollectorTest
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
@@ -956,15 +957,18 @@ fun main(args: Array<String>) {
testClass<AbstractNewJavaToKotlinConverterSingleFileTest> {
model("newJ2k", pattern = """^([^\.]+)\.java$""")
}
testClass<AbstractCommonConstraintCollectorTest> {
model("inference/common")
}
testClass<AbstractNullabilityInferenceTest> {
model("inference/nullability")
}
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
model("copyPaste", pattern = """^([^\.]+)\.java$""")
}
testClass<AbstractTextNewJavaToKotlinCopyPasteConversionTest> {
model("copyPastePlainText", pattern = """^([^\.]+)\.txt$""")
}
testClass<AbstractNullabilityAnalysisTest> {
model("nullabilityAnalysis")
}
}
/* There is no jps in AS
@@ -143,8 +143,9 @@ import org.jetbrains.kotlin.kapt3.test.AbstractClassFileToSourceStubConverterTes
import org.jetbrains.kotlin.kapt3.test.AbstractKotlinKaptContextTest
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.AbstractNullabilityAnalysisTest
import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.nj2k.inference.common.AbstractCommonConstraintCollectorTest
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
@@ -952,15 +953,18 @@ fun main(args: Array<String>) {
testClass<AbstractNewJavaToKotlinConverterSingleFileTest> {
model("newJ2k", pattern = """^([^\.]+)\.java$""")
}
testClass<AbstractCommonConstraintCollectorTest> {
model("inference/common")
}
testClass<AbstractNullabilityInferenceTest> {
model("inference/nullability")
}
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
model("copyPaste", pattern = """^([^\.]+)\.java$""")
}
testClass<AbstractTextNewJavaToKotlinCopyPasteConversionTest> {
model("copyPastePlainText", pattern = """^([^\.]+)\.txt$""")
}
testClass<AbstractNullabilityAnalysisTest> {
model("nullabilityAnalysis")
}
}
/* There is no jps in AS
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* 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.idea.test
@@ -20,6 +9,7 @@ import com.intellij.openapi.editor.Document
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiDocumentManager
import com.intellij.testFramework.LightPlatformTestCase
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.idea.caches.project.LibraryModificationTracker
@@ -29,11 +19,13 @@ import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import java.util.*
fun KtFile.dumpTextWithErrors(): String {
fun KtFile.dumpTextWithErrors(ignoreErrors: Set<DiagnosticFactory<*>> = emptySet()): String {
val text = text
if (InTextDirectivesUtils.isDirectiveDefined(text, "// DISABLE-ERRORS")) return text
val diagnostics = analyzeWithContent().diagnostics
val errors = diagnostics.filter { it.severity == Severity.ERROR }
val errors = diagnostics.filter { diagnostic ->
diagnostic.severity == Severity.ERROR && diagnostic.factory !in ignoreErrors
}
if (errors.isEmpty()) return text
val header = errors.joinToString("\n", postfix = "\n") { "// ERROR: " + DefaultErrorMessages.render(it).replace('\n', ' ') }
return header + text
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* 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.j2k
@@ -27,6 +16,7 @@ import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
import org.jetbrains.kotlin.idea.test.dumpTextWithErrors
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
import java.util.regex.Pattern
@@ -73,7 +63,8 @@ abstract class AbstractJavaToKotlinConverterSingleFileTest : AbstractJavaToKotli
var actual = reformat(rawConverted, project, reformatInFun)
if (prefix == "file") {
actual = createKotlinFile(actual).dumpTextWithErrors()
actual = createKotlinFile(actual)
.dumpTextWithErrors(setOf(element = ErrorsJvm.INTERFACE_STATIC_METHOD_CALL_FROM_JAVA6_TARGET_ERROR))
}
@@ -0,0 +1,259 @@
/*
* 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.common
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.builtIns
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
interface BoundTypeCalculator {
fun expressionsWithBoundType(): List<Pair<KtExpression, BoundType>>
fun KtExpression.boundType(inferenceContext: InferenceContext): BoundType
fun KotlinType.boundType(
typeVariable: TypeVariable? = null,
contextBoundType: BoundType? = null,
call: ResolvedCall<*>? = null,
isImplicitReceiver: Boolean = false,
forceEnhance: Boolean = false,
inferenceContext: InferenceContext
): BoundType
}
class BoundTypeCalculatorImpl(
private val resolutionFacade: ResolutionFacade,
private val enhancer: BoundTypeEnhancer
) : BoundTypeCalculator {
private val cache = mutableMapOf<KtExpression, BoundType>()
override fun expressionsWithBoundType() = cache.toList()
override fun KtExpression.boundType(inferenceContext: InferenceContext): BoundType = cache.getOrPut(this) {
calculateBoundType(inferenceContext, this)
}
private fun calculateBoundType(inferenceContext: InferenceContext, expression: KtExpression): BoundType = when {
expression.isNullExpression() -> BoundType.NULL
expression is KtParenthesizedExpression -> expression.expression?.boundType(inferenceContext)
expression is KtConstantExpression
|| expression is KtStringTemplateExpression
|| expression.node?.elementType == KtNodeTypes.BOOLEAN_CONSTANT
|| expression is KtBinaryExpression ->
BoundType.LITERAL
expression is KtQualifiedExpression -> expression.toBoundTypeAsQualifiedExpression(inferenceContext)
expression is KtBinaryExpressionWithTypeRHS -> expression.toBoundTypeAsCastExpression(inferenceContext)
expression is KtNameReferenceExpression -> expression.toBoundTypeAsReferenceExpression(inferenceContext)
expression is KtCallExpression -> expression.toBoundTypeAsCallableExpression(null, inferenceContext)
expression is KtLambdaExpression -> expression.toBoundTypeAsLambdaExpression(inferenceContext)
expression is KtLabeledExpression -> expression.baseExpression?.boundType(inferenceContext)
expression is KtIfExpression -> expression.toBoundTypeAsIfExpression(inferenceContext)
else -> null
}?.let { boundType ->
enhancer.enhance(expression, boundType, inferenceContext)
} ?: BoundType.LITERAL
private fun KtIfExpression.toBoundTypeAsIfExpression(inferenceContext: InferenceContext): BoundType? {
val isNullLiteralPossible = then?.isNullExpression() == true || `else`?.isNullExpression() == true
if (isNullLiteralPossible) {
return BoundType.NULL
}
return (then ?: `else`)?.boundType(inferenceContext) //TODO handle both cases separatelly
}
private fun KtLambdaExpression.toBoundTypeAsLambdaExpression(inferenceContext: InferenceContext): BoundType? {
val descriptor = functionLiteral.resolveToDescriptorIfAny(resolutionFacade).safeAs<FunctionDescriptor>() ?: return null
val builtIns = getType(analyze())?.builtIns ?: return null
val prototypeDescriptor = builtIns.getFunction(valueParameters.size)
val parameterBoundTypes = if (descriptor.valueParameters.size == valueParameters.size) {
valueParameters.map { parameter ->
parameter.typeReference?.typeElement?.let { typeElement ->
inferenceContext.typeElementToTypeVariable[typeElement]
}?.let { typeVariable ->
typeVariable.asBoundType()
} ?: return null
}
} else {
descriptor.valueParameters.map { parameter ->
parameter.type.boundType(inferenceContext = inferenceContext)
}
}
val returnTypeTypeVariable = inferenceContext.declarationToTypeVariable[functionLiteral] ?: return null
val returnTypeParameter =
TypeParameter(
BoundTypeImpl(
TypeVariableLabel(returnTypeTypeVariable),
returnTypeTypeVariable.typeParameters
),
Variance.OUT_VARIANCE
)
return BoundTypeImpl(
GenericLabel(prototypeDescriptor.classReference),
parameterBoundTypes.map { TypeParameter(it, Variance.IN_VARIANCE) } + returnTypeParameter
)
}
private fun KtNameReferenceExpression.toBoundTypeAsReferenceExpression(inferenceContext: InferenceContext): BoundType? =
mainReference
.resolve()
?.safeAs<KtDeclaration>()
?.let { declaration ->
val boundType = inferenceContext.declarationToTypeVariable[declaration]?.asBoundType()
?: return@let null
if (declaration.safeAs<KtParameter>()?.isVarArg == true) {
val arrayClassReference =
declaration.resolveToDescriptorIfAny(resolutionFacade)
?.safeAs<ValueParameterDescriptor>()
?.returnType
?.constructor
?.declarationDescriptor
?.safeAs<ClassDescriptor>()
?.classReference ?: NoClassReference
BoundTypeImpl(
GenericLabel(arrayClassReference),
listOf(TypeParameter(boundType, Variance.INVARIANT))
)
} else boundType
}
private fun KtBinaryExpressionWithTypeRHS.toBoundTypeAsCastExpression(inferenceContext: InferenceContext): BoundType? =
right?.typeElement
?.let { inferenceContext.typeElementToTypeVariable[it]?.asBoundType() }
private fun KtExpression.toBoundTypeAsCallableExpression(
contextBoundType: BoundType?,
inferenceContext: InferenceContext
): BoundType? {
val call = getResolvedCall(analyze(resolutionFacade)) ?: return null
val returnType = call.candidateDescriptor.original.returnType ?: return null
val callDescriptor = call.candidateDescriptor.original
val returnTypeVariable = inferenceContext.declarationDescriptorToTypeVariable[callDescriptor]
val withImplicitContextBoundType = contextBoundType
?: call.dispatchReceiver
?.type
?.boundType(inferenceContext = inferenceContext)
return returnType.boundType(
returnTypeVariable,
withImplicitContextBoundType,
call,
withImplicitContextBoundType != contextBoundType,
false,
inferenceContext
)
}
private fun KtQualifiedExpression.toBoundTypeAsQualifiedExpression(inferenceContext: InferenceContext): BoundType? {
val receiverBoundType = receiverExpression.boundType(inferenceContext)
val selectorExpression = selectorExpression ?: return null
return selectorExpression.toBoundTypeAsCallableExpression(receiverBoundType, inferenceContext)
}
override fun KotlinType.boundType(
typeVariable: TypeVariable?,
contextBoundType: BoundType?,
call: ResolvedCall<*>?,
isImplicitReceiver: Boolean,
forceEnhance: Boolean,
inferenceContext: InferenceContext
) = boundTypeUnenhanced(
typeVariable,
contextBoundType,
call,
isImplicitReceiver,
inferenceContext
)?.let { boundType ->
val needEnhance = run {
if (forceEnhance) return@run true
!inferenceContext.isInConversionScope(
call?.call?.calleeExpression?.mainReference?.resolve() ?: return@run false
)
}
if (needEnhance) enhancer.enhanceKotlinType(this, boundType, forceEnhance, inferenceContext)
else boundType
} ?: BoundType.LITERAL
private fun KotlinType.boundTypeUnenhanced(
typeVariable: TypeVariable?,
contextBoundType: BoundType?,
call: ResolvedCall<*>?,
isImplicitReceiver: Boolean,
inferenceContext: InferenceContext
): BoundType? {
return when (val target = constructor.declarationDescriptor) {
is ClassDescriptor ->
BoundTypeImpl(
typeVariable?.let { TypeVariableLabel(it) } ?: GenericLabel(target.classReference),
arguments.mapIndexed { i, argument ->
TypeParameter(
argument.type.boundTypeUnenhanced(
typeVariable?.typeParameters?.get(i)?.boundType?.label?.safeAs<TypeVariableLabel>()?.typeVariable,
contextBoundType,
call,
isImplicitReceiver,
inferenceContext
) ?: return null,
constructor.parameters[i].variance
)
}
)
is TypeParameterDescriptor -> {
val containingDeclaration = target.containingDeclaration
when {
containingDeclaration == call?.candidateDescriptor?.original -> {
val returnTypeVariable = inferenceContext.typeElementToTypeVariable[
call.call.typeArguments.getOrNull(target.index)?.typeReference?.typeElement ?: return null
] ?: return null
BoundTypeImpl(
TypeVariableLabel(returnTypeVariable),
returnTypeVariable.typeParameters
)
}
typeVariable != null && isImplicitReceiver ->
BoundTypeImpl(
TypeVariableLabel(typeVariable),
emptyList()
)
contextBoundType?.isReferenceToClass == true ->
contextBoundType.typeParameters.getOrNull(target.index)?.boundType
// `this` or `super` call case
containingDeclaration == call?.candidateDescriptor.safeAs<ConstructorDescriptor>()?.constructedClass -> {
val returnTypeVariable = inferenceContext.typeElementToTypeVariable[
call?.call?.typeArguments?.getOrNull(target.index)?.typeReference?.typeElement ?: return null
] ?: return null
BoundTypeImpl(
TypeVariableLabel(returnTypeVariable),
returnTypeVariable.typeParameters
)
}
else -> BoundTypeImpl(
TypeParameterLabel(target),
emptyList()
)
}
}
else -> null
}
}
}
@@ -0,0 +1,39 @@
/*
* 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.common
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.types.KotlinType
abstract class BoundTypeEnhancer {
abstract fun enhance(
expression: KtExpression,
boundType: BoundType,
inferenceContext: InferenceContext
): BoundType
abstract fun enhanceKotlinType(
type: KotlinType,
boundType: BoundType,
allowLowerEnhancement: Boolean,
inferenceContext: InferenceContext
): BoundType
object ID : BoundTypeEnhancer() {
override fun enhance(
expression: KtExpression,
boundType: BoundType,
inferenceContext: InferenceContext
): BoundType = boundType
override fun enhanceKotlinType(
type: KotlinType,
boundType: BoundType,
allowLowerEnhancement: Boolean,
inferenceContext: InferenceContext
): BoundType = boundType
}
}
@@ -0,0 +1,38 @@
/*
* 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.common
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
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>>) {
operator fun get(klass: ClassDescriptor, typeParameter: TypeParameterDescriptor): KtTypeElement? =
superTypes[klass]?.get(typeParameter)
companion object {
fun createFromKtClass(klass: KtClassOrObject, resolutionFacade: ResolutionFacade): ClassSubstitutor? {
val superTypes =
klass.superTypeListEntries.map { superType ->
val typeReference = superType.typeReference ?: return null
val type = typeReference.analyze(resolutionFacade)[BindingContext.TYPE, typeReference] ?: return null
val declarationDescriptor = type.constructor.declarationDescriptor ?: return null
declarationDescriptor to type.constructor.parameters.zip(
typeReference.typeElement?.typeArgumentsAsTypes ?: return null
) { parameter, argument ->
parameter to (argument.typeElement ?: return null)
}.toMap()
}.toMap()
return ClassSubstitutor(superTypes)
}
}
}
@@ -0,0 +1,81 @@
/*
* 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.common
enum class ConstraintPriority {
/* order of entries here used when solving system of constraints */
SUPER_DECLARATION,
INITIALIZER,
RETURN,
ASSIGNMENT,
PARAMETER,
RECEIVER_PARAMETER,
COMPARE_WITH_NULL,
USE_AS_RECEIVER,
}
sealed class Constraint {
abstract val priority: ConstraintPriority
}
class SubtypeConstraint(
var subtype: ConstraintBound,
var supertype: ConstraintBound,
override val priority: ConstraintPriority
) : Constraint() {
operator fun component1() = subtype
operator fun component2() = supertype
}
class EqualsConstraint(
var left: ConstraintBound,
var right: ConstraintBound,
override val priority: ConstraintPriority
) : Constraint() {
operator fun component1() = left
operator fun component2() = right
}
fun Constraint.copy() = when (this) {
is SubtypeConstraint -> SubtypeConstraint(subtype, supertype, priority)
is EqualsConstraint -> EqualsConstraint(left, right, priority)
}
sealed class ConstraintBound
class TypeVariableBound(val typeVariable: TypeVariable) : ConstraintBound()
class LiteralBound private constructor(val state: State) : ConstraintBound() {
companion object {
val UPPER = LiteralBound(State.UPPER)
val LOWER = LiteralBound(State.LOWER)
val UNKNOWN = LiteralBound(State.UNKNOWN)
}
}
val TypeVariable.constraintBound: TypeVariableBound
get() = TypeVariableBound(this)
val State.constraintBound: LiteralBound
get() = when (this) {
State.LOWER -> LiteralBound.LOWER
State.UPPER -> LiteralBound.UPPER
State.UNKNOWN -> LiteralBound.UNKNOWN
}
val BoundTypeLabel.constraintBound: ConstraintBound?
get() = when (this) {
is TypeVariableLabel -> typeVariable.constraintBound
is TypeParameterLabel -> null
is GenericLabel -> null
StarProjectionLabel -> null
NullLiteralLabel -> LiteralBound.UPPER
LiteralLabel -> LiteralBound.LOWER
}
val BoundType.constraintBound: ConstraintBound?
get() = when (this) {
is BoundTypeImpl -> label.constraintBound
is WithForcedStateBoundType -> forcedState.constraintBound
}
@@ -0,0 +1,136 @@
/*
* 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.common
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtTypeElement
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@Suppress("unused")
class ConstraintBuilder(
private val inferenceContext: InferenceContext,
private val boundTypeCalculator: BoundTypeCalculator
) : BoundTypeCalculator by boundTypeCalculator {
private val constraints = mutableListOf<Constraint>()
fun TypeVariable.isSubtypeOf(supertype: BoundType, priority: ConstraintPriority) {
asBoundType().isSubtypeOf(supertype, priority)
}
fun TypeVariable.isSubtypeOf(supertype: TypeVariable, priority: ConstraintPriority) {
asBoundType().isSubtypeOf(supertype.asBoundType(), priority)
}
fun KtExpression.isSubtypeOf(supertype: KtExpression, priority: ConstraintPriority) {
boundType().isSubtypeOf(supertype.boundType(), priority)
}
fun KtExpression.isSubtypeOf(supertype: TypeVariable, priority: ConstraintPriority) {
boundType().isSubtypeOf(supertype.asBoundType(), priority)
}
fun KtExpression.isSubtypeOf(supertype: BoundType, priority: ConstraintPriority) {
boundType().isSubtypeOf(supertype, priority)
}
fun KtExpression.isTheSameTypeAs(other: State, priority: ConstraintPriority) {
boundType().label.safeAs<TypeVariableLabel>()?.typeVariable?.let { typeVariable ->
constraints += EqualsConstraint(typeVariable.constraintBound, other.constraintBound, priority)
}
}
fun TypeVariable.isTheSameTypeAs(other: BoundType, priority: ConstraintPriority) {
asBoundType().isTheSameTypeAs(other, priority)
}
fun TypeVariable.isTheSameTypeAs(
other: TypeVariable,
priority: ConstraintPriority,
ignoreTypeVariables: Set<TypeVariable> = emptySet()
) {
asBoundType().isTheSameTypeAs(other.asBoundType(), priority, ignoreTypeVariables)
}
fun KtTypeElement.isTheSameTypeAs(
other: KtTypeElement,
priority: ConstraintPriority,
ignoreTypeVariables: Set<TypeVariable> = emptySet()
) {
inferenceContext.typeElementToTypeVariable[this]
?.asBoundType()
?.isTheSameTypeAs(
inferenceContext.typeElementToTypeVariable[other]?.asBoundType() ?: return,
priority,
ignoreTypeVariables
)
}
fun TypeVariable.isTheSameTypeAs(
other: KtTypeElement,
priority: ConstraintPriority,
ignoreTypeVariables: Set<TypeVariable> = emptySet()
) {
asBoundType().isTheSameTypeAs(
inferenceContext.typeElementToTypeVariable[other]?.asBoundType() ?: return,
priority,
ignoreTypeVariables
)
}
fun BoundType.isTheSameTypeAs(
other: KtTypeElement,
priority: ConstraintPriority,
ignoreTypeVariables: Set<TypeVariable> = emptySet()
) {
isTheSameTypeAs(
inferenceContext.typeElementToTypeVariable[other]?.asBoundType() ?: return,
priority,
ignoreTypeVariables
)
}
fun BoundType.isTheSameTypeAs(
other: BoundType,
priority: ConstraintPriority,
ignoreTypeVariables: Set<TypeVariable> = emptySet()
) {
(typeParameters zip other.typeParameters).forEach { (left, right) ->
left.boundType.isTheSameTypeAs(right.boundType, priority, ignoreTypeVariables)
}
if (typeVariable !in ignoreTypeVariables && other.typeVariable !in ignoreTypeVariables) {
constraints += EqualsConstraint(
constraintBound ?: return,
other.constraintBound ?: return,
priority
)
}
}
fun BoundType.isSubtypeOf(supertype: BoundType, priority: ConstraintPriority) {
(typeParameters zip supertype.typeParameters).forEach { (left, right) ->
when (left.variance) {
Variance.OUT_VARIANCE -> left.boundType.isSubtypeOf(right.boundType, priority)
Variance.IN_VARIANCE -> right.boundType.isSubtypeOf(left.boundType, priority)
Variance.INVARIANT -> right.boundType.isTheSameTypeAs(left.boundType, priority)
}
}
constraints += SubtypeConstraint(
constraintBound ?: return,
supertype.constraintBound ?: return,
priority
)
}
fun KtExpression.boundType() = with(boundTypeCalculator) {
this@boundType.boundType(inferenceContext)
}
val collectedConstraints: List<Constraint>
get() = constraints
}
@@ -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.inference.common
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.nj2k.inference.common.collectors.ConstraintsCollector
import org.jetbrains.kotlin.nj2k.parentOfType
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtImportDirective
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
class ConstraintsCollectorAggregator(
private val resolutionFacade: ResolutionFacade,
private val collectors: List<ConstraintsCollector>
) {
fun collectConstraints(
boundTypeCalculator: BoundTypeCalculator,
inferenceContext: InferenceContext,
elements: List<KtElement>
): List<Constraint> {
val constraintsBuilder = ConstraintBuilder(inferenceContext, boundTypeCalculator)
for (element in elements) {
element.forEachDescendantOfType<KtElement> { innerElement ->
if (innerElement.parentOfType<KtImportDirective>() != null) return@forEachDescendantOfType
for (collector in collectors) {
with(collector) {
constraintsBuilder.collectConstraints(
innerElement,
boundTypeCalculator,
inferenceContext,
resolutionFacade
)
}
}
}
}
return constraintsBuilder.collectedConstraints
}
}
@@ -0,0 +1,196 @@
/*
* 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.common
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
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.psi.*
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
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) {
private fun KotlinType.classReference(): ClassReference? =
when (val descriptor = constructor.declarationDescriptor) {
is ClassDescriptor -> descriptor.classReference
is TypeParameterDescriptor -> TypeParameterReference(descriptor)
else -> null
}
private fun KtTypeReference.classReference(): ClassReference? =
analyze()[BindingContext.TYPE, this]?.classReference()
private fun KtTypeElement.toData(): TypeElementData {
val typeReference = parent as? KtTypeReference ?: return TypeElementDataImpl(this)
val typeParameterDescriptor = analyze(resolutionFacade)[BindingContext.TYPE, typeReference]
?.constructor
?.declarationDescriptor
?.safeAs<TypeParameterDescriptor>() ?: return TypeElementDataImpl(this)
return TypeParameterElementData(this, typeParameterDescriptor)
}
fun collectTypeVariables(elements: List<KtElement>): InferenceContext {
val declarationToTypeVariable = mutableMapOf<KtNamedDeclaration, TypeVariable>()
val typeElementToTypeVariable = mutableMapOf<KtTypeElement, TypeVariable>()
val typeBasedTypeVariables = mutableListOf<TypeBasedTypeVariable>()
fun KtTypeReference.toBoundType(defaultState: State? = null): BoundType? {
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 typeArguments =
if (classReference is DescriptorClassReference) {
typeElement.typeArgumentsAsTypes.zip(
classReference.descriptor.declaredTypeParameters
) { typeArgument, typeParameter ->
TypeParameter(
if (typeArgument == null) {
BoundTypeImpl(StarProjectionLabel, emptyList())
} else typeArgument.toBoundType() ?: BoundType.STAR_PROJECTION,
typeParameter.variance
)
}
} else emptyList()
return if (state == null) {
BoundTypeImpl(
GenericLabel(classReference),
typeArguments
)
} else {
val typeVariable = TypeElementBasedTypeVariable(
classReference,
typeArguments,
typeElement.toData(),
state
)
typeElementToTypeVariable[typeElement] = typeVariable
typeVariable.asBoundType()
}
}
fun KotlinType.toBoundType(): BoundType? {
val classReference = classReference() ?: NoClassReference
val state = classReference.getState(typeElement = null)
val typeArguments =
if (classReference is DescriptorClassReference) {
arguments.zip(classReference.descriptor.declaredTypeParameters) { typeArgument, typeParameter ->
TypeParameter(
typeArgument.type.toBoundType() ?: BoundType.STAR_PROJECTION,
typeParameter.variance
)
}
} else emptyList()
return if (state == null) {
BoundTypeImpl(
GenericLabel(classReference),
typeArguments
)
} else {
val typeVariable = TypeBasedTypeVariable(
classReference,
typeArguments,
this,
state
)
typeBasedTypeVariables += typeVariable
typeVariable.asBoundType()
}
}
val substitutors = mutableMapOf<ClassDescriptor, ClassSubstitutor>()
for (element in elements) {
element.forEachDescendantOfType<KtExpression> { expression ->
if (expression is KtCallableDeclaration
&& (expression is KtParameter
|| expression is KtProperty
|| expression is KtNamedFunction)
) run {
val typeReference = expression.typeReference ?: return@run
val typeVariable = typeReference.toBoundType()?.typeVariable ?: return@run
declarationToTypeVariable[expression] = typeVariable
}
if (expression is KtTypeParameterListOwner) {
for (typeParameter in expression.typeParameters) {
typeParameter.extendsBound?.toBoundType(defaultState = State.UPPER)
}
for (constraint in expression.typeConstraints) {
constraint.boundTypeReference?.toBoundType(defaultState = State.UPPER)
}
}
when (expression) {
is KtClass -> {
for (entry in expression.superTypeListEntries) {
for (argument in entry.typeReference?.typeElement?.typeArgumentsAsTypes ?: continue) {
argument.toBoundType()
}
}
val descriptor =
expression.resolveToDescriptorIfAny(resolutionFacade) ?: return@forEachDescendantOfType
substitutors[descriptor] =
ClassSubstitutor.createFromKtClass(expression, resolutionFacade) ?: return@forEachDescendantOfType
for (typeParameter in expression.typeParameters) {
val typeVariable = typeParameter.resolveToDescriptorIfAny(resolutionFacade)
?.safeAs<TypeParameterDescriptor>()
?.defaultType
?.toBoundType()
?.typeVariable
?: continue
declarationToTypeVariable[typeParameter] = typeVariable
}
}
is KtCallExpression ->
for (typeArgument in expression.typeArguments) {
typeArgument.typeReference?.toBoundType()
}
is KtLambdaExpression -> {
val context = expression.analyze(resolutionFacade)
val returnType = expression.getType(context)?.arguments?.lastOrNull()?.type ?: return@forEachDescendantOfType
val typeVariable = returnType.toBoundType()?.typeVariable ?: return@forEachDescendantOfType
declarationToTypeVariable[expression.functionLiteral] = typeVariable
}
is KtBinaryExpressionWithTypeRHS -> {
val typeReference = expression.right ?: return@forEachDescendantOfType
val typeElement = typeReference.typeElement ?: return@forEachDescendantOfType
val typeVariable = typeReference.toBoundType()?.typeVariable ?: return@forEachDescendantOfType
typeElementToTypeVariable[typeElement] = typeVariable
}
}
}
}
val typeVariables =
(typeElementToTypeVariable.values + declarationToTypeVariable.values + typeBasedTypeVariables).distinct()
return InferenceContext(
elements,
typeVariables,
typeElementToTypeVariable,
declarationToTypeVariable,
declarationToTypeVariable.mapNotNull { (key, value) ->
key.resolveToDescriptorIfAny(resolutionFacade)?.let { it to value }
}.toMap(),
substitutors
)
}
abstract fun ClassReference.getState(typeElement: KtTypeElement?): State?
}
@@ -0,0 +1,81 @@
/*
* 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.common
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtTypeElement
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
class DebugPrinter(private val inferenceContext: InferenceContext) {
private val namer = Namer(inferenceContext)
val TypeVariable.name: String
get() = namer.name(this)
private fun ClassReference.asString() = when (this) {
is DescriptorClassReference -> descriptor.name.toString()
is TypeParameterReference -> descriptor.name.toString()
is NoClassReference -> "NoClassRef"
}
fun BoundTypeLabel.asString(): String = when (this) {
is TypeVariableLabel -> typeVariable.name + "@" + typeVariable.classReference.asString()
is TypeParameterLabel -> typeParameter.name.asString()
is GenericLabel -> classReference.asString()
StarProjectionLabel -> "*"
NullLiteralLabel -> "NULL"
LiteralLabel -> "LIT"
}
fun State.asString() = when (this) {
State.LOWER -> "L"
State.UPPER -> "U"
State.UNKNOWN -> "?"
}
fun BoundType.asString(): String = buildString {
append(label.asString())
if (typeParameters.isNotEmpty()) {
typeParameters.joinTo(this, ", ", "<", ">") { it.boundType.asString() }
}
if (this@asString is WithForcedStateBoundType) {
append("!!")
append(forcedState.asString())
}
}
fun Constraint.asString() = when (this) {
is EqualsConstraint -> "${left.asString()} := ${right.asString()}"
is SubtypeConstraint -> "${subtype.asString()} <: ${supertype.asString()}"
} + " due to '$priority'"
private fun ConstraintBound.asString(): String = when (this) {
is LiteralBound -> state.toString()
is TypeVariableBound -> typeVariable.name
}
fun PsiElement.addTypeVariablesNames() {
val factory = KtPsiFactory(this)
for (typeElement in collectDescendantsOfType<KtTypeElement>()) {
val typeVariableName = this@DebugPrinter.inferenceContext.typeElementToTypeVariable[typeElement]?.name ?: continue
val comment = factory.createComment("/*$typeVariableName@*/")
typeElement.parent.addBefore(comment, typeElement)
}
}
}
private class Namer(inferenceContext: InferenceContext) {
val names = inferenceContext.typeVariables.mapIndexed { index, typeVariable ->
typeVariable to "T$index"
}.toMap()
fun name(typeVariable: TypeVariable): String =
names.getValue(typeVariable)
}
@@ -0,0 +1,32 @@
/*
* 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.common
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.core.util.range
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtTypeElement
data class InferenceContext(
val elements: List<KtElement>,
val typeVariables: List<TypeVariable>,
val typeElementToTypeVariable: Map<KtTypeElement, TypeVariable>,
val declarationToTypeVariable: Map<KtNamedDeclaration, TypeVariable>,
val declarationDescriptorToTypeVariable: Map<DeclarationDescriptor, TypeVariable>,
val classSubstitutions: Map<ClassDescriptor, ClassSubstitutor>
) {
fun isInConversionScope(childCandidate: PsiElement) = when (childCandidate) {
is KtElement -> elements.any { element ->
element.containingKtFile == childCandidate.containingKtFile
&& element.range.contains(childCandidate.range)
}
else -> false
}
}
@@ -0,0 +1,52 @@
/*
* 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.common
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtPsiFactory
class InferenceFacade(
private val typeVariablesCollector: ContextCollector,
private val constraintsCollectorAggregator: ConstraintsCollectorAggregator,
private val boundTypeCalculator: BoundTypeCalculator,
private val stateUpdater: StateUpdater,
private val renderDebugTypes: Boolean = false,
private val printDebugConstraints: Boolean = false
) {
fun runOn(elements: List<KtElement>) {
val inferenceContext = typeVariablesCollector.collectTypeVariables(elements)
val constraints = constraintsCollectorAggregator.collectConstraints(boundTypeCalculator, inferenceContext, elements)
val initialConstraints = if (renderDebugTypes) constraints.map { it.copy() } else null
Solver(inferenceContext, printDebugConstraints).solveConstraints(constraints)
if (renderDebugTypes) {
with(DebugPrinter(inferenceContext)) {
runWriteAction {
for ((expression, boundType) in boundTypeCalculator.expressionsWithBoundType()) {
val comment = KtPsiFactory(expression.project).createComment("/*${boundType.asString()}*/")
expression.parent.addAfter(comment, expression)
}
for (element in elements) {
element.addTypeVariablesNames()
}
for (element in elements) {
val factory = KtPsiFactory(element)
element.add(factory.createNewLine(lineBreaks = 2))
for (constraint in initialConstraints!!) {
element.add(factory.createComment("//${constraint.asString()}"))
element.add(factory.createNewLine(lineBreaks = 1))
}
}
}
}
}
runWriteAction {
stateUpdater.updateStates(inferenceContext)
}
}
}
@@ -3,27 +3,29 @@
* 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.nullabilityAnalysis
package org.jetbrains.kotlin.nj2k.inference.common
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal class Solver(
private val analysisContext: AnalysisContext,
private val analysisContext: InferenceContext,
private val printConstraints: Boolean
) {
private val printer = Printer(analysisContext)
private val printer = DebugPrinter(analysisContext)
private fun List<Constraint>.printDebugInfo(step: Int) =
with(printer) {
if (printConstraints) {
println("Step $step:")
println(listConstrains())
for (constraint in this@printDebugInfo) {
println(constraint.asString())
}
println()
println("type variables:")
for (typeVariable in analysisContext.typeElementToTypeVariable.values) {
println("${typeVariable.name} := ${typeVariable.nullability}")
for (typeVariable in analysisContext.typeVariables) {
println("${typeVariable.name} := ${typeVariable.state}")
}
println("---------------\n")
}
@@ -31,7 +33,7 @@ internal class Solver(
fun solveConstraints(constraints: List<Constraint>) {
val mutableConstraints = constraints.toMutableList()
var currentStep = ConstraintCameFrom.values().first()
var currentStep = ConstraintPriority.values().first()
var i = 0
do {
@@ -40,22 +42,22 @@ internal class Solver(
printDebugInfo(i)
somethingChanged = handleConstraintsWithNullableLowerBound(currentStep) || somethingChanged
somethingChanged = handleConstraintsWithNotNullUpperBound(currentStep) || somethingChanged
somethingChanged = handleEqualConstraints(currentStep) || somethingChanged
somethingChanged = handleEqualsConstraints(currentStep) || somethingChanged
somethingChanged = substituteConstraints() || somethingChanged
cleanConstraints()
}
if (!somethingChanged) {
if (currentStep.ordinal < ConstraintCameFrom.values().lastIndex) {
currentStep = ConstraintCameFrom.values()[currentStep.ordinal + 1]
if (currentStep.ordinal < ConstraintPriority.values().lastIndex) {
currentStep = ConstraintPriority.values()[currentStep.ordinal + 1]
somethingChanged = true
}
}
if (!somethingChanged) {
val typeVariable = mutableConstraints.getTypeVariableAsEqualsOrUpperBound()
if (typeVariable != null) {
typeVariable.setNullabilityIfNotFixed(Nullability.NOT_NULL)
typeVariable.setStateIfNotFixed(State.LOWER)
somethingChanged = true
}
}
@@ -65,21 +67,14 @@ internal class Solver(
private fun MutableList<Constraint>.cleanConstraints() {
val newConstraints =
distinct()
.filterNot { constraint ->
constraint is SubtypeConstraint
&& constraint.lowerBound is LiteralBound
&& constraint.upperBound is LiteralBound
}
if (newConstraints.size < size) {
clear()
addAll(newConstraints)
removeIf { constraint ->
constraint is SubtypeConstraint
&& constraint.subtype is LiteralBound
&& constraint.supertype is LiteralBound
}
}
private fun MutableList<Constraint>.handleConstraintsWithNullableLowerBound(step: ConstraintCameFrom): Boolean {
private fun MutableList<Constraint>.handleConstraintsWithNullableLowerBound(step: ConstraintPriority): Boolean {
var somethingChanged = false
val nullableConstraints = getConstraintsWithNullableLowerBound(step)
if (nullableConstraints.isNotEmpty()) {
@@ -87,14 +82,14 @@ internal class Solver(
for ((_, upperBound) in nullableConstraints) {
if (upperBound is TypeVariableBound) {
somethingChanged = true
upperBound.typeVariable.setNullabilityIfNotFixed(Nullability.NULLABLE)
upperBound.typeVariable.setStateIfNotFixed(State.UPPER)
}
}
}
return somethingChanged
}
private fun MutableList<Constraint>.handleConstraintsWithNotNullUpperBound(step: ConstraintCameFrom): Boolean {
private fun MutableList<Constraint>.handleConstraintsWithNotNullUpperBound(step: ConstraintPriority): Boolean {
var somethingChanged = false
val nullableConstraints = getConstraintsWithNotNullUpperBound(step)
if (nullableConstraints.isNotEmpty()) {
@@ -102,37 +97,36 @@ internal class Solver(
for ((lowerBound, _) in nullableConstraints) {
if (lowerBound is TypeVariableBound) {
somethingChanged = true
lowerBound.typeVariable.setNullabilityIfNotFixed(Nullability.NOT_NULL)
lowerBound.typeVariable.setStateIfNotFixed(State.LOWER)
}
}
}
return somethingChanged
}
private fun ConstraintBound.fixedNullability(): Nullability? =
when {
this is LiteralBound -> nullability
this is TypeVariableBound && typeVariable.isFixed -> typeVariable.nullability
else -> null
}
private fun ConstraintBound.fixedState(): State? = when {
this is LiteralBound -> state
this is TypeVariableBound && typeVariable.isFixed -> typeVariable.state
else -> null
}
private fun MutableList<Constraint>.handleEqualConstraints(step: ConstraintCameFrom): Boolean {
private fun MutableList<Constraint>.handleEqualsConstraints(step: ConstraintPriority): Boolean {
var somethingChanged = false
val equalsConstraints = filterIsInstance<EqualConstraint>().filter { it.cameFrom <= step }
val equalsConstraints = filterIsInstance<EqualsConstraint>().filter { it.priority <= step }
if (equalsConstraints.isNotEmpty()) {
for (constraint in equalsConstraints) {
val (leftBound, rightBound) = constraint
when {
leftBound is TypeVariableBound && rightBound.fixedNullability() != null -> {
leftBound is TypeVariableBound && rightBound.fixedState() != null -> {
this -= constraint
somethingChanged = true
leftBound.typeVariable.setNullabilityIfNotFixed(rightBound.fixedNullability()!!)
leftBound.typeVariable.setStateIfNotFixed(rightBound.fixedState()!!)
}
rightBound is TypeVariableBound && leftBound.fixedNullability() != null -> {
rightBound is TypeVariableBound && leftBound.fixedState() != null -> {
this -= constraint
somethingChanged = true
rightBound.typeVariable.setNullabilityIfNotFixed(leftBound.fixedNullability()!!)
rightBound.typeVariable.setStateIfNotFixed(leftBound.fixedState()!!)
}
}
}
@@ -148,39 +142,38 @@ internal class Solver(
val (lower, upper) = constraint
if (lower is TypeVariableBound && lower.typeVariable.isFixed) {
somethingChanged = true
constraint.lowerBound = LiteralBound(lower.typeVariable.nullability)
constraint.subtype = lower.typeVariable.state.constraintBound
}
if (upper is TypeVariableBound && upper.typeVariable.isFixed) {
somethingChanged = true
constraint.upperBound = LiteralBound(upper.typeVariable.nullability)
constraint.supertype = upper.typeVariable.state.constraintBound
}
}
}
return somethingChanged
}
private fun List<Constraint>.getConstraintsWithNullableLowerBound(cameFrom: ConstraintCameFrom) =
private fun List<Constraint>.getConstraintsWithNullableLowerBound(priority: ConstraintPriority) =
filterIsInstance<SubtypeConstraint>().filter { constraint ->
constraint.cameFrom <= cameFrom
&& constraint.lowerBound.safeAs<LiteralBound>()?.nullability == Nullability.NULLABLE
constraint.priority <= priority
&& constraint.subtype.safeAs<LiteralBound>()?.state == State.UPPER
}
private fun List<Constraint>.getConstraintsWithNotNullUpperBound(cameFrom: ConstraintCameFrom) =
private fun List<Constraint>.getConstraintsWithNotNullUpperBound(priority: ConstraintPriority) =
filterIsInstance<SubtypeConstraint>().filter { constraint ->
constraint.cameFrom <= cameFrom
&& constraint.upperBound.safeAs<LiteralBound>()?.nullability == Nullability.NOT_NULL
constraint.priority <= priority
&& constraint.supertype.safeAs<LiteralBound>()?.state == State.LOWER
}
private fun List<Constraint>.getTypeVariableAsEqualsOrUpperBound(): TypeVariable? =
asSequence().filterIsInstance<SubtypeConstraint>()
.map { it.upperBound }
.map { it.supertype }
.firstIsInstanceOrNull<TypeVariableBound>()
?.typeVariable
?: asSequence().filterIsInstance<EqualConstraint>()
.flatMap { sequenceOf(it.leftBound, it.rightBound) }
?: asSequence().filterIsInstance<EqualsConstraint>()
.flatMap { sequenceOf(it.left, it.right) }
.firstIsInstanceOrNull<TypeVariableBound>()
?.typeVariable
}
@@ -0,0 +1,12 @@
/*
* 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.common
enum class State {
LOWER,
UPPER,
UNKNOWN
}
@@ -0,0 +1,10 @@
/*
* 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.common
abstract class StateUpdater {
abstract fun updateStates(inferenceContext: InferenceContext)
}
@@ -0,0 +1,58 @@
/*
* 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.common
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.nj2k.*
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
abstract class SuperFunctionsProvider {
abstract fun provideSuperFunctionDescriptors(function: KtFunction): List<FunctionDescriptor>?
lateinit var inferenceContext: InferenceContext
}
class ResolveSuperFunctionsProvider(private val resolutionFacade: ResolutionFacade) : SuperFunctionsProvider() {
override fun provideSuperFunctionDescriptors(function: KtFunction): List<FunctionDescriptor>? =
function.resolveToDescriptorIfAny(resolutionFacade)
?.safeAs<FunctionDescriptor>()
?.original
?.overriddenDescriptors
?.toList()
}
class ByInfoSuperFunctionsProvider(
private val resolutionFacade: ResolutionFacade,
private val converterContext: NewJ2kConverterContext
) : SuperFunctionsProvider() {
private val labelToFunction by lazy(LazyThreadSafetyMode.NONE) {
val functions = mutableMapOf<JKElementInfoLabel, KtNamedFunction>()
for (element in inferenceContext.elements) {
element.forEachDescendantOfType<KtNamedFunction> { function ->
val label = function.nameIdentifier?.getLabel() ?: return@forEachDescendantOfType
functions += label to function
}
}
functions
}
override fun provideSuperFunctionDescriptors(function: KtFunction): List<FunctionDescriptor>? =
function.nameIdentifier?.elementInfo(converterContext)?.firstIsInstanceOrNull<FunctionInfo>()
?.superFunctions
?.mapNotNull { superFunction ->
when (superFunction) {
is ExternalSuperFunctionInfo -> superFunction.descriptor
is InternalSuperFunctionInfo ->
labelToFunction[superFunction.label]?.resolveToDescriptorIfAny(resolutionFacade)
}
}
}
@@ -0,0 +1,79 @@
/*
* 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.common
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
sealed class ClassReference
class DescriptorClassReference(val descriptor: ClassDescriptor) : ClassReference()
class TypeParameterReference(val descriptor: TypeParameterDescriptor) : ClassReference()
object NoClassReference : ClassReference()
val ClassDescriptor.classReference: DescriptorClassReference
get() = DescriptorClassReference(this)
val ClassReference.descriptor: ClassDescriptor?
get() = safeAs<DescriptorClassReference>()?.descriptor
class TypeParameter(val boundType: BoundType, val variance: Variance)
sealed class BoundType {
abstract val label: BoundTypeLabel
abstract val typeParameters: List<TypeParameter>
companion object {
val LITERAL = BoundTypeImpl(LiteralLabel, emptyList())
val STAR_PROJECTION = BoundTypeImpl(StarProjectionLabel, emptyList())
val NULL = BoundTypeImpl(NullLiteralLabel, emptyList())
}
}
class BoundTypeImpl(
override val label: BoundTypeLabel,
override val typeParameters: List<TypeParameter>
) : BoundType()
class WithForcedStateBoundType(
val original: BoundType,
val forcedState: State
) : BoundType() {
override val label: BoundTypeLabel
get() = original.label
override val typeParameters: List<TypeParameter>
get() = original.typeParameters
}
fun BoundType.withEnhancementFrom(from: BoundType) = when (from) {
is BoundTypeImpl -> this
is WithForcedStateBoundType -> WithForcedStateBoundType(this, from.forcedState)
}
fun BoundType.enhanceWith(state: State?) =
if (state != null) WithForcedStateBoundType(this, state)
else this
sealed class BoundTypeLabel
class TypeVariableLabel(val typeVariable: TypeVariable) : BoundTypeLabel()
class TypeParameterLabel(val typeParameter: TypeParameterDescriptor) : BoundTypeLabel()
class GenericLabel(val classReference: ClassReference) : BoundTypeLabel()
object NullLiteralLabel : BoundTypeLabel()
object LiteralLabel : BoundTypeLabel()
object StarProjectionLabel : BoundTypeLabel()
fun TypeVariable.asBoundType(): BoundType =
BoundTypeImpl(TypeVariableLabel(this), typeParameters)
val BoundType.typeVariable: TypeVariable?
get() = label.safeAs<TypeVariableLabel>()?.typeVariable
val BoundType.isReferenceToClass: Boolean
get() = label is TypeVariableLabel || label is GenericLabel
@@ -0,0 +1,112 @@
/*
* 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.common.collectors
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.nj2k.inference.common.*
import org.jetbrains.kotlin.psi.KtCallElement
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
import org.jetbrains.kotlin.resolve.calls.components.isVararg
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class CallExpressionConstraintCollector : ConstraintsCollector() {
override fun ConstraintBuilder.collectConstraints(
element: KtElement,
boundTypeCalculator: BoundTypeCalculator,
inferenceContext: InferenceContext,
resolutionFacade: ResolutionFacade
) {
if (element !is KtCallElement) return
val call = element.resolveToCall(resolutionFacade) ?: return
val originalDescriptor = call.candidateDescriptor.safeAs<FunctionDescriptor>()?.original ?: return
val valueArguments = call.valueArgumentsByIndex.orEmpty()
val typeParameterBindings =
call.candidateDescriptor.typeParameters.zip(call.call.typeArguments) { typeParameter, typeArgument ->
typeArgument.typeReference?.typeElement?.let {
inferenceContext.typeElementToTypeVariable[it]
}?.let { typeVariable ->
typeParameter?.let { it to typeVariable }
}
}.filterNotNull().toMap()
val receiverExpressionBoundType = element.getQualifiedExpressionForSelector()
?.receiverExpression
?.boundType(inferenceContext)
fun KotlinType.contextBoundType() = boundType(
contextBoundType = receiverExpressionBoundType,
call = call,
inferenceContext = inferenceContext
)
fun BoundType.substituteTypeParameters(): BoundType =
BoundTypeImpl(
when (label) {
is TypeVariableLabel -> (label as TypeVariableLabel)
.typeVariable
.safeAs<TypeElementBasedTypeVariable>()
?.typeElement
?.safeAs<TypeParameterElementData>()
?.let { typeParameterData ->
typeParameterBindings[typeParameterData.typeParameterDescriptor]
}?.let { typeVariable ->
TypeVariableLabel(typeVariable)
} ?: label
else -> label
},
typeParameters.map { typeParameter ->
TypeParameter(
typeParameter.boundType.substituteTypeParameters(),
typeParameter.variance
)
}
).withEnhancementFrom(this)
fun ParameterDescriptor.boundType() =
inferenceContext.declarationDescriptorToTypeVariable[this]
?.asBoundType()
?.substituteTypeParameters()
?: original.type.contextBoundType()
if (receiverExpressionBoundType != null) run {
val receiverBoundType =
(originalDescriptor.extensionReceiverParameter ?: originalDescriptor.dispatchReceiverParameter)?.boundType()
?: return@run
receiverExpressionBoundType.isSubtypeOf(receiverBoundType, ConstraintPriority.RECEIVER_PARAMETER)
}
val parameterToArgument = call.candidateDescriptor.valueParameters.let { parameters ->
valueArguments.mapIndexed { i, arguments ->
val parameter = parameters[i]
val parameterBoundType = parameter.boundType()
val parameterBoundTypeConsideringVararg =
if (parameter.isVararg && KotlinBuiltIns.isArrayOrPrimitiveArray(parameter.type)) {
if (KotlinBuiltIns.isPrimitiveArray(parameter.type))
BoundTypeImpl(
GenericLabel(NoClassReference),//not important as it just a primitive type
emptyList()
) else parameterBoundType.typeParameters[0].boundType
} else parameterBoundType
arguments.arguments.map { argument ->
parameterBoundTypeConsideringVararg to argument
}
}
}.flatten()
for ((parameter, argument) in parameterToArgument) {
val argumentExpression = argument.getArgumentExpression() ?: continue
argumentExpression.isSubtypeOf(parameter, ConstraintPriority.PARAMETER)
}
}
}
@@ -0,0 +1,135 @@
/*
* 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.common.collectors
import org.jetbrains.kotlin.descriptors.ClassDescriptor
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.psi.*
import org.jetbrains.kotlin.psi.psiUtil.asAssignment
import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunction
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class CommonConstraintsCollector : ConstraintsCollector() {
override fun ConstraintBuilder.collectConstraints(
element: KtElement,
boundTypeCalculator: BoundTypeCalculator,
inferenceContext: InferenceContext,
resolutionFacade: ResolutionFacade
) = with(boundTypeCalculator) {
when {
element is KtBinaryExpressionWithTypeRHS && KtPsiUtil.isUnsafeCast(element) -> {
element.right?.typeElement?.let { inferenceContext.typeElementToTypeVariable[it] }?.also { typeVariable ->
element.left.isSubtypeOf(typeVariable, ConstraintPriority.ASSIGNMENT)
}
}
element is KtBinaryExpression && element.asAssignment() != null -> {
element.right?.isSubtypeOf(element.left ?: return, ConstraintPriority.ASSIGNMENT)
}
element is KtVariableDeclaration -> {
inferenceContext.declarationToTypeVariable[element]?.also { typeVariable ->
element.initializer?.isSubtypeOf(typeVariable, ConstraintPriority.INITIALIZER)
}
}
element is KtParameter -> {
inferenceContext.declarationToTypeVariable[element]?.also { typeVariable ->
element.defaultValue?.isSubtypeOf(
typeVariable,
ConstraintPriority.INITIALIZER
)
}
}
element is KtReturnExpression -> {
val functionTypeVariable = element.getTargetFunction(element.analyze(resolutionFacade))
?.resolveToDescriptorIfAny(resolutionFacade)
?.let { functionDescriptor ->
inferenceContext.declarationDescriptorToTypeVariable[functionDescriptor]
} ?: return
element.returnedExpression?.isSubtypeOf(
functionTypeVariable,
ConstraintPriority.RETURN
)
}
element is KtReturnExpression -> {
val targetTypeVariable =
element.getTargetFunction(element.analyze(resolutionFacade))?.let { function ->
inferenceContext.declarationToTypeVariable[function]
}
if (targetTypeVariable != null) {
element.returnedExpression?.isSubtypeOf(
targetTypeVariable,
ConstraintPriority.RETURN
)
}
}
element is KtLambdaExpression -> {
val targetTypeVariable =
inferenceContext.declarationToTypeVariable[element.functionLiteral] ?: return
element.functionLiteral.bodyExpression?.statements?.lastOrNull()
?.takeIf { it !is KtReturnExpression }
?.also { implicitReturn ->
implicitReturn.isSubtypeOf(
targetTypeVariable,
ConstraintPriority.RETURN
)
}
}
element is KtForExpression -> {
val loopParameterTypeVariable =
element.loopParameter?.typeReference?.typeElement?.let { typeElement ->
inferenceContext.typeElementToTypeVariable[typeElement]
}
if (loopParameterTypeVariable != null) {
val loopRangeBoundType = element.loopRange?.boundType(inferenceContext) ?: return
val boundType =
element.loopRangeElementType(resolutionFacade)
?.boundType(
contextBoundType = loopRangeBoundType,
inferenceContext = inferenceContext
) ?: return
loopParameterTypeVariable.isSubtypeOf(
boundType.typeParameters.firstOrNull()?.boundType ?: return,
ConstraintPriority.ASSIGNMENT
)
}
}
}
Unit
}
private fun KtForExpression.loopRangeElementType(resolutionFacade: ResolutionFacade): KotlinType? {
val loopRangeType = loopRange?.getType(analyze(resolutionFacade)) ?: return null
return loopRangeType
.constructor
.declarationDescriptor
?.safeAs<ClassDescriptor>()
?.getMemberScope(loopRangeType.arguments)
?.getDescriptorsFiltered(DescriptorKindFilter.FUNCTIONS) {
it.asString() == "iterator"
}?.filterIsInstance<FunctionDescriptor>()
?.firstOrNull { it.valueParameters.isEmpty() }
?.original
?.returnType
}
}
@@ -0,0 +1,21 @@
/*
* 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.common.collectors
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.InferenceContext
import org.jetbrains.kotlin.psi.KtElement
abstract class ConstraintsCollector {
abstract fun ConstraintBuilder.collectConstraints(
element: KtElement,
boundTypeCalculator: BoundTypeCalculator,
inferenceContext: InferenceContext,
resolutionFacade: ResolutionFacade
)
}
@@ -0,0 +1,105 @@
/*
* 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.common.collectors
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.nj2k.inference.common.*
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtTypeElement
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class FunctionConstraintsCollector(
private val superFunctionsProvider: SuperFunctionsProvider
) : ConstraintsCollector() {
override fun ConstraintBuilder.collectConstraints(
element: KtElement,
boundTypeCalculator: BoundTypeCalculator,
inferenceContext: InferenceContext,
resolutionFacade: ResolutionFacade
) {
if (element !is KtFunction) return
superFunctionsProvider.inferenceContext = inferenceContext
val ktClass = element.containingClassOrObject ?: return
val classDescriptor = ktClass.resolveToDescriptorIfAny(resolutionFacade) ?: return
val superFunctions = superFunctionsProvider.provideSuperFunctionDescriptors(element) ?: return
val substitutor = inferenceContext.classSubstitutions[classDescriptor] ?: return
for (superFunction in superFunctions) {
val superClass = superFunction.containingDeclaration as? ClassDescriptor ?: continue
val superFunctionPsi = superFunction.original.findPsi() as? KtNamedFunction
run {
collectTypeConstraints(
element.typeReference?.typeElement ?: return@run,
superFunction.original.returnType ?: return@run,
superFunctionPsi?.typeReference?.typeElement,
substitutor,
superClass,
inferenceContext
)
}
for (parameterIndex in element.valueParameters.indices) {
collectTypeConstraints(
element.valueParameters.getOrNull(parameterIndex)?.typeReference?.typeElement ?: continue,
superFunction.valueParameters.getOrNull(parameterIndex)?.original?.type ?: continue,
superFunctionPsi?.valueParameters?.getOrNull(parameterIndex)?.typeReference?.typeElement,
substitutor,
superClass,
inferenceContext
)
}
}
}
private fun ConstraintBuilder.collectTypeConstraints(
typeElement: KtTypeElement,
superType: KotlinType,
superTypeElement: KtTypeElement?,
substitutor: ClassSubstitutor,
superClass: ClassDescriptor,
inferenceContext: InferenceContext
) {
val usedTypeVariables = hashSetOf<TypeVariable>()
val substitutions = calculateTypeSubstitutions(typeElement, superType) ?: return
for ((innerTypeElement, innerTypeParameter) in substitutions) {
val superEntryTypeElement = substitutor[superClass, innerTypeParameter] ?: continue
innerTypeElement.isTheSameTypeAs(superEntryTypeElement, ConstraintPriority.SUPER_DECLARATION)
inferenceContext.typeElementToTypeVariable[innerTypeElement]?.also { usedTypeVariables += it }
}
val superTypeVariable = superTypeElement?.let { inferenceContext.typeElementToTypeVariable[it] }
if (superTypeVariable != null) {
superTypeVariable.isTheSameTypeAs(typeElement, ConstraintPriority.SUPER_DECLARATION, usedTypeVariables)
} else {
superType.boundType(forceEnhance = true, inferenceContext = inferenceContext)
.isTheSameTypeAs(typeElement, ConstraintPriority.SUPER_DECLARATION, usedTypeVariables)
}
}
private fun calculateTypeSubstitutions(
typeElement: KtTypeElement,
superType: KotlinType
): List<Pair<KtTypeElement, TypeParameterDescriptor>>? {
val substitution = superType.constructor.declarationDescriptor
?.safeAs<TypeParameterDescriptor>()?.let { typeElement to it }
return typeElement.typeArgumentsAsTypes.zip(superType.arguments)
.flatMap { (argumentTypeElement, argumentType) ->
calculateTypeSubstitutions(argumentTypeElement?.typeElement ?: return null, argumentType.type) ?: return null
} + listOfNotNull(substitution)
}
}
@@ -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.inference.common
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.psi.KtTypeElement
import org.jetbrains.kotlin.types.KotlinType
sealed class TypeVariable {
abstract val classReference: ClassReference
abstract val typeParameters: List<TypeParameter>
abstract var state: State
}
sealed class TypeElementData {
abstract val typeElement: KtTypeElement
}
data class TypeElementDataImpl(override val typeElement: KtTypeElement) : TypeElementData()
data class TypeParameterElementData(
override val typeElement: KtTypeElement,
val typeParameterDescriptor: TypeParameterDescriptor
) : TypeElementData()
data class TypeElementBasedTypeVariable(
override val classReference: ClassReference,
override val typeParameters: List<TypeParameter>,
val typeElement: TypeElementData,
override var state: State
) : TypeVariable() {
//ignore state as it is mutable
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as TypeElementBasedTypeVariable
if (classReference != other.classReference) return false
if (typeParameters != other.typeParameters) return false
if (typeElement != other.typeElement) return false
return true
}
override fun hashCode(): Int {
var result = classReference.hashCode()
result = 31 * result + typeParameters.hashCode()
result = 31 * result + typeElement.hashCode()
return result
}
}
data class TypeBasedTypeVariable(
override val classReference: ClassReference,
override val typeParameters: List<TypeParameter>,
val type: KotlinType,
override var state: State
) : TypeVariable() {
//ignore state as it is mutable
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as TypeBasedTypeVariable
if (classReference != other.classReference) return false
if (typeParameters != other.typeParameters) return false
if (type != other.type) return false
return true
}
override fun hashCode(): Int {
var result = classReference.hashCode()
result = 31 * result + typeParameters.hashCode()
result = 31 * result + type.hashCode()
return result
}
}
val TypeVariable.isFixed: Boolean
get() = state != State.UNKNOWN
fun TypeVariable.setStateIfNotFixed(newState: State) {
if (!isFixed) {
state = newState
}
}
@@ -0,0 +1,30 @@
/*
* 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.common
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.nj2k.JKElementInfo
import org.jetbrains.kotlin.nj2k.JKElementInfoLabel
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.asLabel
import org.jetbrains.kotlin.psi.KtTypeProjection
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
fun PsiElement.getLabel(): JKElementInfoLabel? =
prevSibling
?.safeAs<PsiComment>()
?.text
?.asLabel()
?: parent
?.safeAs<KtTypeProjection>()
?.getLabel()
fun PsiElement.elementInfo(converterContext: NewJ2kConverterContext): List<JKElementInfo>? =
getLabel()?.let { label ->
converterContext.elementsInfoStorage.getInfoForLabel(label)
}
@@ -0,0 +1,117 @@
/*
* 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.nullability
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.nj2k.inference.common.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability
import org.jetbrains.kotlin.resolve.jvm.checkers.mustNotBeNull
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.util.javaslang.getOrNull
class NullabilityBoundTypeEnhancer(private val resolutionFacade: ResolutionFacade) : BoundTypeEnhancer() {
override fun enhance(
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
}
}
private fun enhanceCallExpression(
expression: KtCallExpression,
boundType: BoundType,
inferenceContext: InferenceContext
): BoundType {
if (expression.resolveToCall(resolutionFacade)?.candidateDescriptor is ConstructorDescriptor) {
return WithForcedStateBoundType(boundType, State.LOWER)
}
val resolved = expression.calleeExpression?.mainReference?.resolve() ?: return boundType
if (inferenceContext.isInConversionScope(resolved)) return boundType
return boundType.enhanceWith(expression.getExternallyAnnotatedForcedState())
}
override fun enhanceKotlinType(
type: KotlinType,
boundType: BoundType,
allowLowerEnhancement: Boolean,
inferenceContext: InferenceContext
): BoundType {
if (type.arguments.size != boundType.typeParameters.size) return boundType
val inner = BoundTypeImpl(
boundType.label,
boundType.typeParameters.zip(type.arguments) { typeParameter, typeArgument ->
TypeParameter(
enhanceKotlinType(
typeArgument.type,
typeParameter.boundType,
allowLowerEnhancement,
inferenceContext
),
typeParameter.variance
)
}
)
val enhancement = when {
type.isMarkedNullable -> State.UPPER
allowLowerEnhancement -> State.LOWER
else -> null
}
return inner.enhanceWith(enhancement)
}
private fun KtReferenceExpression.smartCastEnhancement() = analyzeExpressionUponTheTypeInfo { dataFlowValue, dataFlowInfo, _ ->
if (dataFlowInfo.completeNullabilityInfo.get(dataFlowValue)?.getOrNull() == Nullability.NOT_NULL) State.LOWER
else null
}
private inline fun KtExpression.analyzeExpressionUponTheTypeInfo(analyzer: (DataFlowValue, DataFlowInfo, KotlinType) -> State?): State? {
val bindingContext = analyze(resolutionFacade)
val type = getType(bindingContext) ?: return null
val dataFlowValue = resolutionFacade.frontendService<DataFlowValueFactory>()
.createDataFlowValue(this, type, bindingContext, resolutionFacade.moduleDescriptor)
val dataFlowInfo = bindingContext[BindingContext.EXPRESSION_TYPE_INFO, this]?.dataFlowInfo ?: return null
return analyzer(dataFlowValue, dataFlowInfo, type)
}
private fun KtExpression.getExternallyAnnotatedForcedState() = analyzeExpressionUponTheTypeInfo { dataFlowValue, dataFlowInfo, type ->
if (!type.isNullable()) return@analyzeExpressionUponTheTypeInfo State.LOWER
when {
dataFlowInfo.completeNullabilityInfo.get(dataFlowValue)?.getOrNull() == Nullability.NOT_NULL -> State.LOWER
type.isExternallyAnnotatedNotNull(dataFlowInfo, dataFlowValue) -> State.LOWER
else -> null
}
}
private fun KotlinType.isExternallyAnnotatedNotNull(dataFlowInfo: DataFlowInfo, dataFlowValue: DataFlowValue): Boolean =
mustNotBeNull()?.isFromJava == true && dataFlowInfo.getStableNullability(dataFlowValue).canBeNull()
}
@@ -0,0 +1,55 @@
/*
* 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.nullability
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.nj2k.inference.common.*
import org.jetbrains.kotlin.nj2k.inference.common.collectors.ConstraintsCollector
import org.jetbrains.kotlin.psi.*
class NullabilityConstraintsCollector : ConstraintsCollector() {
override fun ConstraintBuilder.collectConstraints(
element: KtElement,
boundTypeCalculator: BoundTypeCalculator,
inferenceContext: InferenceContext,
resolutionFacade: ResolutionFacade
) = with(boundTypeCalculator) {
when {
element is KtBinaryExpression &&
(element.left?.isNullExpression() == true
|| element.right?.isNullExpression() == true) -> {
val notNullOperand =
if (element.left?.isNullExpression() == true) element.right
else element.left
notNullOperand?.isTheSameTypeAs(State.UPPER, ConstraintPriority.COMPARE_WITH_NULL)
}
element is KtQualifiedExpression -> {
element.receiverExpression.isTheSameTypeAs(State.LOWER, ConstraintPriority.USE_AS_RECEIVER)
}
element is KtForExpression -> {
element.loopRange?.isTheSameTypeAs(State.LOWER, ConstraintPriority.USE_AS_RECEIVER)
}
element is KtWhileExpressionBase -> {
element.condition?.isTheSameTypeAs(State.LOWER, ConstraintPriority.USE_AS_RECEIVER)
}
element is KtIfExpression -> {
element.condition?.isTheSameTypeAs(State.LOWER, ConstraintPriority.USE_AS_RECEIVER)
}
element is KtValueArgument && element.isSpread -> {
element.getArgumentExpression()?.isTheSameTypeAs(State.LOWER, ConstraintPriority.USE_AS_RECEIVER)
}
element is KtBinaryExpression && !KtPsiUtil.isAssignment(element) -> {
element.left?.isTheSameTypeAs(State.LOWER, ConstraintPriority.USE_AS_RECEIVER)
element.right?.isTheSameTypeAs(State.LOWER, ConstraintPriority.USE_AS_RECEIVER)
}
}
Unit
}
}
@@ -0,0 +1,43 @@
/*
* 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.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.ClassReference
import org.jetbrains.kotlin.nj2k.inference.common.ContextCollector
import org.jetbrains.kotlin.nj2k.inference.common.State
import org.jetbrains.kotlin.nj2k.inference.common.getLabel
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
class NullabilityContextCollector(
resolutionFacade: ResolutionFacade,
private val converterContext: NewJ2kConverterContext
) : ContextCollector(resolutionFacade) {
override fun ClassReference.getState(typeElement: KtTypeElement?): State? {
val hasUndefinedNullabilityLabel = typeElement
?.parent
?.safeAs<KtTypeReference>()
?.hasUndefinedNullabilityLabel(converterContext)
?: false
return when {
typeElement == null -> State.UNKNOWN
hasUndefinedNullabilityLabel -> State.UNKNOWN
typeElement is KtNullableType -> State.UPPER
else -> State.LOWER
}
}
private fun KtTypeReference.hasUndefinedNullabilityLabel(context: NewJ2kConverterContext) =
getLabel()?.let { label ->
context.elementsInfoStorage.getInfoForLabel(label).orEmpty().contains(UnknownNullability)
} ?: false
}
@@ -0,0 +1,56 @@
/*
* 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.nullability
import org.jetbrains.kotlin.nj2k.inference.common.*
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 updateStates(inferenceContext: InferenceContext) {
if (inferenceContext.typeVariables.isEmpty()) return
val deepComparator = Comparator<TypeElementBasedTypeVariable> { o1, o2 ->
if (o1.typeElement.typeElement.isAncestor(o2.typeElement.typeElement)) 1 else -1
}
for (typeVariable in inferenceContext
.typeVariables
.filterIsInstance<TypeElementBasedTypeVariable>()
.sortedWith(deepComparator)
) {
when (typeVariable.state) {
State.LOWER -> typeVariable.changeState(toNullable = false)
State.UPPER -> typeVariable.changeState(toNullable = true)
State.UNKNOWN -> {
if (typeVariable.typeElement is TypeParameterElementData) {
typeVariable.changeState(toNullable = false)
} else {
typeVariable.changeState(toNullable = true)
}
}
}
}
}
private fun TypeElementBasedTypeVariable.changeState(toNullable: Boolean) {
changeState(typeElement.typeElement, toNullable)
}
companion object {
fun changeState(typeElement: KtTypeElement, toNullable: Boolean) {
val factory = KtPsiFactory(typeElement)
if (typeElement is KtNullableType && !toNullable) {
typeElement.replace(factory.createType(typeElement.innerType?.text ?: return).typeElement ?: return)
}
if (typeElement !is KtNullableType && toNullable) {
typeElement.replace(factory.createType("${typeElement.text}?").typeElement ?: return)
}
}
}
}
@@ -1,203 +0,0 @@
/*
* 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.nullabilityAnalysis
import com.intellij.psi.PsiComment
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.nj2k.postProcessing.resolve
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespace
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.types.typeUtil.builtIns
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal class BoundTypeStorage(private val analysisAnalysisContext: AnalysisContext, private val printConstraints: Boolean) {
private val cache = mutableMapOf<KtExpression, BoundType>()
private val printer = Printer(analysisAnalysisContext)
fun boundTypeFor(expression: KtExpression): BoundType =
cache.getOrPut(expression) {
val boundType =
when (expression) {
is KtParenthesizedExpression -> expression.expression?.let { boundTypeFor(it) }
is KtQualifiedExpression ->
expression.selectorExpression?.toBoundType(
boundTypeFor(expression.receiverExpression)
)
is KtLabeledExpression -> expression.baseExpression?.let { boundTypeFor(it) }
is KtLambdaExpression -> lambdaBoundType(expression)
else -> expression.getQualifiedExpressionForSelector()?.let { boundTypeFor(it) }
} ?: expression.toBoundType(null)
?: LiteralBoundType(expression.isNullable())
if (printConstraints) {
if (expression.getNextSiblingIgnoringWhitespace() !is PsiComment) {
val comment = with(printer) {
KtPsiFactory(expression.project).createComment("/*${boundType.asString()}*/")
}
expression.parent.addAfter(comment, expression)
}
}
boundType.withForcedNullability(expression.getForcedNullability())
}
private fun lambdaBoundType(lambda: KtLambdaExpression): BoundType? {
val builtIns = lambda.getType(lambda.analyze())?.builtIns ?: return null
val descriptor = builtIns.getFunction(lambda.valueParameters.size)
val parameterBoundTypes = lambda.valueParameters.map { parameter ->
parameter.typeReference?.typeElement?.let { typeElement ->
analysisAnalysisContext.typeElementToTypeVariable[typeElement]
}?.let {
BoundTypeTypeParameter(TypeVariableBoundType(it), Variance.IN_VARIANCE)
} ?: return null
}
val returnBoundType =
BoundTypeTypeParameter(
TypeVariableBoundType(
analysisAnalysisContext.declarationToTypeVariable[lambda.functionLiteral] ?: return null
),
Variance.OUT_VARIANCE
)
return GenericBoundType(
DescriptorClassReference(descriptor),
parameterBoundTypes + returnBoundType,
forcedNullabilityTo = null,
isNull = false
)
}
fun boundTypeForType(
type: KotlinType,
contextBoundType: BoundType?,
typeParameterDescriptors: Map<TypeParameterDescriptor, TypeVariable>
): BoundType? =
type.toBoundType(contextBoundType, typeParameterDescriptors)
private fun KtExpression.resolveToTypeVariable(): TypeVariable? =
getCalleeExpressionIfAny()
?.safeAs<KtReferenceExpression>()
?.resolve()
?.safeAs<KtDeclaration>()
?.let {
analysisAnalysisContext.declarationToTypeVariable[it]
} ?: KtPsiUtil.deparenthesize(this)?.let { analysisAnalysisContext.declarationToTypeVariable[it] }
private fun KtExpression.toBoundTypeAsTypeVariable(): BoundType? {
val typeVariableBoundType = resolveToTypeVariable()?.let {
TypeVariableBoundType(it, null)
} ?: return null
val asReferenceToVarArgParameter =
safeAs<KtNameReferenceExpression>()
?.mainReference
?.resolve()
?.safeAs<KtParameter>()
?.takeIf { it.isVarArg }
return if (asReferenceToVarArgParameter != null) {
GenericBoundType(
UnknownClassReference("Array"),//TODO
listOf(BoundTypeTypeParameter(typeVariableBoundType, Variance.INVARIANT)),
isNull = false
)
} else typeVariableBoundType
}
private fun KtExpression.toBoundTypeAsCallExpression(contextBoundType: BoundType?): BoundType? {
val typeElement = getCalleeExpressionIfAny()
?.safeAs<KtReferenceExpression>()
?.resolve()
?.safeAs<KtCallableDeclaration>()
?.typeReference
?.typeElement
typeElement?.let { analysisAnalysisContext.typeElementToTypeVariable[it] }?.also {
return TypeVariableBoundType(it)
}
val bindingContext = analyze()
val descriptor =
getResolvedCall(bindingContext)?.candidateDescriptor?.original?.safeAs<CallableDescriptor>() ?: return null
val typeParameters =
run {
if (this is KtCallElement) {
typeArguments.mapIndexed { index, typeArgument ->
//TODO better check
val typeParameter = descriptor.typeParameters.getOrNull(index) ?: return@run null
val typeVariable =
analysisAnalysisContext.typeElementToTypeVariable[typeArgument.typeReference?.typeElement ?: return@run null]
?: return@run null
typeParameter to typeVariable
}.toMap()
} else emptyMap()
} ?: emptyMap()
return descriptor.returnType?.toBoundType(contextBoundType, typeParameters)
}
private fun KtExpression.toBoundTypeAsCastExpression(): BoundType? {
val castExpression = KtPsiUtil.deparenthesize(this)
?.safeAs<KtBinaryExpressionWithTypeRHS>()
?.takeIf { KtPsiUtil.isUnsafeCast(it) }
?: return null
return castExpression.right?.typeElement
?.let { analysisAnalysisContext.typeElementToTypeVariable[it] }
?.let { TypeVariableBoundType(it) }
}
private fun KtExpression.toBoundType(contextBoundType: BoundType?): BoundType? =
toBoundTypeAsTypeVariable()
?: toBoundTypeAsCallExpression(contextBoundType)
?: toBoundTypeAsCastExpression()
private fun KotlinType.toBoundType(
contextBoundType: BoundType?,
typeParameterDescriptors: Map<TypeParameterDescriptor, TypeVariable>
): BoundType? {
fun KotlinType.toBoundType(): BoundType? {
val forcedNullability = when {
isMarkedNullable -> Nullability.NULLABLE
else -> null
}
val target = constructor.declarationDescriptor
return when (target) {
is ClassDescriptor -> {
val classReference = DescriptorClassReference(target)
GenericBoundType(
classReference,
(arguments zip constructor.parameters).map { (typeArgument, typeParameter) ->
BoundTypeTypeParameter(
typeArgument.type.toBoundType() ?: return null,
typeParameter.variance
)
},
forcedNullability,
isNullable()
)
}
is TypeParameterDescriptor -> {
when {
target in typeParameterDescriptors ->
TypeVariableBoundType(typeParameterDescriptors.getValue(target), forcedNullability)
contextBoundType != null ->
contextBoundType.typeParameters.getOrNull(target.index)?.boundType?.withForcedNullability(forcedNullability)
else -> null
}
}
else -> error(toString())
}
}
return toBoundType()
}
}
@@ -1,63 +0,0 @@
/*
* 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.nullabilityAnalysis
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal class ConstraintBuilder(val boundTypeStorage: BoundTypeStorage) {
private val constraints = mutableListOf<Constraint>()
internal fun getConstraints(): List<Constraint> = constraints
fun KtExpression.addSubtypeNullabilityConstraint(typeVariable: TypeVariable, cameFrom: ConstraintCameFrom) {
addSubtypeNullabilityConstraint(TypeVariableBoundType(typeVariable), cameFrom)
}
fun KtExpression.addEqualsNullabilityConstraint(nullability: Nullability, cameFrom: ConstraintCameFrom) {
val boundType = boundTypeStorage.boundTypeFor(this).safeAs<TypeVariableBoundType>()
?: return
constraints += EqualConstraint(boundType.bound, LiteralBound(nullability), cameFrom)
}
fun TypeVariable.addEqualsNullabilityConstraint(other: BoundType, cameFrom: ConstraintCameFrom) {
TypeVariableBoundType(this).isTheSameType(other, cameFrom)
}
fun KtExpression.addSubtypeNullabilityConstraint(upperTypeExpression: KtExpression, cameFrom: ConstraintCameFrom) {
boundTypeStorage.boundTypeFor(this).subtypeOf(boundTypeStorage.boundTypeFor(upperTypeExpression), cameFrom)
}
fun KtExpression.addSubtypeNullabilityConstraint(upperBoundType: BoundType, cameFrom: ConstraintCameFrom) {
boundTypeStorage.boundTypeFor(this)
.subtypeOf(upperBoundType, cameFrom)
}
fun TypeVariable.subtypeOf(other: BoundType, cameFrom: ConstraintCameFrom) {
TypeVariableBoundType(this).subtypeOf(other, cameFrom)
}
fun BoundType.subtypeOf(other: BoundType, cameFrom: ConstraintCameFrom) {
(typeParameters zip other.typeParameters).forEach { (left, right) ->
val variance = left.variance
when (variance) {
Variance.OUT_VARIANCE -> left.boundType.subtypeOf(right.boundType, cameFrom)
Variance.IN_VARIANCE -> right.boundType.subtypeOf(left.boundType, cameFrom)
Variance.INVARIANT -> right.boundType.isTheSameType(left.boundType, cameFrom)
}
}
constraints += SubtypeConstraint(bound, other.bound, cameFrom)
}
fun BoundType.isTheSameType(other: BoundType, cameFrom: ConstraintCameFrom) {
(typeParameters zip other.typeParameters).forEach { (left, right) ->
left.boundType.isTheSameType(right.boundType, cameFrom)
}
constraints += EqualConstraint(bound, other.bound, cameFrom)
}
}
@@ -1,253 +0,0 @@
/*
* 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.nullabilityAnalysis
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiTypeParameter
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal interface TypeVariableOwner {
val target: KtElement
}
internal interface SingleTypeVariableOwner : TypeVariableOwner {
val typeVariable: TypeVariable
}
internal interface MultipleTypeVariablesOwner : TypeVariableOwner {
val typeVariables: List<TypeVariable>
}
internal val TypeVariableOwner.allTypeVariables
get() = when (this) {
is SingleTypeVariableOwner -> listOf(typeVariable)
is MultipleTypeVariablesOwner -> typeVariables
else -> error("TypeVariableOwner should be either SingleTypeVariableOwner or MultipleTypeVariablesOwner")
}
internal interface DeclarationTypeVariableOwner : SingleTypeVariableOwner {
override val target: KtCallableDeclaration
}
internal data class PropertyTarget(
override val target: KtProperty,
override val typeVariable: TypeVariable
) : DeclarationTypeVariableOwner
internal data class FunctionTarget(
override val target: KtNamedFunction,
override val typeVariable: TypeVariable
) : DeclarationTypeVariableOwner
internal data class LambdaTarget(
override val target: KtFunctionLiteral,
override val typeVariable: TypeVariable
) : DeclarationTypeVariableOwner
internal data class ParameterTarget(
override val target: KtParameter,
override val typeVariable: TypeVariable
) : DeclarationTypeVariableOwner
internal data class TypeCastTarget(
override val target: KtBinaryExpressionWithTypeRHS,
override val typeVariable: TypeVariable
) : SingleTypeVariableOwner
internal data class FunctionCallTypeArgumentTarget(
override val target: KtCallExpression,
override val typeVariables: List<TypeVariable>
) : MultipleTypeVariablesOwner
internal interface ClassReference
internal data class KtClassReference(val klass: KtClassOrObject) : ClassReference
internal data class DescriptorClassReference(val descriptor: ClassDescriptor) : ClassReference
internal data class JavaClassReference(val klass: PsiClass) : ClassReference
internal data class TypeParameterClassReference(val typeParameter: KtTypeParameter) : ClassReference
internal data class UnknownClassReference(val text: String) : ClassReference
internal object LiteralClassReference : ClassReference
internal interface BoundType {
val classReference: ClassReference
val typeParameters: List<BoundTypeTypeParameter>
val forcedNullabilityTo: Nullability?
}
internal class TypeVariableBoundType(
val typeVariable: TypeVariable,
override val forcedNullabilityTo: Nullability? = null
) : BoundType {
override val classReference: ClassReference = typeVariable.classReference
override val typeParameters: List<BoundTypeTypeParameter> =
typeVariable.typeParameters.map { typeParameter ->
val boundType =
if (typeParameter is TypeVariableTypeParameterWithTypeParameter)
TypeVariableBoundType(typeParameter.typeVariable)
else StarProjectionBoundType
BoundTypeTypeParameter(
boundType,
typeParameter.variance //TODO isMarkedNullable
)
}
}
internal class GenericBoundType(
override val classReference: ClassReference,
override val typeParameters: List<BoundTypeTypeParameter>,
override val forcedNullabilityTo: Nullability? = null,
val isNull: Boolean
) : BoundType
internal class LiteralBoundType(val isNull: Boolean) : BoundType {
override val classReference = LiteralClassReference
override val typeParameters: List<BoundTypeTypeParameter> = emptyList()
override val forcedNullabilityTo: Nullability? = null
}
internal class TypeVariable(
val typeElement: KtTypeElement?,
val classReference: ClassReference,
val typeParameters: List<TypeVariableTypeParameter>,
var nullability: Nullability
)
internal inline val BoundType.bound
get() =
when {
forcedNullabilityTo != null -> LiteralBound(forcedNullabilityTo!!)
this is TypeVariableBoundType -> TypeVariableBound(typeVariable)
this is LiteralBoundType -> LiteralBound(isNull.nullableForTrue())
this is GenericBoundType -> LiteralBound(isNull.nullableForTrue())
this is StarProjectionBoundType -> LiteralBound(Nullability.NULLABLE)
else -> error("Bad bound type $this")
}
internal inline fun <reified T : BoundType> T.withForcedNullability(nullability: Nullability?): T =
if (forcedNullabilityTo == nullability || nullability == null) this
else when (this) {
is GenericBoundType ->
GenericBoundType(
classReference,
typeParameters,
nullability,
isNull
)
is TypeVariableBoundType ->
TypeVariableBoundType(
typeVariable,
nullability
)
is LiteralBoundType -> this
else -> this
} as T
internal interface TypeVariableTypeParameter {
val variance: Variance
}
internal data class TypeVariableTypeParameterWithTypeParameter(
val typeVariable: TypeVariable,
override val variance: Variance
) : TypeVariableTypeParameter
internal object TypeVariableStartProjectionTypeParameter : TypeVariableTypeParameter {
override val variance = Variance.INVARIANT //TODO ??
}
internal data class BoundTypeTypeParameter(
val boundType: BoundType,
val variance: Variance
)
internal object StarProjectionBoundType : BoundType {
override val classReference = UnknownClassReference("*")//TODO
override val typeParameters: List<BoundTypeTypeParameter> = emptyList()
override val forcedNullabilityTo = null
}
internal interface Constraint {
val cameFrom: ConstraintCameFrom
}
interface ConstraintBound
internal data class TypeVariableBound(val typeVariable: TypeVariable) : ConstraintBound
internal data class LiteralBound(val nullability: Nullability) : ConstraintBound
internal data class SubtypeConstraint(
var lowerBound: ConstraintBound,
var upperBound: ConstraintBound,
override val cameFrom: ConstraintCameFrom
) : Constraint
enum class ConstraintCameFrom {
SUPER_DECLARATION,
INITIALIZER,
COMPARED_WITH_NULL,
ASSIGNMENT_TARGET,
USED_AS_RECEIVER,
PARAMETER_PASSED
}
internal data class EqualConstraint(
val leftBound: ConstraintBound,
val rightBound: ConstraintBound,
override val cameFrom: ConstraintCameFrom
) : Constraint
private fun PsiTypeParameter.variance() = Variance.INVARIANT//TODO real variance
internal fun ClassReference.typeParametersVariance(parameterIndex: Int): Variance =
when (this) {
is KtClassReference -> klass.typeParameters.getOrNull(parameterIndex)?.variance ?: Variance.INVARIANT
is JavaClassReference -> klass.typeParameters.getOrNull(parameterIndex)?.variance() ?: Variance.INVARIANT
is DescriptorClassReference -> descriptor.typeConstructor.parameters[parameterIndex].variance
is TypeParameterClassReference -> Variance.INVARIANT
is UnknownClassReference -> Variance.INVARIANT
else -> error("Bad class reference `$this`")
}
internal fun TypeVariable.setNullabilityIfNotFixed(nullability: Nullability) {
if (!isFixed) {
this.nullability = nullability
}
}
internal val TypeVariable.isFixed: Boolean
get() = nullability != Nullability.UNKNOWN
enum class Nullability {
NULLABLE, NOT_NULL, UNKNOWN
}
internal fun Boolean.nullableForTrue() =
if (this) Nullability.NULLABLE else Nullability.NOT_NULL
internal fun TypeVariableOwner.innerTypeVariables(): List<TypeVariable> =
when (this) {
is SingleTypeVariableOwner -> typeVariable.innerTypeVariables()
is MultipleTypeVariablesOwner -> typeVariables.flatMap { it.innerTypeVariables() }
else -> error("")
}
internal fun TypeVariable.innerTypeVariables(): List<TypeVariable> =
typeParameters.flatMap { typeParameter ->
typeParameter.safeAs<TypeVariableTypeParameterWithTypeParameter>()?.let { typeVariableTypeParameter ->
typeVariableTypeParameter.typeVariable.innerTypeVariables() + listOfNotNull(typeVariableTypeParameter.typeVariable)
}.orEmpty()
}
@@ -1,310 +0,0 @@
/*
* 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.nullabilityAnalysis
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.nj2k.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.asAssignment
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunction
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.resolve.calls.components.isVararg
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal class LabelToFunctionTypeVariableMapper(analysisContext: AnalysisContext) {
private val labelToFunction = analysisContext.declarationToTypeVariable.mapNotNull { (declaration, _) ->
if (declaration !is KtNamedFunction) return@mapNotNull null
val label = declaration.nameIdentifier?.getLabel() ?: return@mapNotNull null
label to declaration
}.toMap()
fun functionByLabel(label: JKElementInfoLabel): KtNamedFunction? =
labelToFunction[label]
}
internal class ConstraintsCollector(
private val analysisContext: AnalysisContext,
private val converterContext: NewJ2kConverterContext?,
printConstraints: Boolean
) {
private val boundTypeStorage = BoundTypeStorage(analysisContext, printConstraints)
private val constraintBuilder = ConstraintBuilder(boundTypeStorage)
private val labelToFunctionTypeVariableMapper = LabelToFunctionTypeVariableMapper(analysisContext)
private val PsiElement.elementInfo: List<JKElementInfo>?
get() = getLabel()?.let { label ->
converterContext?.elementsInfoStorage?.getInfoForLabel(label)
}
private inline val KtExpression.boundType
get() = boundTypeStorage.boundTypeFor(this)
internal fun collectConstraints(analysisScope: AnalysisScope): List<Constraint> {
analysisScope.forEach { element ->
element.forEachDescendantOfType<KtExpression> { expression ->
collectConstraintsForExpression(expression)
}
}
return constraintBuilder.getConstraints()
}
private fun collectConstraintsForExpression(expression: KtExpression) = with(constraintBuilder) {
when {
expression is KtQualifiedExpression -> {
expression.receiverExpression.addEqualsNullabilityConstraint(Nullability.NOT_NULL, ConstraintCameFrom.USED_AS_RECEIVER)
}
expression is KtBinaryExpressionWithTypeRHS && KtPsiUtil.isUnsafeCast(expression) -> {
expression.right?.typeElement?.let { analysisContext.typeElementToTypeVariable[it] }?.also { typeVariable ->
expression.left.addSubtypeNullabilityConstraint(typeVariable, ConstraintCameFrom.ASSIGNMENT_TARGET)
}
}
expression is KtBinaryExpression && expression.asAssignment() != null -> {
expression.right?.addSubtypeNullabilityConstraint(expression.left ?: return, ConstraintCameFrom.ASSIGNMENT_TARGET)
}
expression is KtBinaryExpression && expression.isComaprationWithNull() -> {
val comparedExpression =
if (expression.left.isNullExpression()) expression.right
else expression.left
comparedExpression?.addEqualsNullabilityConstraint(Nullability.NULLABLE, ConstraintCameFrom.COMPARED_WITH_NULL)
}
expression is KtBinaryExpression -> {
expression.left?.addEqualsNullabilityConstraint(Nullability.NOT_NULL, ConstraintCameFrom.USED_AS_RECEIVER)
expression.right?.addEqualsNullabilityConstraint(Nullability.NOT_NULL, ConstraintCameFrom.USED_AS_RECEIVER)
}
expression is KtProperty -> {
analysisContext.declarationToTypeVariable[expression]?.also { typeVariable ->
expression.initializer?.addSubtypeNullabilityConstraint(
typeVariable,
ConstraintCameFrom.INITIALIZER
)
}
}
expression is KtParameter -> {
analysisContext.declarationToTypeVariable[expression]?.also { typeVariable ->
expression.defaultValue?.addSubtypeNullabilityConstraint(
typeVariable,
ConstraintCameFrom.INITIALIZER
)
}
}
expression is KtIfExpression -> {
expression.condition?.addEqualsNullabilityConstraint(Nullability.NOT_NULL, ConstraintCameFrom.INITIALIZER)
}
expression is KtWhileExpression -> {
expression.condition?.addEqualsNullabilityConstraint(Nullability.NOT_NULL, ConstraintCameFrom.INITIALIZER)
}
expression is KtCallExpression -> {
collectConstraintsForCallExpression(
expression,
expression.parent?.safeAs<KtQualifiedExpression>()?.receiverExpression
)
}
expression is KtReturnExpression -> {
val targetTypeVariable = expression.getTargetFunction(expression.analyze())?.let { function ->
analysisContext.declarationToTypeVariable[function]
}
if (targetTypeVariable != null) {
expression.returnedExpression?.addSubtypeNullabilityConstraint(
targetTypeVariable,
ConstraintCameFrom.ASSIGNMENT_TARGET
)
}
}
expression is KtNamedFunction -> {
collectSuperDeclarationConstraintsForFunction(expression)
}
expression is KtForExpression -> {
expression.loopRange?.addEqualsNullabilityConstraint(Nullability.NOT_NULL, ConstraintCameFrom.INITIALIZER)
val loopParameterTypeVariable =
expression.loopParameter?.typeReference?.typeElement?.let { typeElement ->
analysisContext.typeElementToTypeVariable[typeElement]
}
if (loopParameterTypeVariable != null) {
val loopRangeBoundType = boundTypeStorage.boundTypeFor(expression.loopRange ?: return)
val loopRangeType = expression.loopRange?.getType(expression.analyze()) ?: return
val loopRangeItemType = loopRangeType
.constructor
.declarationDescriptor
?.safeAs<ClassDescriptor>()
?.getMemberScope(loopRangeType.arguments)
?.getDescriptorsFiltered(DescriptorKindFilter.FUNCTIONS) {
it.asString() == "iterator"
}?.filterIsInstance<FunctionDescriptor>()
?.firstOrNull { it.valueParameters.isEmpty() }
?.original
?.returnType
?: return
val boundType = boundTypeStorage.boundTypeForType(
loopRangeItemType,
loopRangeBoundType,
emptyMap()
) ?: return
loopParameterTypeVariable.addEqualsNullabilityConstraint(
boundType.typeParameters.firstOrNull()?.boundType ?: return,
ConstraintCameFrom.ASSIGNMENT_TARGET
)
}
}
}
Unit
}
private fun SuperFunctionInfo.superFunctionBoundType(
originalFunctionDescriptor: FunctionDescriptor
): BoundType? =
when (this) {
is ExternalSuperFunctionInfo -> {
val containingSuperClassBoundType =
descriptor.containingDeclaration.safeAs<ClassDescriptor>()?.let { klass ->
GenericBoundType(
DescriptorClassReference(klass),
emptyList(),//TODO,
isNull = false
)
}
originalFunctionDescriptor.returnType?.let { type ->
boundTypeStorage.boundTypeForType(type, containingSuperClassBoundType, emptyMap()/*TODO*/)
}
}
is InternalSuperFunctionInfo -> {
labelToFunctionTypeVariableMapper.functionByLabel(label)?.let { function ->
analysisContext.declarationToTypeVariable[function]?.let { TypeVariableBoundType(it) }
}
}
}
private fun SuperFunctionInfo.superFunctionParameterBoundType(parameterIndex: Int): BoundType? {
return when (this) {
is ExternalSuperFunctionInfo -> {
val containingSuperClassBoundType =
descriptor.containingDeclaration.safeAs<ClassDescriptor>()?.let { klass ->
GenericBoundType(
DescriptorClassReference(klass),
emptyList(),//TODO,
isNull = false
)
}
val parameter = descriptor.valueParameters.getOrNull(parameterIndex) ?: return null
boundTypeStorage.boundTypeForType(parameter.type, containingSuperClassBoundType, emptyMap()/*TODO*/)
}
is InternalSuperFunctionInfo -> {
val function = labelToFunctionTypeVariableMapper.functionByLabel(label) ?: return null
val parameter = function.valueParameters.getOrNull(parameterIndex) ?: return null
analysisContext.declarationToTypeVariable[parameter]?.let { TypeVariableBoundType(it) }
}
}
}
private fun collectSuperDeclarationConstraintsForFunction(function: KtNamedFunction) = with(constraintBuilder) {
val (descriptor, superDeclarationDescriptors) =
function.nameIdentifier?.elementInfo?.firstIsInstanceOrNull<FunctionInfo>() ?: return@with
for (superDeclarationInfo in superDeclarationDescriptors) {
val superFunctionBoundType = superDeclarationInfo.superFunctionBoundType(descriptor) ?: continue
analysisContext.declarationToTypeVariable[function]?.addEqualsNullabilityConstraint(
superFunctionBoundType,
ConstraintCameFrom.SUPER_DECLARATION
)
for (parameterIndex in function.valueParameters.indices) {
val parameterTypeVariable =
function.valueParameters[parameterIndex].let { analysisContext.declarationToTypeVariable[it] } ?: return
val superParameterBoundType = superDeclarationInfo.superFunctionParameterBoundType(parameterIndex) ?: return
parameterTypeVariable.addEqualsNullabilityConstraint(superParameterBoundType, ConstraintCameFrom.SUPER_DECLARATION)
}
}
}
private fun collectConstraintsForCallExpression(callExpression: KtCallExpression, receiver: KtExpression?) = with(constraintBuilder) {
val receiverBoundType = receiver?.boundType
val descriptor = callExpression.resolveToCall()?.candidateDescriptor?.original?.safeAs<CallableDescriptor>() ?: return
val function = descriptor.findPsi()?.safeAs<KtNamedFunction>()
fun parameterBoundTypeIndex(index: Int): BoundType? =
function?.valueParameters?.let { parameters ->
val parameter =
if (index <= parameters.lastIndex) parameters[index]
else parameters.lastOrNull()?.takeIf { parameter ->
parameter.isVarArg
}
val typeVariable = parameter?.let { analysisContext.declarationToTypeVariable[it] }
typeVariable?.let {
TypeVariableBoundType(it)
}
} ?: run {
if (index < descriptor.valueParameters.lastIndex
|| index == descriptor.valueParameters.lastIndex && descriptor.valueParameters.lastOrNull()?.isVararg == false
) descriptor.valueParameters[index].type
else {
descriptor.valueParameters.lastOrNull()?.takeIf { parameter ->
parameter.isVararg && KotlinBuiltIns.isArray(parameter.type)
}?.let { parameter ->
parameter.type.arguments.singleOrNull()?.type
}
}
}?.let { type ->
boundTypeStorage
.boundTypeForType(type, receiverBoundType, callExpression.typeArgumentsDescriptors(descriptor).orEmpty())
}
for (argumentIndex in callExpression.valueArguments.indices) {
val valueArgument = callExpression.valueArguments[argumentIndex]
val argument = valueArgument.getArgumentExpression() ?: continue
if (valueArgument.isSpread) {
argument.addEqualsNullabilityConstraint(Nullability.NOT_NULL, ConstraintCameFrom.USED_AS_RECEIVER)
}
argument.addSubtypeNullabilityConstraint(
parameterBoundTypeIndex(argumentIndex) ?: continue,
ConstraintCameFrom.PARAMETER_PASSED
)
}
}
private fun KtCallExpression.typeArgumentsDescriptors(descriptor: CallableDescriptor): Map<TypeParameterDescriptor, TypeVariable>? {
return descriptor.typeParameters.zip(typeArguments) { typeParameter, typeArgument ->
val typeVariable =
analysisContext.typeElementToTypeVariable[typeArgument.typeReference?.typeElement ?: return null]
?: return null
typeParameter to typeVariable
}.toMap()
}
}
@@ -1,159 +0,0 @@
/*
* 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.nullabilityAnalysis
import com.intellij.openapi.editor.RangeMarker
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.nj2k.JKElementInfoLabel
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.asLabel
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal class ContextCreator(private val getNullability: (KtTypeElement) -> Nullability) {
private fun KtCallableDeclaration.typeElement(): KtTypeElement? =
typeReference?.typeElement
private fun KtElement.asTypeVariableOwner(): TypeVariableOwner? {
return when (this) {
is KtParameter -> typeElement()?.asTypeVariable()?.let { ParameterTarget(this, it) }
is KtProperty -> typeElement()?.asTypeVariable()?.let { PropertyTarget(this, it) }
is KtNamedFunction -> typeElement()?.asTypeVariable()?.let { FunctionTarget(this, it) }
is KtLambdaExpression -> {
val context = analyze()
val returnType = getType(context)?.arguments?.lastOrNull()?.type ?: return null
val typeVariable = returnType.asTypeVariable() ?: return null
LambdaTarget(functionLiteral, typeVariable)
}
is KtBinaryExpressionWithTypeRHS -> right?.typeElement
?.takeIf { KtPsiUtil.isUnsafeCast(this) }
?.asTypeVariable()
?.let { TypeCastTarget(this, it) }
is KtCallExpression ->
if (typeArguments.isNotEmpty())
FunctionCallTypeArgumentTarget(
this,
typeArguments.map { it.typeReference?.typeElement?.asTypeVariable() ?: return null }
)
else null
else -> null
}
}
fun createContext(analysisScope: AnalysisScope): AnalysisContext {
val typeVariableOwners = analysisScope.flatMap {
it.collectDescendantsOfType<KtElement>().mapNotNull { ktElement ->
ktElement.asTypeVariableOwner()
}
}
val typeElementsToTypeVariables =
typeVariableOwners.flatMap {
it.innerTypeVariables() + it.allTypeVariables
}.filter { it.typeElement != null }
.associateBy { it.typeElement!! }
val declarationToTypeVariable = typeVariableOwners.asSequence()
.mapNotNull { owner ->
if (owner is DeclarationTypeVariableOwner)
owner.target to owner.typeVariable
else null
}.toMap()
return AnalysisContext(typeVariableOwners, typeElementsToTypeVariables, declarationToTypeVariable)
}
private fun KtTypeElement.asTypeVariable(): TypeVariable {
val nullability = getNullability(this)
val classReference: ClassReference = classReference()
val typeParameters: List<TypeVariableTypeParameter> =
typeArgumentsAsTypes.mapIndexed { index, typeRef ->
typeRef?.typeElement?.asTypeVariable()?.let { typeVariable ->
TypeVariableTypeParameterWithTypeParameter(
typeVariable,
classReference.typeParametersVariance(index)
)
} ?: TypeVariableStartProjectionTypeParameter
}
return TypeVariable(this, classReference, typeParameters, nullability)
}
private fun KotlinType.asTypeVariable(): TypeVariable? {
val classReference = constructor
.declarationDescriptor
?.safeAs<ClassDescriptor>()
?.let { DescriptorClassReference(it) }
?: return null
val typeParameters =
arguments.zip(constructor.parameters) { argument, parameter ->
TypeVariableTypeParameterWithTypeParameter(
argument.type.asTypeVariable() ?: return null,
parameter.variance
)
}
return TypeVariable(null, classReference, typeParameters, Nullability.UNKNOWN)
}
}
fun nullabilityByUndefinedNullabilityComment(typeElement: KtTypeElement, context: NewJ2kConverterContext): Nullability {
val hasUndefinedNullabilityLabel =
typeElement.parent?.safeAs<KtTypeReference>()?.hasUndefinedNullabilityLabel(context) ?: false
return when {
hasUndefinedNullabilityLabel -> Nullability.UNKNOWN
typeElement is KtNullableType -> Nullability.NULLABLE
else -> Nullability.NOT_NULL
}
}
fun PsiElement.getLabel(): JKElementInfoLabel? =
prevSibling?.safeAs<PsiComment>()?.text?.asLabel()
?: parent?.safeAs<KtTypeProjection>()?.getLabel()
private fun KtTypeReference.hasUndefinedNullabilityLabel(context: NewJ2kConverterContext) =
getLabel()?.let { label ->
context.elementsInfoStorage.getInfoForLabel(label).orEmpty().contains(org.jetbrains.kotlin.nj2k.UnknownNullability)
} ?: false
fun prepareTypeElementByMakingAllTypesNullableConsideringNullabilityComment(typeElement: KtTypeElement, context: NewJ2kConverterContext) {
val hasUndefinedNullabilityLabel =
typeElement.parent?.safeAs<KtTypeReference>()?.hasUndefinedNullabilityLabel(context) ?: false
if (hasUndefinedNullabilityLabel) {
typeElement.changeNullability(toNullable = true)
}
}
fun prepareTypeElementByMakingAllTypesNullable(typeElement: KtTypeElement) {
typeElement.changeNullability(toNullable = true)
}
internal data class AnalysisContext(
val typeVariableOwners: List<TypeVariableOwner>,
val typeElementToTypeVariable: Map<KtTypeElement, TypeVariable>,
val declarationToTypeVariable: Map<KtCallableDeclaration, TypeVariable>
)
data class AnalysisScope(val elements: List<PsiElement>) : Iterable<PsiElement> by elements {
constructor(vararg elements: KtElement) : this(elements.toList())
constructor(file: KtFile, rangeMarker: RangeMarker?) :
this(rangeMarker?.let { marker ->
file.elementsInRange(TextRange(marker.startOffset, marker.endOffset))
} ?: listOf(file))
}
@@ -1,133 +0,0 @@
/*
* 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.nullabilityAnalysis
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.command.CommandProcessor
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.asLabel
import org.jetbrains.kotlin.nj2k.parentOfType
import org.jetbrains.kotlin.nj2k.postProcessing.resolve
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal fun TypeVariable.changeNullability(toNullable: Boolean) {
typeElement?.changeNullability(toNullable)
}
internal fun KtTypeElement.changeNullability(toNullable: Boolean) {
val factory = KtPsiFactory(this)
if (this is KtNullableType && !toNullable) {
replace(factory.createType(innerType?.text ?: return).typeElement ?: return)
}
if (this !is KtNullableType && toNullable) {
replace(factory.createType("$text?").typeElement ?: return)
}
}
internal fun AnalysisContext.fixTypeVariablesNullability() {
if (typeElementToTypeVariable.isEmpty()) return
val deepComparator = Comparator<TypeVariable> { o1, o2 ->
if (o1.typeElement == null || o2.typeElement == null) return@Comparator -1
if (o1.typeElement.isAncestor(o2.typeElement)) 1 else -1
}
for (typeVariableOwner in typeVariableOwners) {
for (typeVariable in (typeVariableOwner.innerTypeVariables() + typeVariableOwner.allTypeVariables).sortedWith(deepComparator)) {
when (typeVariable.nullability) {
Nullability.NOT_NULL -> typeVariable.changeNullability(toNullable = false)
Nullability.NULLABLE -> typeVariable.changeNullability(toNullable = true)
Nullability.UNKNOWN -> {
if (typeVariableOwner is FunctionCallTypeArgumentTarget) {
typeVariable.changeNullability(toNullable = false)
} else {
typeVariable.changeNullability(toNullable = true)
}
}
}
}
}
}
internal fun KtTypeElement.classReference(): ClassReference {
val target = when (this) {
is KtNullableType -> innerType?.safeAs()
is KtUserType -> this
else -> null
}?.referenceExpression?.resolve()
return when (target) {
is KtClassOrObject -> KtClassReference(target)
is PsiClass -> JavaClassReference(target)
is KtTypeAlias -> target.getTypeReference()?.typeElement?.classReference()
is KtTypeParameter -> TypeParameterClassReference(target)
else -> null
} ?: UnknownClassReference(text)
}
class NullabilityAnalysisFacade(
private val conversionContext: NewJ2kConverterContext?,
private val getTypeElementNullability: (KtTypeElement) -> Nullability,
private val prepareTypeElement: (KtTypeElement) -> Unit,
private val debugPrint: Boolean
) {
fun fixNullability(analysisScope: AnalysisScope) {
CommandProcessor.getInstance().runUndoTransparentAction {
runWriteAction {
analysisScope.prepareTypeElements(prepareTypeElement)
val context = ContextCreator(getTypeElementNullability).createContext(analysisScope)
if (debugPrint) {
with(Printer(context)) {
analysisScope.forEach { it.addTypeVariablesNames() }
}
}
val constraints = ConstraintsCollector(context, conversionContext, debugPrint).collectConstraints(analysisScope)
Solver(context, debugPrint).solveConstraints(constraints)
context.fixTypeVariablesNullability()
analysisScope.clearUndefinedLabels()
}
}
}
}
private fun AnalysisScope.prepareTypeElements(prepareTypeElement: (KtTypeElement) -> Unit) {
val typeElements = flatMap { it.collectDescendantsOfType<KtTypeReference>() }
typeElements.forEach { typeReference ->
val typeElement = typeReference.typeElement ?: return@forEach
if (typeElement.parentOfType<KtSuperTypeCallEntry>() == null) {
prepareTypeElement(typeElement)
}
}
}
private fun AnalysisScope.clearUndefinedLabels() {
val comments = mutableListOf<PsiComment>()
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() }
}
@@ -1,85 +0,0 @@
/*
* 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.nullabilityAnalysis
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtTypeElement
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
internal class Printer(private val analysisContext: AnalysisContext) {
private val namer = Namer(analysisContext)
internal val TypeVariable.name: String
get() = namer.name(this)
private fun ClassReference.asString() =
when (this) {
is KtClassReference -> klass.name
is JavaClassReference -> klass.name
is DescriptorClassReference -> descriptor.name.toString()
is TypeParameterClassReference -> typeParameter.name
is LiteralClassReference -> "LITERAL"
is UnknownClassReference -> text
else -> error(this::class.toString())
}
fun BoundType.asString(): String =
buildString {
if (this@asString is TypeVariableBoundType) {
append(this@asString.typeVariable.name)
append("@")
}
append(classReference.asString())
if (typeParameters.isNotEmpty()) {
typeParameters.joinTo(this, ", ", "<", ">") { it.boundType.asString() }
}
}
private fun Constraint.asString() =
when (this) {
is EqualConstraint -> "${leftBound.asString()} := ${rightBound.asString()}"
is SubtypeConstraint -> "${lowerBound.asString()} <: ${upperBound.asString()}"
else -> error("Unknown constraint ${this::class.qualifiedName}")
} + ", because of '$cameFrom'"
private fun ConstraintBound.asString(): String =
when (this) {
is LiteralBound -> nullability.toString()
is TypeVariableBound -> typeVariable.name
else -> error("Unknown constraint bound ${this::class.qualifiedName}")
}
internal fun PsiElement.addTypeVariablesNames() {
val factory = KtPsiFactory(this)
for (typeElement in collectDescendantsOfType<KtTypeElement>()) {
val typeVariableName = this@Printer.analysisContext.typeElementToTypeVariable[typeElement]?.name ?: continue
val comment = factory.createComment("/*$typeVariableName@*/")
typeElement.parent.addBefore(comment, typeElement)
}
}
internal fun List<Constraint>.listConstrains() =
joinToString(separator = "\n") { it.asString() }
}
private class Namer(analysisContext: AnalysisContext) {
val names = run {
val typeVariables =
analysisContext.typeVariableOwners.flatMap { owner ->
owner.allTypeVariables.flatMap { it.innerTypeVariables() + it }
}
typeVariables.mapIndexed { index, typeVariable ->
typeVariable to "T$index"
}.toMap()
}
fun name(typeVariable: TypeVariable): String =
names.getValue(typeVariable)
}
@@ -1,73 +0,0 @@
/*
* 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.nullabilityAnalysis
import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactoryImpl
import org.jetbrains.kotlin.resolve.jvm.checkers.mustNotBeNull
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.util.javaslang.getOrNull
internal fun KtExpression.deepestReceiver(): KtExpression =
generateSequence(this) {
if (it is KtQualifiedExpression) it.receiverExpression else null
}.last()
internal fun KtExpression.isNullable(): Boolean =
getType(analyze())?.isNullable() != false
internal fun KtExpression.getForcedNullability(): Nullability? {
val bindingContext = analyze()
val type = this.getType(bindingContext) ?: return null
if (!type.isNullable()) return Nullability.NOT_NULL
//TODO better way of getting DataFlowValueFactoryImpl
val dataFlowValue = DataFlowValueFactoryImpl(LanguageVersionSettingsImpl.DEFAULT)
.createDataFlowValue(
this,
type,
bindingContext,
getResolutionFacade().moduleDescriptor
)
val dataFlowInfo = analyze()[BindingContext.EXPRESSION_TYPE_INFO, this]?.dataFlowInfo ?: return null
return when {
dataFlowInfo.completeNullabilityInfo.get(dataFlowValue)?.getOrNull() ==
org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL -> Nullability.NOT_NULL
type.isExternallyAnnotatedNotNull(dataFlowInfo, dataFlowValue) -> Nullability.NOT_NULL
else -> null
}
}
private fun KotlinType.isExternallyAnnotatedNotNull(dataFlowInfo: DataFlowInfo, dataFlowValue: DataFlowValue): Boolean =
mustNotBeNull()?.isFromJava == true && dataFlowInfo.getStableNullability(dataFlowValue).canBeNull()
private fun IElementType.isEqualsToken() =
this == KtTokens.EQEQ
|| this == KtTokens.EXCLEQ
|| this == KtTokens.EQEQEQ
|| this == KtTokens.EXCLEQEQEQ
fun KtBinaryExpression.isComaprationWithNull() =
operationToken.isEqualsToken() &&
(left.isNullExpression() || right.isNullExpression())
fun KtExpression.isLiteral() =
this is KtStringTemplateExpression
|| this is KtLiteralStringTemplateEntry
|| this is KtConstantExpression
|| isNullExpression()
@@ -161,6 +161,7 @@ private val removeRedundantElementsProcessingGroup =
runSingleTime = true,
processings = listOf(
RemoveExplicitTypeArgumentsProcessing(),
RemoveJavaStreamsCollectCallTypeArgumentsProcessing(),
generalInspectionBasedProcessing(RedundantCompanionReferenceInspection()),
generalInspectionBasedProcessing(ExplicitThisInspection()),
intentionBasedProcessing(RemoveEmptyClassBodyIntention())
@@ -234,7 +235,10 @@ private val cleaningUpDiagnosticBasedPostProcessingGroup =
private val processings: List<NamedPostProcessingGroup> = listOf(
NamedPostProcessingGroup(
"Inferring declarations nullability",
listOf(nullabilityProcessing)
listOf(
nullabilityProcessing,
clearUndefinedLabelsProcessing
)
),
NamedPostProcessingGroup(
"Formatting code",
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.nj2k.postProcessing.processings
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.search.searches.ReferencesSearch
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
@@ -41,7 +42,6 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.utils.addToStdlib.cast
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -57,8 +57,7 @@ class RemoveExplicitPropertyTypeProcessing : ApplicabilityBasedInspectionLikePro
val withoutExpectedType =
initializer.analyzeInContext(initializer.getResolutionScope()).getType(initializer) ?: return false
val descriptor = element.resolveToDescriptorIfAny() as? CallableDescriptor ?: return false
return if (element.isVar) withoutExpectedType == descriptor.returnType
else withoutExpectedType.makeNotNullable() == descriptor.returnType?.makeNotNullable()
return withoutExpectedType == descriptor.returnType
}
override fun apply(element: KtProperty) {
@@ -101,6 +100,26 @@ class RemoveExplicitTypeArgumentsProcessing : ApplicabilityBasedInspectionLikePr
}
}
// the types arguments for Stream.collect calls cannot be explicitly specified in Kotlin
// but we need them in nullability inference, so we remove it here
class RemoveJavaStreamsCollectCallTypeArgumentsProcessing :
ApplicabilityBasedInspectionLikeProcessing<KtCallExpression>(KtCallExpression::class) {
override fun isApplicableTo(element: KtCallExpression, settings: ConverterSettings?): Boolean {
if (element.typeArgumentList == null) return false
val resolved = element.calleeExpression?.mainReference?.resolve() as? PsiMethod ?: return false
return resolved.getKotlinFqName()?.asString() == COLLECT_FQ_NAME
}
override fun apply(element: KtCallExpression) {
element.typeArgumentList?.delete()
}
companion object {
private const val COLLECT_FQ_NAME = "java.util.stream.Stream.collect"
}
}
class RemoveRedundantOverrideVisibilityProcessing : InspectionLikeProcessing {
override val writeActionNeeded = true
@@ -6,23 +6,33 @@
package org.jetbrains.kotlin.nj2k.postProcessing.processings
import com.intellij.codeInsight.actions.OptimizeImportsProcessor
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.nj2k.nullabilityAnalysis.AnalysisScope
import org.jetbrains.kotlin.nj2k.nullabilityAnalysis.NullabilityAnalysisFacade
import org.jetbrains.kotlin.nj2k.nullabilityAnalysis.nullabilityByUndefinedNullabilityComment
import org.jetbrains.kotlin.nj2k.nullabilityAnalysis.prepareTypeElementByMakingAllTypesNullableConsideringNullabilityComment
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.KtElement
import org.jetbrains.kotlin.psi.KtImportDirective
import org.jetbrains.kotlin.psi.KtImportList
import org.jetbrains.kotlin.psi.KtPackageDirective
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
val formatCodeProcessing =
postProcessing { file, rangeMarker, _ ->
file.commitAndUnblockDocument()
val codeStyleManager = CodeStyleManager.getInstance(file.project)
if (rangeMarker != null) {
if (rangeMarker.isValid) {
@@ -35,14 +45,49 @@ val formatCodeProcessing =
val nullabilityProcessing =
postProcessing { file, rangeMarker, converterContext ->
NullabilityAnalysisFacade(
converterContext,
getTypeElementNullability = { nullabilityByUndefinedNullabilityComment(it, converterContext) },
prepareTypeElement = { prepareTypeElementByMakingAllTypesNullableConsideringNullabilityComment(it, converterContext) },
debugPrint = false
).fixNullability(AnalysisScope(file, rangeMarker))
val resolutionFacade = file.getResolutionFacade()
val inferenceFacade = InferenceFacade(
NullabilityContextCollector(resolutionFacade, converterContext),
ConstraintsCollectorAggregator(
resolutionFacade,
listOf(
CommonConstraintsCollector(),
CallExpressionConstraintCollector(),
FunctionConstraintsCollector(ByInfoSuperFunctionsProvider(resolutionFacade, converterContext)),
NullabilityConstraintsCollector()
)
),
BoundTypeCalculatorImpl(resolutionFacade, NullabilityBoundTypeEnhancer(resolutionFacade)),
NullabilityStateUpdater()
)
val elements = if (rangeMarker != null) {
file.elementsInRange(rangeMarker.range ?: return@postProcessing).filterIsInstance<KtElement>()
} else listOf(file)
inferenceFacade.runOn(elements)
}
val clearUndefinedLabelsProcessing =
postProcessing { file, _, _ ->
file.clearUndefinedLabels()
}
private fun KtFile.clearUndefinedLabels() {
val comments = mutableListOf<PsiComment>()
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() }
}
val shortenReferencesProcessing =
postProcessing { file, rangeMarker, _ ->
if (rangeMarker != null) {
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* 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
@@ -725,7 +714,9 @@ class NewCodeBuilder(context: NewJ2kConverterContext) {
override fun visitArrayAccessExpressionRaw(arrayAccessExpression: JKArrayAccessExpression) {
arrayAccessExpression.expression.accept(this)
printer.par(ParenthesisKind.SQUARE) { arrayAccessExpression.indexExpression.accept(this) }
printer.print(".get(")
arrayAccessExpression.indexExpression.accept(this)
printer.print(")")
}
override fun visitPackageAccessExpressionRaw(packageAccessExpression: JKPackageAccessExpression) {
@@ -946,9 +937,7 @@ private class JKPrinter(
fun renderType(type: JKType, owner: JKTreeElement?) {
if (type is JKNoTypeImpl) return
elementInfoStorage.getOrCreateInfoForElement(type).let {
print(it.render())
}
print(elementInfoStorage.getOrCreateInfoForElement(type).render())
when (type) {
is JKClassType -> {
renderClassSymbol(type.classReference, owner)
@@ -975,6 +964,12 @@ private class JKPrinter(
if (type.nullability == Nullability.Nullable) {
printWithNoIndent("?")
}
// we print undefined types as nullable because we need smartcast work in nullability inference in post-processing
if (type.nullability == Nullability.Default
&& owner?.safeAs<JKLambdaExpression>()?.functionalType?.type != type
) {
printWithNoIndent("?")
}
}
fun renderClassSymbol(classSymbol: JKClassSymbol, owner: JKTreeElement?) {
@@ -36,14 +36,14 @@ class AddElementsInfoConversion(private val context: NewJ2kConverterContext) : R
val psiMethod = function.psi<PsiMethod>() ?: return
val descriptor = psiMethod.getJavaMethodDescriptor() ?: return
val superDescriptorsInfo =
descriptor.overriddenDescriptors.map { superDescriptor ->
val psi = superDescriptor.findPsi() ?: return@map ExternalSuperFunctionInfo(descriptor)
when (val symbol = context.symbolProvider.symbolsByPsi[psi]) {
descriptor.overriddenDescriptors.mapNotNull { superDescriptor ->
val superPsi = superDescriptor.original.findPsi()
when (val symbol = context.symbolProvider.symbolsByPsi[superPsi]) {
is JKUniverseMethodSymbol ->
InternalSuperFunctionInfo(
context.elementsInfoStorage.getOrCreateInfoForElement(symbol.target)
)
else -> ExternalSuperFunctionInfo(descriptor)
else -> ExternalSuperFunctionInfo(superDescriptor)
}
}
context.elementsInfoStorage.addEntry(function, FunctionInfo(descriptor, superDescriptorsInfo))
+3 -3
View File
@@ -1,7 +1,7 @@
class A {
fun someOther() = false
private fun formatElement(element: PsiElement): String {
private fun formatElement(element: PsiElement): String? {
var element: PsiElement = element
element = JetPsiUtil.ascendIfPropertyAccessor(element)
if (element is JetNamedFunction || element is JetProperty) {
@@ -28,9 +28,9 @@ class A {
}
fun getSelected(): ArrayList<UsageInfo> {
val result: ArrayList<UsageInfo> = ArrayList<UsageInfo>()
val result: ArrayList<UsageInfo> = ArrayList<UsageInfo?>()
for (i in 0 until myChecked.length) {
if (myChecked[i]) {
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?>()
@@ -4,7 +4,7 @@ import java.util.UUID
class ExampleClass {
override fun toString(): String {
override fun toString(): String? {
return UUID.randomUUID().toString()
}
}
+4 -4
View File
@@ -9,7 +9,7 @@ import java.util.HashMap
class Target {
var listOfPlatformType: List<String> = ArrayList()
var unresolved: UnresolvedInterface<UnresolvedGeneric?>? = UnresolvedImplementation() // Should not add import
var unresolved: UnresolvedInterface<UnresolvedGeneric?> = UnresolvedImplementation() // Should not add import
var hashMapOfNotImported: Map<ToBeImportedJava, ToBeImportedKotlin> = HashMap()
@@ -18,11 +18,11 @@ class Target {
internal fun acceptJavaClass(tbi: ToBeImportedJava?) {}
var ambiguousKotlin: IAmbiguousKotlin? = AmbiguousKotlin() // Should not add import in case of 2 declarations in Kotlin
var ambiguousKotlin: IAmbiguousKotlin = AmbiguousKotlin() // Should not add import in case of 2 declarations in Kotlin
var ambiguous: IAmbiguous? = Ambiguous() // Should not add import in case of ambiguous declarations in Kotlin and in Java
var ambiguous: IAmbiguous = Ambiguous() // Should not add import in case of ambiguous declarations in Kotlin and in Java
var ambiguousJava: IAmbiguousJava? = AmbiguousJava() // Should not add import in case of 2 declarations in Java
var ambiguousJava: IAmbiguousJava = AmbiguousJava() // Should not add import in case of 2 declarations in Java
internal fun workWithStatics() {
+1 -1
View File
@@ -3,5 +3,5 @@ import java.util.ArrayList
fun foo() {
bar(/*comment*/ArrayList<String>())
bar(/*comment*/ArrayList<String?>())
}
@@ -6,7 +6,7 @@ import java.util.ArrayList
class JavaClass {
internal fun foo(file: File?, target: List<String?>?) {
val list = ArrayList<String?>()
val list = ArrayList<String>()
if (file != null) {
list.add(file.name)
}
+8
View File
@@ -0,0 +1,8 @@
fun test() {
val x: /*T2@*/Array</*T1@*/Int> = arrayOf</*T0@*/Int>()/*Array<T0@Int>*/
val y: /*T4@*/Array</*T3@*/Int> = x/*T2@Array<T1@Int>*/
}
//T1 := T0 due to 'INITIALIZER'
//T3 := T1 due to 'INITIALIZER'
//T2 <: T4 due to 'INITIALIZER'
+16
View File
@@ -0,0 +1,16 @@
class Test {
fun foo() {
val x: /*T5@*/Array</*T4@*/Array</*T3@*/Int>> =
Array</*T2@*/Array</*T1@*/Int>>(1/*LIT*/, {
arrayOf</*T0@*/Int>(2/*LIT*/)/*Array<T0@Int>*/
}/*Function0<Int, T6@Array<T7@Int>>*/
)/*Array<T2@Array<T1@Int>>*/
}
}
//LOWER <: T0 due to 'PARAMETER'
//T7 := T0 due to 'RETURN'
//T1 := T7 due to 'PARAMETER'
//T6 <: T2 due to 'PARAMETER'
//T3 := T1 due to 'INITIALIZER'
//T4 := T2 due to 'INITIALIZER'
@@ -0,0 +1,12 @@
class X<T> {
fun x(): /*T1@*/List</*T0@*/T> {
TODO()
}
fun y() {
val a: /*T3@*/List</*T2@*/T> = x()/*T1@List<T0@T>*/
}
}
//T0 <: T2 due to 'INITIALIZER'
//T1 <: T3 due to 'INITIALIZER'
+9
View File
@@ -0,0 +1,9 @@
fun test() {
foo</*T1@*/Int>(
listOf</*T0@*/Int>()/*List<T0@Int>*/
)
}
fun <T> foo (x: /*T3@*/List</*T2@*/T>) {}
//T0 <: T1 due to 'PARAMETER'
+15
View File
@@ -0,0 +1,15 @@
fun test() {
val x: /*T2@*/X</*T1@*/Int> = X</*T0@*/Int>(10/*LIT*/)/*X<T0@Int>*/
val x: /*T5@*/X</*T4@*/Int> = X</*T3@*/Int>(10/*LIT*/, 20/*LIT*/)/*X<T3@Int>*/
}
class X<T>(x: /*T6@*/T) {
constructor(x: /*T7@*/T, y: /*T8@*/Int): this(x/*T7@T*/)
}
//LOWER <: T0 due to 'PARAMETER'
//T1 := T0 due to 'INITIALIZER'
//LOWER <: T3 due to 'PARAMETER'
//LOWER <: T8 due to 'PARAMETER'
//T4 := T3 due to 'INITIALIZER'
//T7 <: T6 due to 'PARAMETER'
@@ -0,0 +1,5 @@
class X<T>(x: /*T0@*/T) {
constructor(x: /*T1@*/T, y: /*T2@*/Int): this(x/*T1@T*/)
}
//T1 <: T0 due to 'PARAMETER'
@@ -0,0 +1,14 @@
fun test() {
val x: /*T4@*/Array</*T3@*/Array</*T2@*/Int>> = arrayOf</*T1@*/Array</*T0@*/Int>>()/*Array<T1@Array<T0@Int>>*/
val y: /*T6@*/Array</*T5@*/Int> = x/*T4@Array<T3@Array<T2@Int>>*/.get(0/*LIT*/)/*T3@Array<T2@Int>*/
val z: /*T7@*/Int = y/*T6@Array<T5@Int>*/.get(0/*LIT*/)/*T5@Int*/
}
//T2 := T0 due to 'INITIALIZER'
//T3 := T1 due to 'INITIALIZER'
//T2 := T2 due to 'RECEIVER_PARAMETER'
//T3 := T3 due to 'RECEIVER_PARAMETER'
//T5 := T2 due to 'INITIALIZER'
//T3 <: T6 due to 'INITIALIZER'
//T5 := T5 due to 'RECEIVER_PARAMETER'
//T5 <: T7 due to 'INITIALIZER'
@@ -0,0 +1,14 @@
fun test() {
val x: /*T4@*/List</*T3@*/List</*T2@*/Int>> = listOf</*T1@*/List</*T0@*/Int>>()/*List<T1@List<T0@Int>>*/
val y: /*T6@*/List</*T5@*/Int> = x/*T4@List<T3@List<T2@Int>>*/.get(0/*LIT*/)/*T3@List<T2@Int>*/
val z: /*T7@*/Int = y/*T6@List<T5@Int>*/.get(0/*LIT*/)/*T5@Int*/
}
//T0 <: T2 due to 'INITIALIZER'
//T1 <: T3 due to 'INITIALIZER'
//T2 <: T2 due to 'RECEIVER_PARAMETER'
//T3 <: T3 due to 'RECEIVER_PARAMETER'
//T2 <: T5 due to 'INITIALIZER'
//T3 <: T6 due to 'INITIALIZER'
//T5 <: T5 due to 'RECEIVER_PARAMETER'
//T5 <: T7 due to 'INITIALIZER'
+10
View File
@@ -0,0 +1,10 @@
fun test() {
val x: /*T2@*/List</*T1@*/String> = listOf</*T0@*/String>()/*List<T0@String>*/
for (y: /*T3@*/String in x/*T2@List<T1@String>*/) {
val z: /*T4@*/String = x/*T2@List<T1@String>*/
}
}
//T0 <: T1 due to 'INITIALIZER'
//T2 <: T4 due to 'INITIALIZER'
//T3 <: T1 due to 'ASSIGNMENT'
+10
View File
@@ -0,0 +1,10 @@
fun test() {
val x: /*T1@*/List</*T0@*/Int> = nya()/*T3@List<T2@Int>*/
}
fun nya(): /*T3@*/List</*T2@*/Int> {
TODO()
}
//T2 <: T0 due to 'INITIALIZER'
//T3 <: T1 due to 'INITIALIZER'
+7
View File
@@ -0,0 +1,7 @@
fun f(): /*T1@*/Int {
val x: /*T0@*/Int = 42/*LIT*/
return x/*T0@Int*/
}
//LOWER <: T0 due to 'INITIALIZER'
//T0 <: T1 due to 'RETURN'
@@ -0,0 +1,11 @@
fun test() {
val x: /*T3@*/Map</*T1@*/String, /*T2@*/Int> = nya</*T0@*/String>()/*T6@Map<T0@String, T5@Int>*/
}
fun <T> nya(): /*T6@*/Map</*T4@*/T, /*T5@*/Int> {
TODO()
}
//T1 := T0 due to 'INITIALIZER'
//T5 <: T2 due to 'INITIALIZER'
//T6 <: T3 due to 'INITIALIZER'
+11
View File
@@ -0,0 +1,11 @@
fun test() {
nya</*T2@*/Boolean>({ a: /*T0@*/Int, b: /*T1@*/Boolean -> ""/*LIT*/ }/*Function2<T0@Int, T1@Boolean, T7@String>*/)
}
fun <T> nya(x: /*T6@*/Function2</*T3@*/T, /*T4@*/Boolean, /*T5@*/String>) {
}
//LOWER <: T7 due to 'RETURN'
//T2 <: T0 due to 'PARAMETER'
//T4 <: T1 due to 'PARAMETER'
//T7 <: T5 due to 'PARAMETER'
+8
View File
@@ -0,0 +1,8 @@
fun test() {
val x: /*T5@*/Function2</*T2@*/Int, /*T3@*/String, /*T4@*/Boolean> = { a: /*T0@*/Int, b: /*T1@*/String -> true/*LIT*/ }/*Function2<T0@Int, T1@String, T6@Boolean>*/
}
//LOWER <: T6 due to 'RETURN'
//T2 <: T0 due to 'INITIALIZER'
//T3 <: T1 due to 'INITIALIZER'
//T6 <: T4 due to 'INITIALIZER'
+12
View File
@@ -0,0 +1,12 @@
fun test() {
val x: /*T6@*/Function2</*T3@*/Int, /*T4@*/String, /*T5@*/Boolean> = { a: /*T0@*/Int, b: /*T1@*/String ->
val a: /*T2@*/Boolean = true/*LIT*/
a/*T2@Boolean*/
}/*Function2<T0@Int, T1@String, T7@Boolean>*/
}
//LOWER <: T2 due to 'INITIALIZER'
//T2 <: T7 due to 'RETURN'
//T3 <: T0 due to 'INITIALIZER'
//T4 <: T1 due to 'INITIALIZER'
//T7 <: T5 due to 'INITIALIZER'
+21
View File
@@ -0,0 +1,21 @@
class Test {
fun foo1(r: /*T2@*/Function1</*T0@*/Int, /*T1@*/String>) {}
fun foo() {
foo1 { i: /*T3@*/Int ->
val str: /*T4@*/String = ""/*LIT*/
val str2: /*T5@*/String = ""/*LIT*/
if (i > 1) {
return@foo1 str/*T4@String*/
}
str2/*T5@String*/
}/*Function1<T3@Int, T6@String>*/
}
}
//LOWER <: T4 due to 'INITIALIZER'
//LOWER <: T5 due to 'INITIALIZER'
//T4 <: T6 due to 'RETURN'
//T5 <: T6 due to 'RETURN'
//T0 <: T3 due to 'PARAMETER'
//T6 <: T1 due to 'PARAMETER'
+8
View File
@@ -0,0 +1,8 @@
fun test() {
val x: /*T2@*/List</*T1@*/Int> = listOf</*T0@*/Int>()/*List<T0@Int>*/
val y: /*T4@*/List</*T3@*/Int> = x/*T2@List<T1@Int>*/
}
//T0 <: T1 due to 'INITIALIZER'
//T1 <: T3 due to 'INITIALIZER'
//T2 <: T4 due to 'INITIALIZER'
+7
View File
@@ -0,0 +1,7 @@
fun test() {
val x: /*T2@*/List</*T1@*/Int> = List</*T0@*/Int>(10/*LIT*/, { 10/*LIT*/ }/*Function0<Int, T3@Int>*/)/*List<T0@Int>*/
}
//LOWER <: T3 due to 'RETURN'
//T3 <: T0 due to 'PARAMETER'
//T0 <: T1 due to 'INITIALIZER'
+12
View File
@@ -0,0 +1,12 @@
fun foo() {
val a: /*T5@*/List</*T4@*/List</*T3@*/Int>> = listOf</*T2@*/List</*T1@*/Int>>(
listOf</*T0@*/Int>(
1/*LIT*/
)/*List<T0@Int>*/
)/*List<T2@List<T1@Int>>*/
}
//LOWER <: T0 due to 'PARAMETER'
//T0 <: T1 due to 'PARAMETER'
//T1 <: T3 due to 'INITIALIZER'
//T2 <: T4 due to 'INITIALIZER'
+16
View File
@@ -0,0 +1,16 @@
fun test() {
val x: /*T5@*/List</*T4@*/List</*T3@*/Int>> = List</*T2@*/List</*T1@*/Int>>(13/*LIT*/, {
List</*T0@*/Int>(7/*LIT*/, {
666/*LIT*/
}/*Function0<Int, T6@Int>*/)/*List<T0@Int>*/
}/*Function0<Int, T7@List<T8@Int>>*/
)/*List<T2@List<T1@Int>>*/
}
//LOWER <: T6 due to 'RETURN'
//T6 <: T0 due to 'PARAMETER'
//T0 <: T8 due to 'RETURN'
//T8 <: T1 due to 'PARAMETER'
//T7 <: T2 due to 'PARAMETER'
//T1 <: T3 due to 'INITIALIZER'
//T2 <: T4 due to 'INITIALIZER'
+16
View File
@@ -0,0 +1,16 @@
fun test() {
val x: /*T2@*/X</*T1@*/Int> = X</*T0@*/Int>()/*X<T0@Int>*/
val y: /*T5@*/Map</*T3@*/Int, /*T4@*/String> = x/*T2@X<T1@Int>*/.foo()/*T8@Map<T1@Int, T7@String>*/
}
class X<T> {
fun foo(): /*T8@*/Map</*T6@*/T, /*T7@*/String> {
TODO()
}
}
//T1 := T0 due to 'INITIALIZER'
//T1 := T1 due to 'RECEIVER_PARAMETER'
//T3 := T1 due to 'INITIALIZER'
//T7 <: T4 due to 'INITIALIZER'
//T8 <: T5 due to 'INITIALIZER'
+16
View File
@@ -0,0 +1,16 @@
fun test() {
val x: /*T6@*/Function2</*T3@*/Int, /*T4@*/String, /*T5@*/Boolean> = l@{ a: /*T0@*/Int, b: /*T1@*/String ->
val y: /*T2@*/Boolean = true/*LIT*/
if (a == 3) {
return@l y/*T2@Boolean*/
}
true/*LIT*/
}/*Function2<T0@Int, T1@String, T7@Boolean>*//*Function2<T0@Int, T1@String, T7@Boolean>*/
}
//LOWER <: T2 due to 'INITIALIZER'
//T2 <: T7 due to 'RETURN'
//LOWER <: T7 due to 'RETURN'
//T3 <: T0 due to 'INITIALIZER'
//T4 <: T1 due to 'INITIALIZER'
//T7 <: T5 due to 'INITIALIZER'
+9
View File
@@ -0,0 +1,9 @@
fun a(lst: /*T1@*/List</*T0@*/String>) {
val newList: /*T5@*/List</*T4@*/String> = lst/*T1@List<T0@String>*/
.asSequence</*T2@*/String>()/*Sequence<T2@String>*/
.toList</*T3@*/String>()/*List<T3@String>*/
}
//T0 <: T2 due to 'RECEIVER_PARAMETER'
//T2 <: T3 due to 'RECEIVER_PARAMETER'
//T3 <: T4 due to 'INITIALIZER'
@@ -0,0 +1,14 @@
fun a(lst: /*T1@*/List</*T0@*/String>) {
val newList: /*T8@*/List</*T7@*/Int> = lst/*T1@List<T0@String>*/
.asSequence</*T2@*/String>()/*Sequence<T2@String>*/
.map</*T4@*/String, /*T5@*/Int>({ x: /*T3@*/String -> 1/*LIT*/ }/*Function1<T3@String, T9@Int>*/)/*Sequence<T5@Int>*/
.toList</*T6@*/Int>()/*List<T6@Int>*/
}
//T0 <: T2 due to 'RECEIVER_PARAMETER'
//LOWER <: T9 due to 'RETURN'
//T2 <: T4 due to 'RECEIVER_PARAMETER'
//T4 <: T3 due to 'PARAMETER'
//T9 <: T5 due to 'PARAMETER'
//T5 <: T6 due to 'RECEIVER_PARAMETER'
//T6 <: T7 due to 'INITIALIZER'
+9
View File
@@ -0,0 +1,9 @@
fun test() {
val x: /*T0@*/Int = 10/*LIT*/
val y: /*T1@*/Int = x/*T0@Int*/
val z: /*T2@*/Int = y/*T1@Int*/
}
//LOWER <: T0 due to 'INITIALIZER'
//T0 <: T1 due to 'INITIALIZER'
//T1 <: T2 due to 'INITIALIZER'
+14
View File
@@ -0,0 +1,14 @@
open class A<T> {
open fun foo(x: /*T0@*/T, y: /*T2@*/List</*T1@*/T>) {
}
}
class B : A</*T6@*/Int>() {
override fun foo(x: /*T3@*/Int, y: /*T5@*/List</*T4@*/Int>) {
super/*LIT*/.foo(x/*T3@Int*/, y/*T5@List<T4@Int>*/)
}
}
//T3 := T6 due to 'SUPER_DECLARATION'
//T4 := T6 due to 'SUPER_DECLARATION'
//T2 := T5 due to 'SUPER_DECLARATION'
+7
View File
@@ -0,0 +1,7 @@
open class X<T>(x: /*T0@*/T) {
}
class Y<T>(x: /*T1@*/T) : X</*T2@*/T>(x/*T1@T*/) {
}
//T1 <: T0 due to 'PARAMETER'
@@ -0,0 +1,15 @@
interface A<T, S> {
fun foo(): /*T3@*/List</*T2@*/Map</*T0@*/T, /*T1@*/S>>
}
open class B<X> : A</*T8@*/X, /*T9@*/Int> {
override fun foo(): /*T7@*/List</*T6@*/Map</*T4@*/X, /*T5@*/Int>> {
TODO()
}
}
//T4 := T8 due to 'SUPER_DECLARATION'
//T5 := T9 due to 'SUPER_DECLARATION'
//T2 := T6 due to 'SUPER_DECLARATION'
//T3 := T7 due to 'SUPER_DECLARATION'
@@ -0,0 +1,13 @@
open class A<T> {
open fun foo(): /*T0@*/T {
TODO()
}
}
class B<T> : A</*T2@*/Int>() {
override fun foo(): /*T1@*/Int {
TODO()
}
}
//T1 := T2 due to 'SUPER_DECLARATION'
+13
View File
@@ -0,0 +1,13 @@
open class A {
open fun foo(): /*T0@*/String {
TODO()
}
}
class B : A() {
override fun foo(): /*T1@*/String {
TODO()
}
}
//T0 := T1 due to 'SUPER_DECLARATION'
+5
View File
@@ -0,0 +1,5 @@
class Test {
fun <X : /*T0@*/Any> x() {
}
}
+14
View File
@@ -0,0 +1,14 @@
fun test() {
val x: /*T0@*/String = ""/*LIT*/
val y: /*T1@*/String = ""/*LIT*/
val z: /*T2@*/String = ""/*LIT*/
val e: /*T5@*/List</*T4@*/String> = listOf</*T3@*/String>(x/*T0@String*/, y/*T1@String*/, z/*T2@String*/)/*List<T3@String>*/
}
//LOWER <: T0 due to 'INITIALIZER'
//LOWER <: T1 due to 'INITIALIZER'
//LOWER <: T2 due to 'INITIALIZER'
//T0 <: T3 due to 'PARAMETER'
//T1 <: T3 due to 'PARAMETER'
//T2 <: T3 due to 'PARAMETER'
//T3 <: T4 due to 'INITIALIZER'
@@ -0,0 +1,10 @@
class Test {
fun foo() {
val ss = Array</*T2@*/Array</*T1@*/String?>>(5/*LIT*/, { arrayOfNulls</*T0@*/String?>(5/*LIT*/)/*Array<T0@String!!U>!!L*/ }/*Function0<Int, T3@Array<T4@String>>!!L*/)
}
}
//T4 := UPPER due to 'RETURN'
//LOWER <: T3 due to 'RETURN'
//T1 := T4 due to 'PARAMETER'
//T3 <: T2 due to 'PARAMETER'
+6
View File
@@ -0,0 +1,6 @@
fun test() {
val x: /*T2@*/Array</*T1@*/Int?> = arrayOfNulls</*T0@*/Int?>(10/*LIT*/)/*Array<T0@Int!!U>!!L*/
}
//T1 := UPPER due to 'INITIALIZER'
//LOWER <: T2 due to 'INITIALIZER'
@@ -0,0 +1,7 @@
fun test() {
val x: /*T0@*/Int = 10/*LIT*/
x/*T0@Int*/ > 0/*LIT*/
}
//LOWER <: T0 due to 'INITIALIZER'
//T0 := LOWER due to 'USE_AS_RECEIVER'
@@ -0,0 +1,11 @@
import java.util.Arrays.asList
fun test() {
val x: /*T2@*/List</*T1@*/Int> = asList</*T0@*/Int>(1/*LIT*/)/*MutableList<T0@Int>!!L*/
val i: /*T3@*/Int = Integer/*LIT*/.valueOf(""/*LIT*/)/*Int!!L*/
}
//LOWER <: T0 due to 'PARAMETER'
//T1 := T0 due to 'INITIALIZER'
//LOWER <: T2 due to 'INITIALIZER'
//LOWER <: T3 due to 'INITIALIZER'
@@ -0,0 +1,27 @@
open class A<T> {
open fun foo(): /*T3@*/Map</*T0@*/T, /*T2@*/List</*T1@*/T>>? {
TODO()
}
open fun bar(): /*T4@*/T? {
return null/*NULL!!U*/
}
}
class B : A</*T10@*/Int>() {
override fun foo(): /*T8@*/Map</*T5@*/Int, /*T7@*/List</*T6@*/Int>>? {
return null/*NULL!!U*/
}
override fun bar(): /*T9@*/Int {
return 42/*LIT*/
}
}
//UPPER <: T4 due to 'RETURN'
//UPPER <: T8 due to 'RETURN'
//T5 := T10 due to 'SUPER_DECLARATION'
//T6 := T10 due to 'SUPER_DECLARATION'
//T2 := T7 due to 'SUPER_DECLARATION'
//T3 := T8 due to 'SUPER_DECLARATION'
//LOWER <: T9 due to 'RETURN'
//T9 := T10 due to 'SUPER_DECLARATION'
+19
View File
@@ -0,0 +1,19 @@
fun a(): /*T0@*/Int? {
return 42/*LIT*/
}
val b: /*T1@*/Int? = 2/*LIT*/
fun c(p: /*T2@*/Int?) {
if (p/*T2@Int*/ == null/*LIT*/);
}
fun check() {
if (a()/*T0@Int*/ == null/*LIT*/ || b/*T1@Int*/ == null/*LIT*//*LIT*/);
}
//LOWER <: T0 due to 'RETURN'
//LOWER <: T1 due to 'INITIALIZER'
//T2 := UPPER due to 'COMPARE_WITH_NULL'
//T0 := UPPER due to 'COMPARE_WITH_NULL'
//T1 := UPPER due to 'COMPARE_WITH_NULL'
@@ -0,0 +1,9 @@
fun b(m: /*T2@*/Map</*T0@*/Int, /*T1@*/String>): /*T3@*/String? {
return m/*T2@Map<T0@Int, T1@String>*/.get(42/*LIT*/)/*T1@String!!U*/
}
//T0 := T0 due to 'RECEIVER_PARAMETER'
//T1 <: T1 due to 'RECEIVER_PARAMETER'
//LOWER <: T0 due to 'PARAMETER'
//T2 := LOWER due to 'USE_AS_RECEIVER'
//UPPER <: T3 due to 'RETURN'
@@ -0,0 +1,20 @@
fun <T, E, F, S> foo(x: /*T0@*/T, y: /*T2@*/List</*T1@*/E>, z: /*T4@*/List</*T3@*/F>, a: /*T5@*/S) {}
fun bar() {
val lst: /*T8@*/List</*T7@*/Int?> = listOf</*T6@*/Int?>(null/*NULL!!U*/)/*List<T6@Int>!!L*/
val lst2: /*T11@*/List</*T10@*/Int> = listOf</*T9@*/Int>(1/*LIT*/)/*List<T9@Int>!!L*/
foo</*T12@*/Int?, /*T13@*/Int?, /*T14@*/Int, /*T15@*/String>(null/*NULL!!U*/, lst/*T8@List<T7@Int>*/, lst2/*T11@List<T10@Int>*/, "nya"/*LIT*/)
}
//UPPER <: T6 due to 'PARAMETER'
//T6 <: T7 due to 'INITIALIZER'
//LOWER <: T8 due to 'INITIALIZER'
//LOWER <: T9 due to 'PARAMETER'
//T9 <: T10 due to 'INITIALIZER'
//LOWER <: T11 due to 'INITIALIZER'
//UPPER <: T12 due to 'PARAMETER'
//T7 <: T13 due to 'PARAMETER'
//T8 <: T2 due to 'PARAMETER'
//T10 <: T14 due to 'PARAMETER'
//T11 <: T4 due to 'PARAMETER'
//LOWER <: T15 due to 'PARAMETER'
+40
View File
@@ -0,0 +1,40 @@
fun notNullParameters(f: /*T3@*/Function2</*T0@*/Int, /*T1@*/Int, /*T2@*/String>) {}
fun nullableParameter(f: /*T7@*/Function2</*T4@*/Int?, /*T5@*/Int, /*T6@*/String>) {}
fun nullableReturnType(f: /*T11@*/Function2</*T8@*/Int, /*T9@*/Int, /*T10@*/String?>) {}
fun test() {
notNullParameters({ i: /*T12@*/Int, j: /*T13@*/Int ->
if (i/*T12@Int*/ < 10/*LIT*//*LIT*/ && j/*T13@Int*/ > 0/*LIT*//*LIT*//*LIT*/) ""/*LIT*/ else ""/*LIT*/
}/*Function2<T12@Int, T13@Int, T18@String>!!L*/)
nullableParameter({ i: /*T14@*/Int?, j: /*T15@*/Int ->
if (i/*T14@Int*/ == null/*LIT*/) ""/*LIT*/ else ""/*LIT*/
}/*Function2<T14@Int, T15@Int, T19@String>!!L*/)
nullableReturnType({ i: /*T16@*/Int, j: /*T17@*/Int ->
if (i/*T16@Int*/ < 10/*LIT*//*LIT*/) return@nullableReturnType null/*NULL!!U*/
return@nullableReturnType "nya"/*LIT*/
}/*Function2<T16@Int, T17@Int, T20@String>!!L*/)
}
//T12 := LOWER due to 'USE_AS_RECEIVER'
//T13 := LOWER due to 'USE_AS_RECEIVER'
//LOWER <: T18 due to 'RETURN'
//T0 <: T12 due to 'PARAMETER'
//T1 <: T13 due to 'PARAMETER'
//T18 <: T2 due to 'PARAMETER'
//LOWER <: T3 due to 'PARAMETER'
//T14 := UPPER due to 'COMPARE_WITH_NULL'
//LOWER <: T19 due to 'RETURN'
//T4 <: T14 due to 'PARAMETER'
//T5 <: T15 due to 'PARAMETER'
//T19 <: T6 due to 'PARAMETER'
//LOWER <: T7 due to 'PARAMETER'
//T16 := LOWER due to 'USE_AS_RECEIVER'
//UPPER <: T20 due to 'RETURN'
//LOWER <: T20 due to 'RETURN'
//T8 <: T16 due to 'PARAMETER'
//T9 <: T17 due to 'PARAMETER'
//T20 <: T10 due to 'PARAMETER'
//LOWER <: T11 due to 'PARAMETER'
+5
View File
@@ -0,0 +1,5 @@
fun test(a: /*T0@*/Boolean) {
if (a/*T0@Boolean*/) {}
}
//T0 := LOWER due to 'USE_AS_RECEIVER'
+25
View File
@@ -0,0 +1,25 @@
// RUNTIME_WITH_FULL_JDK
import java.util.stream.Collector
import java.util.stream.Collectors
import java.util.stream.Stream
fun test(list: /*T1@*/List</*T0@*/String>) {
val x: /*T9@*/List</*T8@*/String> = list/*T1@List<T0@String>*/.stream()/*Stream<T0@String>!!L*/
.map</*T3@*/String>({ x: /*T2@*/String -> x/*T2@String*/ + ""/*LIT*//*LIT*/ }/*Function1<T2@String, T10@String>!!L*/)/*Stream<T3@String>*/
.collect</*T6@*/List</*T5@*/String>, /*T7@*/Any?>(Collectors/*LIT*/.toList</*T4@*/String>()/*Collector<T4@String, Any, MutableList<T4@String>>*/)/*T6@List<T5@String>*/
}
//T0 <: T0 due to 'RECEIVER_PARAMETER'
//T1 := LOWER due to 'USE_AS_RECEIVER'
//T2 := LOWER due to 'USE_AS_RECEIVER'
//LOWER <: T10 due to 'RETURN'
//T0 := T0 due to 'RECEIVER_PARAMETER'
//T0 <: T2 due to 'PARAMETER'
//T10 <: T3 due to 'PARAMETER'
//T3 := T3 due to 'RECEIVER_PARAMETER'
//T3 := T4 due to 'PARAMETER'
//T5 := T4 due to 'PARAMETER'
//T5 <: T8 due to 'INITIALIZER'
//T6 <: T9 due to 'INITIALIZER'
+23
View File
@@ -0,0 +1,23 @@
class Test {
fun foo1(r: /*T2@*/Function1</*T0@*/Int, /*T1@*/String?>) {}
fun foo() {
foo1({ x: /*T3@*/Int -> ""/*LIT*/ }/*Function1<T3@Int, T5@String>!!L*/)
foo1({ i: /*T4@*/Int ->
if (i/*T4@Int*/ > 1/*LIT*//*LIT*/) {
return@foo1 null/*NULL!!U*/
}
""/*LIT*/
}/*Function1<T4@Int, T6@String>!!L*/)
}
}
//LOWER <: T5 due to 'RETURN'
//T0 <: T3 due to 'PARAMETER'
//T5 <: T1 due to 'PARAMETER'
//LOWER <: T2 due to 'PARAMETER'
//T4 := LOWER due to 'USE_AS_RECEIVER'
//UPPER <: T6 due to 'RETURN'
//LOWER <: T6 due to 'RETURN'
//T0 <: T4 due to 'PARAMETER'
//T6 <: T1 due to 'PARAMETER'
//LOWER <: T2 due to 'PARAMETER'
@@ -0,0 +1,9 @@
fun test() {
val x: /*T2@*/List</*T1@*/Int?> = listOf</*T0@*/Int?>(1/*LIT*/, null/*NULL!!U*/, 3/*LIT*/)/*List<T0@Int>!!L*/
}
//LOWER <: T0 due to 'PARAMETER'
//UPPER <: T0 due to 'PARAMETER'
//LOWER <: T0 due to 'PARAMETER'
//T0 <: T1 due to 'INITIALIZER'
//LOWER <: T2 due to 'INITIALIZER'
+8
View File
@@ -0,0 +1,8 @@
fun test(a: /*T1@*/List</*T0@*/Int>) {
for (i: /*T2@*/Int in a/*T1@List<T0@Int>*/) {
}
}
//T2 <: T0 due to 'ASSIGNMENT'
//T1 := LOWER due to 'USE_AS_RECEIVER'
+31
View File
@@ -0,0 +1,31 @@
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*/) {
println(value/*T10@Int*/ + 1/*LIT*//*LIT*/)
}
}
for (i: /*T11@*/Int in list1/*T4@List<T3@Int>*/) {
i/*T11@Int*/ + 1/*LIT*/
}
for (i: /*T12@*/String? in list2/*T6@List<T5@String>*/) {
i/*T12@String*/ == null
}
}
//T2 := LOWER due to 'USE_AS_RECEIVER'
//T9 := LOWER due to 'USE_AS_RECEIVER'
//T8 <: T10 due to 'INITIALIZER'
//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'
//T11 := LOWER due to 'USE_AS_RECEIVER'
//T11 <: T3 due to 'ASSIGNMENT'
//T4 := LOWER due to 'USE_AS_RECEIVER'
//T12 := UPPER due to 'COMPARE_WITH_NULL'
//T12 <: T5 due to 'ASSIGNMENT'
//T6 := LOWER due to 'USE_AS_RECEIVER'
@@ -0,0 +1,16 @@
// RUNTIME_WITH_FULL_JDK
fun main() {
val list: /*T5@*/List</*T4@*/String?> =
listOf</*T0@*/String?>(""/*LIT*/, null/*NULL!!U*/)/*List<T0@String>!!L*/
.map</*T2@*/String?, /*T3@*/String?>({ x: /*T1@*/String? -> x/*T1@String*/ }/*Function1<T1@String, T6@String>!!L*/)/*List<T3@String>!!L*/
}
//LOWER <: T0 due to 'PARAMETER'
//UPPER <: T0 due to 'PARAMETER'
//T1 <: T6 due to 'RETURN'
//T0 <: T2 due to 'RECEIVER_PARAMETER'
//T2 <: T1 due to 'PARAMETER'
//T6 <: T3 due to 'PARAMETER'
//T3 <: T4 due to 'INITIALIZER'
//LOWER <: T5 due to 'INITIALIZER'

Some files were not shown because too many files have changed in this diff Show More