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:
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
|
||||
+259
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+39
@@ -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
|
||||
}
|
||||
+136
@@ -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
|
||||
}
|
||||
+42
@@ -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
|
||||
}
|
||||
}
|
||||
+196
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
-50
@@ -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)
|
||||
}
|
||||
+58
@@ -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
|
||||
+112
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+135
@@ -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
|
||||
}
|
||||
}
|
||||
+21
@@ -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
|
||||
)
|
||||
}
|
||||
+105
@@ -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)
|
||||
}
|
||||
+117
@@ -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()
|
||||
}
|
||||
+55
@@ -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
|
||||
}
|
||||
}
|
||||
+43
@@ -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
|
||||
}
|
||||
+56
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-203
@@ -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()
|
||||
}
|
||||
}
|
||||
-63
@@ -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()
|
||||
}
|
||||
-310
@@ -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))
|
||||
}
|
||||
-133
@@ -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",
|
||||
|
||||
+22
-3
@@ -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
|
||||
|
||||
|
||||
+59
-14
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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
@@ -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
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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
@@ -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'
|
||||
@@ -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
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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
@@ -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'
|
||||
@@ -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'
|
||||
+13
@@ -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'
|
||||
@@ -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'
|
||||
@@ -0,0 +1,5 @@
|
||||
class Test {
|
||||
fun <X : /*T0@*/Any> x() {
|
||||
}
|
||||
}
|
||||
|
||||
+14
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -0,0 +1,5 @@
|
||||
fun test(a: /*T0@*/Boolean) {
|
||||
if (a/*T0@Boolean*/) {}
|
||||
}
|
||||
|
||||
//T0 := LOWER due to 'USE_AS_RECEIVER'
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user