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.
|
* 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.
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.generators.tests
|
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.kapt3.test.AbstractKotlinKaptContextTest
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
|
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
|
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractNullabilityAnalysisTest
|
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionTest
|
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.AbstractBlackBoxCodegenTestForNoArg
|
||||||
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
|
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
|
||||||
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
|
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
|
||||||
@@ -987,15 +977,18 @@ fun main(args: Array<String>) {
|
|||||||
testClass<AbstractNewJavaToKotlinConverterSingleFileTest> {
|
testClass<AbstractNewJavaToKotlinConverterSingleFileTest> {
|
||||||
model("newJ2k", pattern = """^([^\.]+)\.java$""")
|
model("newJ2k", pattern = """^([^\.]+)\.java$""")
|
||||||
}
|
}
|
||||||
|
testClass<AbstractCommonConstraintCollectorTest> {
|
||||||
|
model("inference/common")
|
||||||
|
}
|
||||||
|
testClass<AbstractNullabilityInferenceTest> {
|
||||||
|
model("inference/nullability")
|
||||||
|
}
|
||||||
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
|
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
|
||||||
model("copyPaste", pattern = """^([^\.]+)\.java$""")
|
model("copyPaste", pattern = """^([^\.]+)\.java$""")
|
||||||
}
|
}
|
||||||
testClass<AbstractTextNewJavaToKotlinCopyPasteConversionTest> {
|
testClass<AbstractTextNewJavaToKotlinCopyPasteConversionTest> {
|
||||||
model("copyPastePlainText", pattern = """^([^\.]+)\.txt$""")
|
model("copyPastePlainText", pattern = """^([^\.]+)\.txt$""")
|
||||||
}
|
}
|
||||||
testClass<AbstractNullabilityAnalysisTest> {
|
|
||||||
model("nullabilityAnalysis")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
testGroup("jps-plugin/jps-tests/test", "jps-plugin/testData") {
|
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.kapt3.test.AbstractKotlinKaptContextTest
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
|
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
|
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractNullabilityAnalysisTest
|
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionTest
|
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.AbstractBlackBoxCodegenTestForNoArg
|
||||||
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
|
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
|
||||||
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
|
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
|
||||||
@@ -992,15 +993,18 @@ fun main(args: Array<String>) {
|
|||||||
testClass<AbstractNewJavaToKotlinConverterSingleFileTest> {
|
testClass<AbstractNewJavaToKotlinConverterSingleFileTest> {
|
||||||
model("newJ2k", pattern = """^([^\.]+)\.java$""")
|
model("newJ2k", pattern = """^([^\.]+)\.java$""")
|
||||||
}
|
}
|
||||||
|
testClass<AbstractCommonConstraintCollectorTest> {
|
||||||
|
model("inference/common")
|
||||||
|
}
|
||||||
|
testClass<AbstractNullabilityInferenceTest> {
|
||||||
|
model("inference/nullability")
|
||||||
|
}
|
||||||
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
|
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
|
||||||
model("copyPaste", pattern = """^([^\.]+)\.java$""")
|
model("copyPaste", pattern = """^([^\.]+)\.java$""")
|
||||||
}
|
}
|
||||||
testClass<AbstractTextNewJavaToKotlinCopyPasteConversionTest> {
|
testClass<AbstractTextNewJavaToKotlinCopyPasteConversionTest> {
|
||||||
model("copyPastePlainText", pattern = """^([^\.]+)\.txt$""")
|
model("copyPastePlainText", pattern = """^([^\.]+)\.txt$""")
|
||||||
}
|
}
|
||||||
testClass<AbstractNullabilityAnalysisTest> {
|
|
||||||
model("nullabilityAnalysis")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
testGroup("jps-plugin/jps-tests/test", "jps-plugin/testData") {
|
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.kapt3.test.AbstractKotlinKaptContextTest
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
|
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
|
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractNullabilityAnalysisTest
|
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionTest
|
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.AbstractBlackBoxCodegenTestForNoArg
|
||||||
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
|
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
|
||||||
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
|
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
|
||||||
@@ -952,15 +953,18 @@ fun main(args: Array<String>) {
|
|||||||
testClass<AbstractNewJavaToKotlinConverterSingleFileTest> {
|
testClass<AbstractNewJavaToKotlinConverterSingleFileTest> {
|
||||||
model("newJ2k", pattern = """^([^\.]+)\.java$""")
|
model("newJ2k", pattern = """^([^\.]+)\.java$""")
|
||||||
}
|
}
|
||||||
|
testClass<AbstractCommonConstraintCollectorTest> {
|
||||||
|
model("inference/common")
|
||||||
|
}
|
||||||
|
testClass<AbstractNullabilityInferenceTest> {
|
||||||
|
model("inference/nullability")
|
||||||
|
}
|
||||||
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
|
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
|
||||||
model("copyPaste", pattern = """^([^\.]+)\.java$""")
|
model("copyPaste", pattern = """^([^\.]+)\.java$""")
|
||||||
}
|
}
|
||||||
testClass<AbstractTextNewJavaToKotlinCopyPasteConversionTest> {
|
testClass<AbstractTextNewJavaToKotlinCopyPasteConversionTest> {
|
||||||
model("copyPastePlainText", pattern = """^([^\.]+)\.txt$""")
|
model("copyPastePlainText", pattern = """^([^\.]+)\.txt$""")
|
||||||
}
|
}
|
||||||
testClass<AbstractNullabilityAnalysisTest> {
|
|
||||||
model("nullabilityAnalysis")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* There is no jps in AS
|
/* 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.kapt3.test.AbstractKotlinKaptContextTest
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
|
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
|
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractNullabilityAnalysisTest
|
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionTest
|
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.AbstractBlackBoxCodegenTestForNoArg
|
||||||
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
|
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
|
||||||
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
|
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
|
||||||
@@ -956,15 +957,18 @@ fun main(args: Array<String>) {
|
|||||||
testClass<AbstractNewJavaToKotlinConverterSingleFileTest> {
|
testClass<AbstractNewJavaToKotlinConverterSingleFileTest> {
|
||||||
model("newJ2k", pattern = """^([^\.]+)\.java$""")
|
model("newJ2k", pattern = """^([^\.]+)\.java$""")
|
||||||
}
|
}
|
||||||
|
testClass<AbstractCommonConstraintCollectorTest> {
|
||||||
|
model("inference/common")
|
||||||
|
}
|
||||||
|
testClass<AbstractNullabilityInferenceTest> {
|
||||||
|
model("inference/nullability")
|
||||||
|
}
|
||||||
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
|
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
|
||||||
model("copyPaste", pattern = """^([^\.]+)\.java$""")
|
model("copyPaste", pattern = """^([^\.]+)\.java$""")
|
||||||
}
|
}
|
||||||
testClass<AbstractTextNewJavaToKotlinCopyPasteConversionTest> {
|
testClass<AbstractTextNewJavaToKotlinCopyPasteConversionTest> {
|
||||||
model("copyPastePlainText", pattern = """^([^\.]+)\.txt$""")
|
model("copyPastePlainText", pattern = """^([^\.]+)\.txt$""")
|
||||||
}
|
}
|
||||||
testClass<AbstractNullabilityAnalysisTest> {
|
|
||||||
model("nullabilityAnalysis")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* There is no jps in AS
|
/* 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.kapt3.test.AbstractKotlinKaptContextTest
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
|
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
|
import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractNullabilityAnalysisTest
|
|
||||||
import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionTest
|
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.AbstractBlackBoxCodegenTestForNoArg
|
||||||
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
|
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
|
||||||
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
|
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
|
||||||
@@ -952,15 +953,18 @@ fun main(args: Array<String>) {
|
|||||||
testClass<AbstractNewJavaToKotlinConverterSingleFileTest> {
|
testClass<AbstractNewJavaToKotlinConverterSingleFileTest> {
|
||||||
model("newJ2k", pattern = """^([^\.]+)\.java$""")
|
model("newJ2k", pattern = """^([^\.]+)\.java$""")
|
||||||
}
|
}
|
||||||
|
testClass<AbstractCommonConstraintCollectorTest> {
|
||||||
|
model("inference/common")
|
||||||
|
}
|
||||||
|
testClass<AbstractNullabilityInferenceTest> {
|
||||||
|
model("inference/nullability")
|
||||||
|
}
|
||||||
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
|
testClass<AbstractNewJavaToKotlinCopyPasteConversionTest> {
|
||||||
model("copyPaste", pattern = """^([^\.]+)\.java$""")
|
model("copyPaste", pattern = """^([^\.]+)\.java$""")
|
||||||
}
|
}
|
||||||
testClass<AbstractTextNewJavaToKotlinCopyPasteConversionTest> {
|
testClass<AbstractTextNewJavaToKotlinCopyPasteConversionTest> {
|
||||||
model("copyPastePlainText", pattern = """^([^\.]+)\.txt$""")
|
model("copyPastePlainText", pattern = """^([^\.]+)\.txt$""")
|
||||||
}
|
}
|
||||||
testClass<AbstractNullabilityAnalysisTest> {
|
|
||||||
model("nullabilityAnalysis")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* There is no jps in AS
|
/* There is no jps in AS
|
||||||
|
|||||||
@@ -1,17 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* 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.
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.idea.test
|
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.openapi.project.Project
|
||||||
import com.intellij.psi.PsiDocumentManager
|
import com.intellij.psi.PsiDocumentManager
|
||||||
import com.intellij.testFramework.LightPlatformTestCase
|
import com.intellij.testFramework.LightPlatformTestCase
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||||
import org.jetbrains.kotlin.diagnostics.Severity
|
import org.jetbrains.kotlin.diagnostics.Severity
|
||||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||||
import org.jetbrains.kotlin.idea.caches.project.LibraryModificationTracker
|
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 org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
fun KtFile.dumpTextWithErrors(): String {
|
fun KtFile.dumpTextWithErrors(ignoreErrors: Set<DiagnosticFactory<*>> = emptySet()): String {
|
||||||
val text = text
|
val text = text
|
||||||
if (InTextDirectivesUtils.isDirectiveDefined(text, "// DISABLE-ERRORS")) return text
|
if (InTextDirectivesUtils.isDirectiveDefined(text, "// DISABLE-ERRORS")) return text
|
||||||
val diagnostics = analyzeWithContent().diagnostics
|
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
|
if (errors.isEmpty()) return text
|
||||||
val header = errors.joinToString("\n", postfix = "\n") { "// ERROR: " + DefaultErrorMessages.render(it).replace('\n', ' ') }
|
val header = errors.joinToString("\n", postfix = "\n") { "// ERROR: " + DefaultErrorMessages.render(it).replace('\n', ' ') }
|
||||||
return header + text
|
return header + text
|
||||||
|
|||||||
@@ -1,17 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* 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.
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.j2k
|
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.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||||
import org.jetbrains.kotlin.idea.test.dumpTextWithErrors
|
import org.jetbrains.kotlin.idea.test.dumpTextWithErrors
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
@@ -73,7 +63,8 @@ abstract class AbstractJavaToKotlinConverterSingleFileTest : AbstractJavaToKotli
|
|||||||
var actual = reformat(rawConverted, project, reformatInFun)
|
var actual = reformat(rawConverted, project, reformatInFun)
|
||||||
|
|
||||||
if (prefix == "file") {
|
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.
|
* 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.firstIsInstanceOrNull
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
|
|
||||||
internal class Solver(
|
internal class Solver(
|
||||||
private val analysisContext: AnalysisContext,
|
private val analysisContext: InferenceContext,
|
||||||
private val printConstraints: Boolean
|
private val printConstraints: Boolean
|
||||||
) {
|
) {
|
||||||
private val printer = Printer(analysisContext)
|
private val printer = DebugPrinter(analysisContext)
|
||||||
|
|
||||||
private fun List<Constraint>.printDebugInfo(step: Int) =
|
private fun List<Constraint>.printDebugInfo(step: Int) =
|
||||||
with(printer) {
|
with(printer) {
|
||||||
if (printConstraints) {
|
if (printConstraints) {
|
||||||
println("Step $step:")
|
println("Step $step:")
|
||||||
println(listConstrains())
|
for (constraint in this@printDebugInfo) {
|
||||||
|
println(constraint.asString())
|
||||||
|
}
|
||||||
println()
|
println()
|
||||||
println("type variables:")
|
println("type variables:")
|
||||||
for (typeVariable in analysisContext.typeElementToTypeVariable.values) {
|
for (typeVariable in analysisContext.typeVariables) {
|
||||||
println("${typeVariable.name} := ${typeVariable.nullability}")
|
println("${typeVariable.name} := ${typeVariable.state}")
|
||||||
}
|
}
|
||||||
println("---------------\n")
|
println("---------------\n")
|
||||||
}
|
}
|
||||||
@@ -31,7 +33,7 @@ internal class Solver(
|
|||||||
|
|
||||||
fun solveConstraints(constraints: List<Constraint>) {
|
fun solveConstraints(constraints: List<Constraint>) {
|
||||||
val mutableConstraints = constraints.toMutableList()
|
val mutableConstraints = constraints.toMutableList()
|
||||||
var currentStep = ConstraintCameFrom.values().first()
|
var currentStep = ConstraintPriority.values().first()
|
||||||
|
|
||||||
var i = 0
|
var i = 0
|
||||||
do {
|
do {
|
||||||
@@ -40,22 +42,22 @@ internal class Solver(
|
|||||||
printDebugInfo(i)
|
printDebugInfo(i)
|
||||||
somethingChanged = handleConstraintsWithNullableLowerBound(currentStep) || somethingChanged
|
somethingChanged = handleConstraintsWithNullableLowerBound(currentStep) || somethingChanged
|
||||||
somethingChanged = handleConstraintsWithNotNullUpperBound(currentStep) || somethingChanged
|
somethingChanged = handleConstraintsWithNotNullUpperBound(currentStep) || somethingChanged
|
||||||
somethingChanged = handleEqualConstraints(currentStep) || somethingChanged
|
somethingChanged = handleEqualsConstraints(currentStep) || somethingChanged
|
||||||
somethingChanged = substituteConstraints() || somethingChanged
|
somethingChanged = substituteConstraints() || somethingChanged
|
||||||
cleanConstraints()
|
cleanConstraints()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!somethingChanged) {
|
if (!somethingChanged) {
|
||||||
if (currentStep.ordinal < ConstraintCameFrom.values().lastIndex) {
|
if (currentStep.ordinal < ConstraintPriority.values().lastIndex) {
|
||||||
currentStep = ConstraintCameFrom.values()[currentStep.ordinal + 1]
|
currentStep = ConstraintPriority.values()[currentStep.ordinal + 1]
|
||||||
somethingChanged = true
|
somethingChanged = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!somethingChanged) {
|
if (!somethingChanged) {
|
||||||
val typeVariable = mutableConstraints.getTypeVariableAsEqualsOrUpperBound()
|
val typeVariable = mutableConstraints.getTypeVariableAsEqualsOrUpperBound()
|
||||||
if (typeVariable != null) {
|
if (typeVariable != null) {
|
||||||
typeVariable.setNullabilityIfNotFixed(Nullability.NOT_NULL)
|
typeVariable.setStateIfNotFixed(State.LOWER)
|
||||||
somethingChanged = true
|
somethingChanged = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,21 +67,14 @@ internal class Solver(
|
|||||||
|
|
||||||
|
|
||||||
private fun MutableList<Constraint>.cleanConstraints() {
|
private fun MutableList<Constraint>.cleanConstraints() {
|
||||||
val newConstraints =
|
removeIf { constraint ->
|
||||||
distinct()
|
constraint is SubtypeConstraint
|
||||||
.filterNot { constraint ->
|
&& constraint.subtype is LiteralBound
|
||||||
constraint is SubtypeConstraint
|
&& constraint.supertype is LiteralBound
|
||||||
&& constraint.lowerBound is LiteralBound
|
|
||||||
&& constraint.upperBound is LiteralBound
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newConstraints.size < size) {
|
|
||||||
clear()
|
|
||||||
addAll(newConstraints)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MutableList<Constraint>.handleConstraintsWithNullableLowerBound(step: ConstraintCameFrom): Boolean {
|
private fun MutableList<Constraint>.handleConstraintsWithNullableLowerBound(step: ConstraintPriority): Boolean {
|
||||||
var somethingChanged = false
|
var somethingChanged = false
|
||||||
val nullableConstraints = getConstraintsWithNullableLowerBound(step)
|
val nullableConstraints = getConstraintsWithNullableLowerBound(step)
|
||||||
if (nullableConstraints.isNotEmpty()) {
|
if (nullableConstraints.isNotEmpty()) {
|
||||||
@@ -87,14 +82,14 @@ internal class Solver(
|
|||||||
for ((_, upperBound) in nullableConstraints) {
|
for ((_, upperBound) in nullableConstraints) {
|
||||||
if (upperBound is TypeVariableBound) {
|
if (upperBound is TypeVariableBound) {
|
||||||
somethingChanged = true
|
somethingChanged = true
|
||||||
upperBound.typeVariable.setNullabilityIfNotFixed(Nullability.NULLABLE)
|
upperBound.typeVariable.setStateIfNotFixed(State.UPPER)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return somethingChanged
|
return somethingChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MutableList<Constraint>.handleConstraintsWithNotNullUpperBound(step: ConstraintCameFrom): Boolean {
|
private fun MutableList<Constraint>.handleConstraintsWithNotNullUpperBound(step: ConstraintPriority): Boolean {
|
||||||
var somethingChanged = false
|
var somethingChanged = false
|
||||||
val nullableConstraints = getConstraintsWithNotNullUpperBound(step)
|
val nullableConstraints = getConstraintsWithNotNullUpperBound(step)
|
||||||
if (nullableConstraints.isNotEmpty()) {
|
if (nullableConstraints.isNotEmpty()) {
|
||||||
@@ -102,37 +97,36 @@ internal class Solver(
|
|||||||
for ((lowerBound, _) in nullableConstraints) {
|
for ((lowerBound, _) in nullableConstraints) {
|
||||||
if (lowerBound is TypeVariableBound) {
|
if (lowerBound is TypeVariableBound) {
|
||||||
somethingChanged = true
|
somethingChanged = true
|
||||||
lowerBound.typeVariable.setNullabilityIfNotFixed(Nullability.NOT_NULL)
|
lowerBound.typeVariable.setStateIfNotFixed(State.LOWER)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return somethingChanged
|
return somethingChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ConstraintBound.fixedNullability(): Nullability? =
|
private fun ConstraintBound.fixedState(): State? = when {
|
||||||
when {
|
this is LiteralBound -> state
|
||||||
this is LiteralBound -> nullability
|
this is TypeVariableBound && typeVariable.isFixed -> typeVariable.state
|
||||||
this is TypeVariableBound && typeVariable.isFixed -> typeVariable.nullability
|
else -> null
|
||||||
else -> null
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun MutableList<Constraint>.handleEqualConstraints(step: ConstraintCameFrom): Boolean {
|
private fun MutableList<Constraint>.handleEqualsConstraints(step: ConstraintPriority): Boolean {
|
||||||
var somethingChanged = false
|
var somethingChanged = false
|
||||||
val equalsConstraints = filterIsInstance<EqualConstraint>().filter { it.cameFrom <= step }
|
val equalsConstraints = filterIsInstance<EqualsConstraint>().filter { it.priority <= step }
|
||||||
if (equalsConstraints.isNotEmpty()) {
|
if (equalsConstraints.isNotEmpty()) {
|
||||||
for (constraint in equalsConstraints) {
|
for (constraint in equalsConstraints) {
|
||||||
val (leftBound, rightBound) = constraint
|
val (leftBound, rightBound) = constraint
|
||||||
when {
|
when {
|
||||||
leftBound is TypeVariableBound && rightBound.fixedNullability() != null -> {
|
leftBound is TypeVariableBound && rightBound.fixedState() != null -> {
|
||||||
this -= constraint
|
this -= constraint
|
||||||
somethingChanged = true
|
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
|
this -= constraint
|
||||||
somethingChanged = true
|
somethingChanged = true
|
||||||
rightBound.typeVariable.setNullabilityIfNotFixed(leftBound.fixedNullability()!!)
|
rightBound.typeVariable.setStateIfNotFixed(leftBound.fixedState()!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,39 +142,38 @@ internal class Solver(
|
|||||||
val (lower, upper) = constraint
|
val (lower, upper) = constraint
|
||||||
if (lower is TypeVariableBound && lower.typeVariable.isFixed) {
|
if (lower is TypeVariableBound && lower.typeVariable.isFixed) {
|
||||||
somethingChanged = true
|
somethingChanged = true
|
||||||
constraint.lowerBound = LiteralBound(lower.typeVariable.nullability)
|
constraint.subtype = lower.typeVariable.state.constraintBound
|
||||||
}
|
}
|
||||||
if (upper is TypeVariableBound && upper.typeVariable.isFixed) {
|
if (upper is TypeVariableBound && upper.typeVariable.isFixed) {
|
||||||
somethingChanged = true
|
somethingChanged = true
|
||||||
constraint.upperBound = LiteralBound(upper.typeVariable.nullability)
|
constraint.supertype = upper.typeVariable.state.constraintBound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return somethingChanged
|
return somethingChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<Constraint>.getConstraintsWithNullableLowerBound(cameFrom: ConstraintCameFrom) =
|
private fun List<Constraint>.getConstraintsWithNullableLowerBound(priority: ConstraintPriority) =
|
||||||
filterIsInstance<SubtypeConstraint>().filter { constraint ->
|
filterIsInstance<SubtypeConstraint>().filter { constraint ->
|
||||||
constraint.cameFrom <= cameFrom
|
constraint.priority <= priority
|
||||||
&& constraint.lowerBound.safeAs<LiteralBound>()?.nullability == Nullability.NULLABLE
|
&& 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 ->
|
filterIsInstance<SubtypeConstraint>().filter { constraint ->
|
||||||
constraint.cameFrom <= cameFrom
|
constraint.priority <= priority
|
||||||
&& constraint.upperBound.safeAs<LiteralBound>()?.nullability == Nullability.NOT_NULL
|
&& constraint.supertype.safeAs<LiteralBound>()?.state == State.LOWER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun List<Constraint>.getTypeVariableAsEqualsOrUpperBound(): TypeVariable? =
|
private fun List<Constraint>.getTypeVariableAsEqualsOrUpperBound(): TypeVariable? =
|
||||||
asSequence().filterIsInstance<SubtypeConstraint>()
|
asSequence().filterIsInstance<SubtypeConstraint>()
|
||||||
.map { it.upperBound }
|
.map { it.supertype }
|
||||||
.firstIsInstanceOrNull<TypeVariableBound>()
|
.firstIsInstanceOrNull<TypeVariableBound>()
|
||||||
?.typeVariable
|
?.typeVariable
|
||||||
?: asSequence().filterIsInstance<EqualConstraint>()
|
?: asSequence().filterIsInstance<EqualsConstraint>()
|
||||||
.flatMap { sequenceOf(it.leftBound, it.rightBound) }
|
.flatMap { sequenceOf(it.left, it.right) }
|
||||||
.firstIsInstanceOrNull<TypeVariableBound>()
|
.firstIsInstanceOrNull<TypeVariableBound>()
|
||||||
?.typeVariable
|
?.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,
|
runSingleTime = true,
|
||||||
processings = listOf(
|
processings = listOf(
|
||||||
RemoveExplicitTypeArgumentsProcessing(),
|
RemoveExplicitTypeArgumentsProcessing(),
|
||||||
|
RemoveJavaStreamsCollectCallTypeArgumentsProcessing(),
|
||||||
generalInspectionBasedProcessing(RedundantCompanionReferenceInspection()),
|
generalInspectionBasedProcessing(RedundantCompanionReferenceInspection()),
|
||||||
generalInspectionBasedProcessing(ExplicitThisInspection()),
|
generalInspectionBasedProcessing(ExplicitThisInspection()),
|
||||||
intentionBasedProcessing(RemoveEmptyClassBodyIntention())
|
intentionBasedProcessing(RemoveEmptyClassBodyIntention())
|
||||||
@@ -234,7 +235,10 @@ private val cleaningUpDiagnosticBasedPostProcessingGroup =
|
|||||||
private val processings: List<NamedPostProcessingGroup> = listOf(
|
private val processings: List<NamedPostProcessingGroup> = listOf(
|
||||||
NamedPostProcessingGroup(
|
NamedPostProcessingGroup(
|
||||||
"Inferring declarations nullability",
|
"Inferring declarations nullability",
|
||||||
listOf(nullabilityProcessing)
|
listOf(
|
||||||
|
nullabilityProcessing,
|
||||||
|
clearUndefinedLabelsProcessing
|
||||||
|
)
|
||||||
),
|
),
|
||||||
NamedPostProcessingGroup(
|
NamedPostProcessingGroup(
|
||||||
"Formatting code",
|
"Formatting code",
|
||||||
|
|||||||
+22
-3
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.nj2k.postProcessing.processings
|
package org.jetbrains.kotlin.nj2k.postProcessing.processings
|
||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiMethod
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
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.psi.psiUtil.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.types.isNullable
|
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.cast
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
@@ -57,8 +57,7 @@ class RemoveExplicitPropertyTypeProcessing : ApplicabilityBasedInspectionLikePro
|
|||||||
val withoutExpectedType =
|
val withoutExpectedType =
|
||||||
initializer.analyzeInContext(initializer.getResolutionScope()).getType(initializer) ?: return false
|
initializer.analyzeInContext(initializer.getResolutionScope()).getType(initializer) ?: return false
|
||||||
val descriptor = element.resolveToDescriptorIfAny() as? CallableDescriptor ?: return false
|
val descriptor = element.resolveToDescriptorIfAny() as? CallableDescriptor ?: return false
|
||||||
return if (element.isVar) withoutExpectedType == descriptor.returnType
|
return withoutExpectedType == descriptor.returnType
|
||||||
else withoutExpectedType.makeNotNullable() == descriptor.returnType?.makeNotNullable()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun apply(element: KtProperty) {
|
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 {
|
class RemoveRedundantOverrideVisibilityProcessing : InspectionLikeProcessing {
|
||||||
override val writeActionNeeded = true
|
override val writeActionNeeded = true
|
||||||
|
|
||||||
|
|||||||
+59
-14
@@ -6,23 +6,33 @@
|
|||||||
package org.jetbrains.kotlin.nj2k.postProcessing.processings
|
package org.jetbrains.kotlin.nj2k.postProcessing.processings
|
||||||
|
|
||||||
import com.intellij.codeInsight.actions.OptimizeImportsProcessor
|
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 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.ShortenReferences
|
||||||
import org.jetbrains.kotlin.idea.core.util.range
|
import org.jetbrains.kotlin.idea.core.util.range
|
||||||
import org.jetbrains.kotlin.idea.formatter.commitAndUnblockDocument
|
import org.jetbrains.kotlin.idea.formatter.commitAndUnblockDocument
|
||||||
import org.jetbrains.kotlin.nj2k.nullabilityAnalysis.AnalysisScope
|
import org.jetbrains.kotlin.nj2k.asLabel
|
||||||
import org.jetbrains.kotlin.nj2k.nullabilityAnalysis.NullabilityAnalysisFacade
|
import org.jetbrains.kotlin.nj2k.inference.common.BoundTypeCalculatorImpl
|
||||||
import org.jetbrains.kotlin.nj2k.nullabilityAnalysis.nullabilityByUndefinedNullabilityComment
|
import org.jetbrains.kotlin.nj2k.inference.common.ByInfoSuperFunctionsProvider
|
||||||
import org.jetbrains.kotlin.nj2k.nullabilityAnalysis.prepareTypeElementByMakingAllTypesNullableConsideringNullabilityComment
|
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.nj2k.postProcessing.postProcessing
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtImportDirective
|
|
||||||
import org.jetbrains.kotlin.psi.KtImportList
|
|
||||||
import org.jetbrains.kotlin.psi.KtPackageDirective
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
|
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
|
||||||
|
|
||||||
val formatCodeProcessing =
|
val formatCodeProcessing =
|
||||||
postProcessing { file, rangeMarker, _ ->
|
postProcessing { file, rangeMarker, _ ->
|
||||||
|
file.commitAndUnblockDocument()
|
||||||
val codeStyleManager = CodeStyleManager.getInstance(file.project)
|
val codeStyleManager = CodeStyleManager.getInstance(file.project)
|
||||||
if (rangeMarker != null) {
|
if (rangeMarker != null) {
|
||||||
if (rangeMarker.isValid) {
|
if (rangeMarker.isValid) {
|
||||||
@@ -35,14 +45,49 @@ val formatCodeProcessing =
|
|||||||
|
|
||||||
val nullabilityProcessing =
|
val nullabilityProcessing =
|
||||||
postProcessing { file, rangeMarker, converterContext ->
|
postProcessing { file, rangeMarker, converterContext ->
|
||||||
NullabilityAnalysisFacade(
|
val resolutionFacade = file.getResolutionFacade()
|
||||||
converterContext,
|
val inferenceFacade = InferenceFacade(
|
||||||
getTypeElementNullability = { nullabilityByUndefinedNullabilityComment(it, converterContext) },
|
NullabilityContextCollector(resolutionFacade, converterContext),
|
||||||
prepareTypeElement = { prepareTypeElementByMakingAllTypesNullableConsideringNullabilityComment(it, converterContext) },
|
ConstraintsCollectorAggregator(
|
||||||
debugPrint = false
|
resolutionFacade,
|
||||||
).fixNullability(AnalysisScope(file, rangeMarker))
|
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 =
|
val shortenReferencesProcessing =
|
||||||
postProcessing { file, rangeMarker, _ ->
|
postProcessing { file, rangeMarker, _ ->
|
||||||
if (rangeMarker != null) {
|
if (rangeMarker != null) {
|
||||||
|
|||||||
@@ -1,17 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2017 JetBrains s.r.o.
|
* 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.
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.nj2k
|
package org.jetbrains.kotlin.nj2k
|
||||||
@@ -725,7 +714,9 @@ class NewCodeBuilder(context: NewJ2kConverterContext) {
|
|||||||
|
|
||||||
override fun visitArrayAccessExpressionRaw(arrayAccessExpression: JKArrayAccessExpression) {
|
override fun visitArrayAccessExpressionRaw(arrayAccessExpression: JKArrayAccessExpression) {
|
||||||
arrayAccessExpression.expression.accept(this)
|
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) {
|
override fun visitPackageAccessExpressionRaw(packageAccessExpression: JKPackageAccessExpression) {
|
||||||
@@ -946,9 +937,7 @@ private class JKPrinter(
|
|||||||
|
|
||||||
fun renderType(type: JKType, owner: JKTreeElement?) {
|
fun renderType(type: JKType, owner: JKTreeElement?) {
|
||||||
if (type is JKNoTypeImpl) return
|
if (type is JKNoTypeImpl) return
|
||||||
elementInfoStorage.getOrCreateInfoForElement(type).let {
|
print(elementInfoStorage.getOrCreateInfoForElement(type).render())
|
||||||
print(it.render())
|
|
||||||
}
|
|
||||||
when (type) {
|
when (type) {
|
||||||
is JKClassType -> {
|
is JKClassType -> {
|
||||||
renderClassSymbol(type.classReference, owner)
|
renderClassSymbol(type.classReference, owner)
|
||||||
@@ -975,6 +964,12 @@ private class JKPrinter(
|
|||||||
if (type.nullability == Nullability.Nullable) {
|
if (type.nullability == Nullability.Nullable) {
|
||||||
printWithNoIndent("?")
|
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?) {
|
fun renderClassSymbol(classSymbol: JKClassSymbol, owner: JKTreeElement?) {
|
||||||
|
|||||||
@@ -36,14 +36,14 @@ class AddElementsInfoConversion(private val context: NewJ2kConverterContext) : R
|
|||||||
val psiMethod = function.psi<PsiMethod>() ?: return
|
val psiMethod = function.psi<PsiMethod>() ?: return
|
||||||
val descriptor = psiMethod.getJavaMethodDescriptor() ?: return
|
val descriptor = psiMethod.getJavaMethodDescriptor() ?: return
|
||||||
val superDescriptorsInfo =
|
val superDescriptorsInfo =
|
||||||
descriptor.overriddenDescriptors.map { superDescriptor ->
|
descriptor.overriddenDescriptors.mapNotNull { superDescriptor ->
|
||||||
val psi = superDescriptor.findPsi() ?: return@map ExternalSuperFunctionInfo(descriptor)
|
val superPsi = superDescriptor.original.findPsi()
|
||||||
when (val symbol = context.symbolProvider.symbolsByPsi[psi]) {
|
when (val symbol = context.symbolProvider.symbolsByPsi[superPsi]) {
|
||||||
is JKUniverseMethodSymbol ->
|
is JKUniverseMethodSymbol ->
|
||||||
InternalSuperFunctionInfo(
|
InternalSuperFunctionInfo(
|
||||||
context.elementsInfoStorage.getOrCreateInfoForElement(symbol.target)
|
context.elementsInfoStorage.getOrCreateInfoForElement(symbol.target)
|
||||||
)
|
)
|
||||||
else -> ExternalSuperFunctionInfo(descriptor)
|
else -> ExternalSuperFunctionInfo(superDescriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
context.elementsInfoStorage.addEntry(function, FunctionInfo(descriptor, superDescriptorsInfo))
|
context.elementsInfoStorage.addEntry(function, FunctionInfo(descriptor, superDescriptorsInfo))
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
class A {
|
class A {
|
||||||
fun someOther() = false
|
fun someOther() = false
|
||||||
|
|
||||||
private fun formatElement(element: PsiElement): String {
|
private fun formatElement(element: PsiElement): String? {
|
||||||
var element: PsiElement = element
|
var element: PsiElement = element
|
||||||
element = JetPsiUtil.ascendIfPropertyAccessor(element)
|
element = JetPsiUtil.ascendIfPropertyAccessor(element)
|
||||||
if (element is JetNamedFunction || element is JetProperty) {
|
if (element is JetNamedFunction || element is JetProperty) {
|
||||||
@@ -28,9 +28,9 @@ class A {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getSelected(): ArrayList<UsageInfo> {
|
fun getSelected(): ArrayList<UsageInfo> {
|
||||||
val result: ArrayList<UsageInfo> = ArrayList<UsageInfo>()
|
val result: ArrayList<UsageInfo> = ArrayList<UsageInfo?>()
|
||||||
for (i in 0 until myChecked.length) {
|
for (i in 0 until myChecked.length) {
|
||||||
if (myChecked[i]) {
|
if (myChecked.get(i)) {
|
||||||
result.add(myOverridingMethods.get(i))
|
result.add(myOverridingMethods.get(i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ import java.util.ArrayList
|
|||||||
|
|
||||||
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
bar(ArrayList<String>())
|
bar(ArrayList<String?>())
|
||||||
}
|
}
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
|
|
||||||
|
|
||||||
fun foo() = ArrayList<String>()
|
fun foo() = ArrayList<String?>()
|
||||||
@@ -4,7 +4,7 @@ import java.util.UUID
|
|||||||
|
|
||||||
|
|
||||||
class ExampleClass {
|
class ExampleClass {
|
||||||
override fun toString(): String {
|
override fun toString(): String? {
|
||||||
return UUID.randomUUID().toString()
|
return UUID.randomUUID().toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ import java.util.HashMap
|
|||||||
class Target {
|
class Target {
|
||||||
var listOfPlatformType: List<String> = ArrayList()
|
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()
|
var hashMapOfNotImported: Map<ToBeImportedJava, ToBeImportedKotlin> = HashMap()
|
||||||
@@ -18,11 +18,11 @@ class Target {
|
|||||||
|
|
||||||
internal fun acceptJavaClass(tbi: ToBeImportedJava?) {}
|
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() {
|
internal fun workWithStatics() {
|
||||||
|
|||||||
+1
-1
@@ -3,5 +3,5 @@ import java.util.ArrayList
|
|||||||
|
|
||||||
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
bar(/*comment*/ArrayList<String>())
|
bar(/*comment*/ArrayList<String?>())
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@ import java.util.ArrayList
|
|||||||
|
|
||||||
class JavaClass {
|
class JavaClass {
|
||||||
internal fun foo(file: File?, target: List<String?>?) {
|
internal fun foo(file: File?, target: List<String?>?) {
|
||||||
val list = ArrayList<String?>()
|
val list = ArrayList<String>()
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
list.add(file.name)
|
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