FIR IDE: introduce HLSpecifyExplicitTypeForCallableDeclarationIntention and HLRedundantUnitReturnTypeInspection
This commit is contained in:
@@ -100,12 +100,10 @@ import org.jetbrains.kotlin.idea.highlighter.*
|
||||
import org.jetbrains.kotlin.idea.imports.AbstractJsOptimizeImportsTest
|
||||
import org.jetbrains.kotlin.idea.imports.AbstractJvmOptimizeImportsTest
|
||||
import org.jetbrains.kotlin.idea.index.AbstractKotlinTypeAliasByExpansionShortNameIndexTest
|
||||
import org.jetbrains.kotlin.idea.inspections.AbstractHLInspectionTest
|
||||
import org.jetbrains.kotlin.idea.inspections.AbstractLocalInspectionTest
|
||||
import org.jetbrains.kotlin.idea.inspections.AbstractMultiFileLocalInspectionTest
|
||||
import org.jetbrains.kotlin.idea.intentions.AbstractConcatenatedStringGeneratorTest
|
||||
import org.jetbrains.kotlin.idea.intentions.AbstractIntentionTest
|
||||
import org.jetbrains.kotlin.idea.intentions.AbstractIntentionTest2
|
||||
import org.jetbrains.kotlin.idea.intentions.AbstractMultiFileIntentionTest
|
||||
import org.jetbrains.kotlin.idea.intentions.*
|
||||
import org.jetbrains.kotlin.idea.intentions.declarations.AbstractJoinLinesTest
|
||||
import org.jetbrains.kotlin.idea.internal.AbstractBytecodeToolWindowTest
|
||||
import org.jetbrains.kotlin.idea.kdoc.AbstractKDocHighlightingTest
|
||||
@@ -1108,6 +1106,16 @@ fun main(args: Array<String>) {
|
||||
model("quickfix/modifiers", pattern = pattern, filenameStartsLowerCase = true, recursive = false)
|
||||
model("quickfix/override/typeMismatchOnOverride", pattern = pattern, filenameStartsLowerCase = true, recursive = false)
|
||||
}
|
||||
|
||||
testClass<AbstractHLInspectionTest> {
|
||||
val pattern = "^(inspections\\.test)$"
|
||||
model("inspections/redundantUnitReturnType", pattern = pattern, singleClass = true)
|
||||
}
|
||||
|
||||
testClass<AbstractHLIntentionTest> {
|
||||
val pattern = "^([\\w\\-_]+)\\.(kt|kts)$"
|
||||
model("intentions/specifyTypeExplicitly", pattern = pattern)
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("idea/idea-fir/tests", "idea/idea-completion/testData") {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.fir.applicators
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.idea.fir.api.applicator.applicabilityTarget
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
|
||||
object ApplicabilityRanges {
|
||||
val SELF = applicabilityTarget<PsiElement> { it }
|
||||
|
||||
val CALLABLE_RETURN_TYPE = applicabilityTarget<KtCallableDeclaration> { decalration ->
|
||||
decalration.typeReference?.typeElement
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.fir.applicators
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.fir.api.applicator.HLApplicatorInput
|
||||
import org.jetbrains.kotlin.idea.fir.api.applicator.applicator
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtTypeRendererOptions
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.isUnit
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
|
||||
object CallableReturnTypeUpdaterApplicator {
|
||||
val applicator = applicator<KtCallableDeclaration, Type> {
|
||||
familyName(KotlinBundle.message("fix.change.return.type.family"))
|
||||
|
||||
applyTo { declaration, type, project ->
|
||||
val newTypeRef = if (!declaration.isProcedure(type)) {
|
||||
// TODO use longTypeRepresentation and the shorten
|
||||
KtPsiFactory(project ?: declaration.project).createType(type.shortTypeRepresentation)
|
||||
} else null
|
||||
runWriteAction {
|
||||
declaration.typeReference = newTypeRef
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtCallableDeclaration.isProcedure(type: Type) =
|
||||
type.isUnit && this is KtFunction && hasBlockBody()
|
||||
|
||||
class Type(
|
||||
val isUnit: Boolean,
|
||||
val longTypeRepresentation: String,
|
||||
val shortTypeRepresentation: String
|
||||
) : HLApplicatorInput {
|
||||
override fun isValidFor(psi: PsiElement): Boolean = true
|
||||
|
||||
companion object {
|
||||
fun KtAnalysisSession.createByKtType(ktType: KtType): Type = Type(
|
||||
isUnit = ktType.isUnit,
|
||||
longTypeRepresentation = ktType.render(KtTypeRendererOptions.DEFAULT),
|
||||
shortTypeRepresentation = ktType.render(KtTypeRendererOptions.SHORT_NAMES),
|
||||
)
|
||||
|
||||
val UNIT = Type(isUnit = true, longTypeRepresentation = "kotlin.Unit", shortTypeRepresentation = "Unit")
|
||||
}
|
||||
}
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.fir.inspections
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.*
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
|
||||
class AddFunctionReturnTypeIntention :
|
||||
AbstractHighLevelApiBasedIntention<KtNamedFunction, TypeCandidate>(
|
||||
KtNamedFunction::class.java,
|
||||
{ "Specify type explicitly" }
|
||||
) {
|
||||
override fun isApplicableByPsi(element: KtNamedFunction): Boolean =
|
||||
element.typeReference == null && !element.hasBlockBody()
|
||||
|
||||
override fun KtAnalysisSession.analyzeAndGetData(element: KtNamedFunction): TypeCandidate? {
|
||||
val returnType = element.getReturnKtType()
|
||||
val approximated = approximateTypeToUpperDenotable(returnType) ?: return null
|
||||
return TypeCandidate(approximated.render())
|
||||
}
|
||||
|
||||
private tailrec fun approximateTypeToUpperDenotable(type: KtType): KtDenotableType? = when (type) {
|
||||
is KtNonDenotableType -> when (type) {
|
||||
is KtFlexibleType -> approximateTypeToUpperDenotable(type.upperBound)
|
||||
is KtIntersectionType -> null
|
||||
}
|
||||
is KtDenotableType -> type
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
||||
override fun applyTo(element: KtNamedFunction, data: TypeCandidate, editor: Editor?) {
|
||||
element.typeReference = KtPsiFactory(element).createType(data.candidate)
|
||||
}
|
||||
}
|
||||
|
||||
data class TypeCandidate(val candidate: String)
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.fir.inspections.declarations
|
||||
|
||||
import com.intellij.codeInspection.CleanupLocalInspectionTool
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.fir.api.*
|
||||
import org.jetbrains.kotlin.idea.fir.api.applicator.inputProvider
|
||||
import org.jetbrains.kotlin.idea.fir.api.applicator.presentation
|
||||
import org.jetbrains.kotlin.idea.fir.api.applicator.with
|
||||
import org.jetbrains.kotlin.idea.fir.applicators.ApplicabilityRanges
|
||||
import org.jetbrains.kotlin.idea.fir.applicators.CallableReturnTypeUpdaterApplicator
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.isUnit
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
|
||||
internal class HLRedundantUnitReturnTypeInspection :
|
||||
AbstractHLInspection<KtNamedFunction, CallableReturnTypeUpdaterApplicator.Type>(
|
||||
KtNamedFunction::class
|
||||
), CleanupLocalInspectionTool {
|
||||
|
||||
override val applicabilityRange = ApplicabilityRanges.CALLABLE_RETURN_TYPE
|
||||
|
||||
override val applicator = CallableReturnTypeUpdaterApplicator.applicator.with {
|
||||
isApplicableByPsi { callable ->
|
||||
val function = callable as? KtNamedFunction ?: return@isApplicableByPsi false
|
||||
function.hasBlockBody() && function.typeReference != null
|
||||
}
|
||||
familyAndActionName(KotlinBundle.lazyMessage("remove.explicit.type.specification"))
|
||||
}
|
||||
|
||||
override val inputProvider = inputProvider<KtNamedFunction, CallableReturnTypeUpdaterApplicator.Type> { function ->
|
||||
when {
|
||||
function.getFunctionSymbol().annotatedType.type.isUnit -> CallableReturnTypeUpdaterApplicator.Type.UNIT
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override val presentation = presentation<KtNamedFunction> {
|
||||
inspectionText(KotlinBundle.message("redundant.unit.return.type"))
|
||||
highlightType(ProblemHighlightType.LIKE_UNUSED_SYMBOL)
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.fir.intentions.declarations
|
||||
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.fir.api.*
|
||||
import org.jetbrains.kotlin.idea.fir.api.applicator.HLApplicabilityRange
|
||||
import org.jetbrains.kotlin.idea.fir.api.applicator.applicabilityTarget
|
||||
import org.jetbrains.kotlin.idea.fir.api.applicator.inputProvider
|
||||
import org.jetbrains.kotlin.idea.fir.api.applicator.with
|
||||
import org.jetbrains.kotlin.idea.fir.applicators.ApplicabilityRanges
|
||||
import org.jetbrains.kotlin.idea.fir.applicators.CallableReturnTypeUpdaterApplicator
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.*
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
class HLSpecifyExplicitTypeForCallableDeclarationIntention :
|
||||
AbstractHLIntention<KtCallableDeclaration, CallableReturnTypeUpdaterApplicator.Type>(
|
||||
KtCallableDeclaration::class
|
||||
) {
|
||||
override val applicator = CallableReturnTypeUpdaterApplicator.applicator.with {
|
||||
isApplicableByPsi { declaration: KtCallableDeclaration ->
|
||||
if (declaration is KtConstructor<*> || declaration is KtFunctionLiteral) return@isApplicableByPsi false
|
||||
declaration.typeReference == null && (declaration as? KtNamedFunction)?.hasBlockBody() != true
|
||||
}
|
||||
familyAndActionName(KotlinBundle.lazyMessage("specify.return.type.explicitly"))
|
||||
}
|
||||
override val applicabilityRange: HLApplicabilityRange<KtCallableDeclaration> = ApplicabilityRanges.SELF
|
||||
|
||||
override val inputProvider = inputProvider<KtCallableDeclaration, CallableReturnTypeUpdaterApplicator.Type> { declaration ->
|
||||
val returnType = declaration.getReturnKtType()
|
||||
val denotableType = returnType.approximateToPublicDenotable() ?: return@inputProvider null
|
||||
with(CallableReturnTypeUpdaterApplicator.Type) { createByKtType(denotableType) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.inspections
|
||||
|
||||
import org.jetbrains.kotlin.idea.codeInsight.AbstractInspectionTest
|
||||
|
||||
abstract class AbstractHLInspectionTest : AbstractInspectionTest() {
|
||||
override fun isFirPlugin() = true
|
||||
override fun inspectionClassDirective() = "// FIR_INSPECTION_CLASS:"
|
||||
override fun registerGradlPlugin() {}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.inspections;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/testData/inspections/redundantUnitReturnType")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class HLInspectionTestGenerated extends AbstractHLInspectionTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInRedundantUnitReturnType() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentInSingleGeneratedClassWithExcluded(this.getClass(), new File("idea/testData/inspections/redundantUnitReturnType"), Pattern.compile("^(inspections\\.test)$"), null);
|
||||
}
|
||||
|
||||
@TestMetadata("inspectionData/inspections.test")
|
||||
public void testInspectionData_Inspections_test() throws Exception {
|
||||
runTest("idea/testData/inspections/redundantUnitReturnType/inspectionData/inspections.test");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.test.uitls.IgnoreTests
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractHLIntentionTest : AbstractIntentionTest() {
|
||||
override fun intentionFileName() = ".firIntention"
|
||||
override fun isFirPlugin() = true
|
||||
|
||||
override fun doTestFor(mainFile: File, pathToFiles: Map<String, PsiFile>, intentionAction: IntentionAction, fileText: String) {
|
||||
IgnoreTests.runTestIfNotDisabledByFileDirective(mainFile.toPath(), IgnoreTests.DIRECTIVES.IGNORE_FIR) {
|
||||
super.doTestFor(mainFile, pathToFiles, intentionAction, fileText)
|
||||
}
|
||||
}
|
||||
|
||||
override fun checkForErrorsAfter(fileText: String) {}
|
||||
override fun checkForErrorsBefore(fileText: String) {}
|
||||
}
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/testData/intentions/specifyTypeExplicitly")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class HLIntentionTestGenerated extends AbstractHLIntentionTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInSpecifyTypeExplicitly() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/intentions/specifyTypeExplicitly"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("anonymousObject.kt")
|
||||
public void testAnonymousObject() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/anonymousObject.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("backticked.kt")
|
||||
public void testBackticked() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/backticked.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("badCaretPosition.kt")
|
||||
public void testBadCaretPosition() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/badCaretPosition.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("classNameClashing.kt")
|
||||
public void testClassNameClashing() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/classNameClashing.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructor.kt")
|
||||
public void testConstructor() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/constructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("destructuringInLambda.kt")
|
||||
public void testDestructuringInLambda() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/destructuringInLambda.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enumType.kt")
|
||||
public void testEnumType() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/enumType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("forAsExpression.kt")
|
||||
public void testForAsExpression() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/forAsExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionType.kt")
|
||||
public void testFunctionType() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/functionType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("genericClass.kt")
|
||||
public void testGenericClass() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/genericClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("genericClassWithTypeParameters.kt")
|
||||
public void testGenericClassWithTypeParameters() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/genericClassWithTypeParameters.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("genericClassWithTypeParameters2.kt")
|
||||
public void testGenericClassWithTypeParameters2() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/genericClassWithTypeParameters2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("genericFunction.kt")
|
||||
public void testGenericFunction() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/genericFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("innerTypeParameter.kt")
|
||||
public void testInnerTypeParameter() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/innerTypeParameter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("innerTypeParameter2.kt")
|
||||
public void testInnerTypeParameter2() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/innerTypeParameter2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaParam.kt")
|
||||
public void testLambdaParam() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/lambdaParam.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localClass.kt")
|
||||
public void testLocalClass() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/localClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localClassInSecondTypeParameter.kt")
|
||||
public void testLocalClassInSecondTypeParameter() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/localClassInSecondTypeParameter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localClassInSecondTypeParameter2.kt")
|
||||
public void testLocalClassInSecondTypeParameter2() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/localClassInSecondTypeParameter2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localClassInSecondTypeParameter3.kt")
|
||||
public void testLocalClassInSecondTypeParameter3() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/localClassInSecondTypeParameter3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localClassInTypeParameter.kt")
|
||||
public void testLocalClassInTypeParameter() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/localClassInTypeParameter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("loopParameter.kt")
|
||||
public void testLoopParameter() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/loopParameter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("outClass.kt")
|
||||
public void testOutClass() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/outClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("outClass2.kt")
|
||||
public void testOutClass2() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/outClass2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("outClass3.kt")
|
||||
public void testOutClass3() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/outClass3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("overriddenAsNull.kt")
|
||||
public void testOverriddenAsNull() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/overriddenAsNull.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("overrideNotNullFunction.kt")
|
||||
public void testOverrideNotNullFunction() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/overrideNotNullFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("overrideNotNullProperty.kt")
|
||||
public void testOverrideNotNullProperty() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/overrideNotNullProperty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("overrideNullableFunction.kt")
|
||||
public void testOverrideNullableFunction() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/overrideNullableFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("overrideNullableProperty.kt")
|
||||
public void testOverrideNullableProperty() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/overrideNullableProperty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyTypeFromGetter.kt")
|
||||
public void testPropertyTypeFromGetter() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/propertyTypeFromGetter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("publicMember.kt")
|
||||
public void testPublicMember() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/publicMember.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stringRedefined.kt")
|
||||
public void testStringRedefined() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/stringRedefined.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("typeAlreadyProvided.kt")
|
||||
public void testTypeAlreadyProvided() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/typeAlreadyProvided.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unitType.kt")
|
||||
public void testUnitType() throws Exception {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/unitType.kt");
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,8 @@ abstract class KtAnalysisSession(final override val token: ValidityToken) : Vali
|
||||
|
||||
val builtinTypes: KtBuiltinTypes get() = typeProvider.builtinTypes
|
||||
|
||||
fun KtType.approximateToPublicDenotable(): KtType? = typeProvider.approximateToPublicDenotable(this)
|
||||
|
||||
fun KtClassOrObjectSymbol.buildSelfClassType(): KtType = typeProvider.buildSelfClassType(this)
|
||||
|
||||
fun KtElement.getDiagnostics(): Collection<KtDiagnostic> = diagnosticProvider.getDiagnosticsForElement(this)
|
||||
|
||||
+2
@@ -13,6 +13,8 @@ import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
abstract class KtTypeProvider : KtAnalysisSessionComponent() {
|
||||
abstract val builtinTypes: KtBuiltinTypes
|
||||
|
||||
abstract fun approximateToPublicDenotable(type: KtType): KtType?
|
||||
|
||||
abstract fun buildSelfClassType(symbol: KtClassOrObjectSymbol): KtType
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.frontend.api.fir.components
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
|
||||
@@ -17,6 +18,8 @@ import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
|
||||
internal interface KtFirAnalysisSessionComponent {
|
||||
val analysisSession: KtFirAnalysisSession
|
||||
|
||||
val rootModuleSession: FirSession get() = analysisSession.firResolveState.rootModuleSession
|
||||
|
||||
val firSymbolBuilder get() = analysisSession.firSymbolBuilder
|
||||
val firResolveState get() = analysisSession.firResolveState
|
||||
fun ConeKotlinType.asKtType() = analysisSession.firSymbolBuilder.buildKtType(this)
|
||||
|
||||
+14
-1
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.idea.frontend.api.fir.components
|
||||
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
||||
import org.jetbrains.kotlin.idea.frontend.api.ValidityToken
|
||||
@@ -14,9 +15,9 @@ import org.jetbrains.kotlin.idea.frontend.api.components.KtTypeProvider
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.types.KtFirType
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.types.PublicTypeApproximator
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
import org.jetbrains.kotlin.idea.frontend.api.withValidityAssertion
|
||||
|
||||
internal class KtFirTypeProvider(
|
||||
override val analysisSession: KtFirAnalysisSession,
|
||||
@@ -25,6 +26,18 @@ internal class KtFirTypeProvider(
|
||||
override val builtinTypes: KtBuiltinTypes =
|
||||
KtFirBuiltInTypes(analysisSession.firResolveState.rootModuleSession.builtinTypes, analysisSession.firSymbolBuilder, token)
|
||||
|
||||
override fun approximateToPublicDenotable(type: KtType): KtType? {
|
||||
require(type is KtFirType)
|
||||
val coneType = type.coneType
|
||||
val approximatedConeType = PublicTypeApproximator.approximateTypeToPublicDenotable(
|
||||
coneType,
|
||||
rootModuleSession.inferenceComponents.ctx,
|
||||
rootModuleSession
|
||||
)
|
||||
return approximatedConeType?.asKtType()
|
||||
}
|
||||
|
||||
|
||||
override fun buildSelfClassType(symbol: KtClassOrObjectSymbol): KtType {
|
||||
require(symbol is KtFirClassOrObjectSymbol)
|
||||
val type = symbol.firRef.withFir(FirResolvePhase.TYPES) { firClass ->
|
||||
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.frontend.api.fir.types
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.fullyExpandedClass
|
||||
import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
internal object PublicTypeApproximator {
|
||||
fun approximateTypeToPublicDenotable(
|
||||
type: ConeKotlinType,
|
||||
context: ConeInferenceContext,
|
||||
session: FirSession,
|
||||
): ConeKotlinType? = approximate(type, context, session) as ConeKotlinType?
|
||||
|
||||
private fun approximate(
|
||||
type: ConeKotlinType,
|
||||
context: ConeInferenceContext,
|
||||
session: FirSession,
|
||||
): ConeTypeProjection? = when (type) {
|
||||
is ConeFlexibleType -> approximate(type.upperBound, context, session)
|
||||
is ConeCapturedType -> type
|
||||
is ConeDefinitelyNotNullType -> ConeDefinitelyNotNullType(
|
||||
approximate(
|
||||
type.original,
|
||||
context,
|
||||
session,
|
||||
) as ConeKotlinType
|
||||
)
|
||||
is ConeIntegerLiteralType -> type
|
||||
is ConeIntersectionType -> context.commonSuperTypeOrNull(type.intersectedTypes.toList())?.let { approximate(it, context, session) }
|
||||
is ConeLookupTagBasedType -> when (type) {
|
||||
is ConeTypeParameterType -> type
|
||||
is ConeClassErrorType -> null
|
||||
is ConeClassLikeTypeImpl -> approximateClassLikeType(type, context, session)
|
||||
else -> error("Unexpected type ${type::class}")
|
||||
}
|
||||
is ConeStubType -> null
|
||||
}
|
||||
|
||||
private fun approximateClassLikeType(
|
||||
typeImpl: ConeClassLikeTypeImpl,
|
||||
context: ConeInferenceContext,
|
||||
session: FirSession,
|
||||
): ConeTypeProjection? {
|
||||
val regularClass = typeImpl.classOrAnonymousClass(session) ?: return null
|
||||
val type = if (needApproximationAsSuperType(regularClass)) {
|
||||
firstSuperType(regularClass, session) ?: return null
|
||||
} else {
|
||||
typeImpl
|
||||
}
|
||||
|
||||
val typeClassifier = type.classOrAnonymousClass(session) ?: return null
|
||||
|
||||
val typeArguments = approximateTypeParameters(type, typeClassifier, context, session)?.toTypedArray() ?: return null
|
||||
return ConeClassLikeTypeImpl(
|
||||
type.lookupTag,
|
||||
typeArguments,
|
||||
type.isNullable,
|
||||
type.attributes
|
||||
)
|
||||
}
|
||||
|
||||
private fun approximateTypeParameters(
|
||||
type: ConeClassLikeTypeImpl,
|
||||
typeClassifier: FirClass<*>,
|
||||
context: ConeInferenceContext,
|
||||
session: FirSession
|
||||
): List<ConeTypeProjection>? {
|
||||
val result = mutableListOf<ConeTypeProjection>()
|
||||
if (type.typeArguments.size != typeClassifier.typeParameters.size) return null
|
||||
for ((typeArg, typeParam) in type.typeArguments.zip(typeClassifier.typeParameters)) {
|
||||
val variance = (typeParam as? FirTypeParameter)?.variance ?: return null
|
||||
result += approximateTypeProjection(typeArg, context, session, variance) ?: return null
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun firstSuperType(regularClass: FirClass<*>, session: FirSession): ConeClassLikeTypeImpl? {
|
||||
val superTypes = lookupSuperTypes(regularClass, lookupInterfaces = true, deep = false, session, substituteTypes = true)
|
||||
return superTypes.first() as? ConeClassLikeTypeImpl
|
||||
}
|
||||
|
||||
fun ConeClassLikeTypeImpl.classOrAnonymousClass(session: FirSession): FirClass<*>? {
|
||||
val fir = lookupTag.toSymbol(session)?.fir ?: return null
|
||||
if (fir is FirAnonymousObject) return fir
|
||||
else return fir.fullyExpandedClass(session)
|
||||
}
|
||||
|
||||
private fun needApproximationAsSuperType(fir: FirClass<*>) = when (fir) {
|
||||
is FirRegularClass -> fir.isLocal
|
||||
is FirAnonymousObject -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun approximateTypeProjection(
|
||||
typeProjection: ConeTypeProjection,
|
||||
context: ConeInferenceContext,
|
||||
session: FirSession,
|
||||
variance: Variance,
|
||||
): ConeTypeProjection? {
|
||||
val type = when (typeProjection) {
|
||||
ConeStarProjection -> return ConeStarProjection
|
||||
is ConeKotlinTypeProjection -> typeProjection.type
|
||||
else -> error("Unexpected type ${typeProjection::class}")
|
||||
}
|
||||
val newType = when (val new = approximate(type, context, session)) {
|
||||
ConeStarProjection -> return ConeStarProjection
|
||||
is ConeKotlinTypeProjection -> new.type
|
||||
null -> return null
|
||||
else -> error("Unexpected type ${new::class}")
|
||||
}
|
||||
return when (variance) {
|
||||
Variance.INVARIANT -> when {
|
||||
with(context) { newType.typeConstructor().isAnyConstructor() } -> ConeStarProjection
|
||||
AbstractTypeChecker.equalTypes(context, type, newType) -> newType
|
||||
else -> ConeStarProjection
|
||||
}
|
||||
Variance.IN_VARIANCE -> if (AbstractTypeChecker.isSubtypeOf(context, newType, type)) newType else ConeStarProjection
|
||||
Variance.OUT_VARIANCE -> if (AbstractTypeChecker.isSubtypeOf(context, type, newType)) newType else ConeStarProjection
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection reports a redundant <b>Unit</b> return type which can be omitted.
|
||||
</body>
|
||||
</html>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>Write your description here.
|
||||
Start the description with a verb in 3rd person singular, like reports, detects, highlights.
|
||||
In the first sentence, briefly explain what exactly the inspection helps you detect.
|
||||
Make sure the sentence is not very long and complicated.
|
||||
The first sentence must be in a dedicated paragraph separated from the rest of the text. This will make the description easier to read.
|
||||
Make sure the description doesn’t just repeat the inspection title.
|
||||
</p>
|
||||
<!-- tooltip end -->
|
||||
<p>Text after this comment will only be shown in the settings of the inspection.</p>
|
||||
</body>
|
||||
</html>
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>
|
||||
Specify declaration return type explicitly
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.ktQuickFixRegistrar"
|
||||
interface="org.jetbrains.kotlin.idea.quickfix.KtQuickFixRegistrar"
|
||||
interface="org.jetbrains.kotlin.idea.fir.api.fixes.KtQuickFixRegistrar"
|
||||
dynamic="true"/>
|
||||
</extensionPoints>
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<idea-plugin>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.fir.inspections.declarations.HLRedundantUnitReturnTypeInspection"
|
||||
groupPath="Kotlin"
|
||||
groupName="Redundant constructs"
|
||||
enabledByDefault="true"
|
||||
cleanupTool="true"
|
||||
level="WARNING"
|
||||
language="kotlin"
|
||||
key="inspection.redundant.unit.return.type.display.name" bundle="messages.KotlinBundle"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -0,0 +1,8 @@
|
||||
<idea-plugin>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.fir.intentions.declarations.HLSpecifyExplicitTypeForCallableDeclarationIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -37,6 +37,8 @@ The Kotlin FIR plugin provides language support in IntelliJ IDEA and Android Stu
|
||||
<xi:include href="jps.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<xi:include href="idea.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<xi:include href="caches.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<xi:include href="firInspections.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<xi:include href="firIntentions.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
<xi:include href="extensions/ide-frontend-independent.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention adds an explicit type specification for functions.
|
||||
</body>
|
||||
</html>
|
||||
+2
-1
@@ -1 +1,2 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.RedundantUnitReturnTypeInspection
|
||||
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.RedundantUnitReturnTypeInspection
|
||||
// FIR_INSPECTION_CLASS: org.jetbrains.kotlin.idea.fir.inspections.declarations.HLRedundantUnitReturnTypeInspection
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.fir.intentions.declarations.HLSpecifyExplicitTypeForCallableDeclarationIntention
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_FIR
|
||||
class A {
|
||||
private fun foo() <caret>= { object {} }
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_FIR
|
||||
class A {
|
||||
private fun foo(): () -> Any<caret> = { object {} }
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
// IGNORE_FIR
|
||||
class `1`
|
||||
val x<caret> = `1`()
|
||||
@@ -1,2 +1,3 @@
|
||||
// IGNORE_FIR
|
||||
class `1`
|
||||
val x: `1` = `1`()
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_FIR
|
||||
// IS_APPLICABLE: false
|
||||
|
||||
val x = "<caret>"
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_FIR
|
||||
// WITH_RUNTIME
|
||||
|
||||
object Holder {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_FIR
|
||||
// WITH_RUNTIME
|
||||
|
||||
object Holder {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_FIR
|
||||
class A {
|
||||
private fun bar() <caret>= {
|
||||
class Local()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// IGNORE_FIR
|
||||
class A {
|
||||
private fun bar(): () -> Any<caret> = {
|
||||
private fun bar(): () -> Any = {
|
||||
class Local()
|
||||
Local()
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// IGNORE_FIR
|
||||
open class F
|
||||
class TestClass<V, out K>
|
||||
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// IGNORE_FIR
|
||||
open class F
|
||||
class TestClass<V, out K>
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_FIR
|
||||
// WITH_RUNTIME
|
||||
|
||||
import java.util.HashMap
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_FIR
|
||||
// WITH_RUNTIME
|
||||
|
||||
import java.util.HashMap
|
||||
|
||||
@@ -7,3 +7,5 @@ interface I {
|
||||
class Test : I {
|
||||
override fun foo()<caret> = null
|
||||
}
|
||||
|
||||
// IGNORE_FIR
|
||||
|
||||
@@ -7,3 +7,5 @@ interface I {
|
||||
class Test : I {
|
||||
override fun foo(): String? = null
|
||||
}
|
||||
|
||||
// IGNORE_FIR
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_FIR
|
||||
// CHOOSE_NULLABLE_TYPE_IF_EXISTS
|
||||
// WITH_RUNTIME
|
||||
interface Base {
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// IGNORE_FIR
|
||||
// CHOOSE_NULLABLE_TYPE_IF_EXISTS
|
||||
// WITH_RUNTIME
|
||||
interface Base {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_FIR
|
||||
// CHOOSE_NULLABLE_TYPE_IF_EXISTS
|
||||
// WITH_RUNTIME
|
||||
interface Base {
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// IGNORE_FIR
|
||||
// CHOOSE_NULLABLE_TYPE_IF_EXISTS
|
||||
// WITH_RUNTIME
|
||||
interface Base {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_FIR
|
||||
class String {}
|
||||
|
||||
val <caret>x = ""
|
||||
@@ -1,5 +1,6 @@
|
||||
import kotlin.String
|
||||
|
||||
// IGNORE_FIR
|
||||
class String {}
|
||||
|
||||
val x: String = ""
|
||||
@@ -37,13 +37,17 @@ abstract class AbstractInspectionTest : KotlinLightCodeInsightFixtureTestCase()
|
||||
try {
|
||||
super.setUp()
|
||||
EntryPointsManagerBase.getInstance(project).ADDITIONAL_ANNOTATIONS.add(ENTRY_POINT_ANNOTATION)
|
||||
runWriteAction { FileTypeManager.getInstance().associateExtension(GroovyFileType.GROOVY_FILE_TYPE, "gradle") }
|
||||
registerGradlPlugin()
|
||||
} catch (e: Throwable) {
|
||||
TestLoggerFactory.onTestFinished(false)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun registerGradlPlugin() {
|
||||
runWriteAction { FileTypeManager.getInstance().associateExtension(GroovyFileType.GROOVY_FILE_TYPE, "gradle") }
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
EntryPointsManagerBase.getInstance(project).ADDITIONAL_ANNOTATIONS.remove(ENTRY_POINT_ANNOTATION)
|
||||
super.tearDown()
|
||||
@@ -55,11 +59,13 @@ abstract class AbstractInspectionTest : KotlinLightCodeInsightFixtureTestCase()
|
||||
|
||||
protected open val forceUsePackageFolder: Boolean = false //workaround for IDEA-176033
|
||||
|
||||
protected fun doTest(path: String) {
|
||||
protected open fun inspectionClassDirective(): String = "// INSPECTION_CLASS: "
|
||||
|
||||
protected open fun doTest(path: String) {
|
||||
val optionsFile = File(path)
|
||||
val options = FileUtil.loadFile(optionsFile, true)
|
||||
|
||||
val inspectionClass = Class.forName(InTextDirectivesUtils.findStringWithPrefixes(options, "// INSPECTION_CLASS: ")!!)
|
||||
val inspectionClass = Class.forName(InTextDirectivesUtils.findStringWithPrefixes(options, inspectionClassDirective())!!)
|
||||
|
||||
val fixtureClasses = InTextDirectivesUtils.findListWithPrefixes(options, "// FIXTURE_CLASS: ")
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.refactoring.util.CommonRefactoringUtil
|
||||
import com.intellij.testFramework.PlatformTestUtil
|
||||
import junit.framework.ComparisonFailure
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.annotations.NotNull
|
||||
import org.jetbrains.kotlin.formatter.FormatSettingsUtil
|
||||
import org.jetbrains.kotlin.idea.test.*
|
||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||
@@ -109,15 +110,11 @@ abstract class AbstractIntentionTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
val minJavaVersion = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// MIN_JAVA_VERSION: ")
|
||||
if (minJavaVersion != null && !SystemInfo.isJavaVersionAtLeast(minJavaVersion)) return@configureRegistryAndRun
|
||||
|
||||
if (file is KtFile && !InTextDirectivesUtils.isDirectiveDefined(fileText, "// SKIP_ERRORS_BEFORE")) {
|
||||
DirectiveBasedActionUtils.checkForUnexpectedErrors(file as KtFile)
|
||||
}
|
||||
checkForErrorsBefore(fileText)
|
||||
|
||||
doTestFor(mainFile.name, pathToFiles, intentionAction, fileText)
|
||||
doTestFor(mainFile, pathToFiles, intentionAction, fileText)
|
||||
|
||||
if (file is KtFile && !InTextDirectivesUtils.isDirectiveDefined(fileText, "// SKIP_ERRORS_AFTER")) {
|
||||
DirectiveBasedActionUtils.checkForUnexpectedErrors(file as KtFile)
|
||||
}
|
||||
checkForErrorsAfter(fileText)
|
||||
} finally {
|
||||
ConfigLibraryUtil.unconfigureLibrariesByDirective(module, fileText)
|
||||
}
|
||||
@@ -126,6 +123,18 @@ abstract class AbstractIntentionTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun checkForErrorsAfter(fileText: String) {
|
||||
if (file is KtFile && !InTextDirectivesUtils.isDirectiveDefined(fileText, "// SKIP_ERRORS_AFTER")) {
|
||||
DirectiveBasedActionUtils.checkForUnexpectedErrors(file as KtFile)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun checkForErrorsBefore(fileText: String) {
|
||||
if (file is KtFile && !InTextDirectivesUtils.isDirectiveDefined(fileText, "// SKIP_ERRORS_BEFORE")) {
|
||||
DirectiveBasedActionUtils.checkForUnexpectedErrors(file as KtFile)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> computeUnderProgressIndicatorAndWait(compute: () -> T): T {
|
||||
val result = CompletableFuture<T>()
|
||||
val progressIndicator = ProgressIndicatorBase()
|
||||
@@ -143,7 +152,8 @@ abstract class AbstractIntentionTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun doTestFor(mainFilePath: String, pathToFiles: Map<String, PsiFile>, intentionAction: IntentionAction, fileText: String) {
|
||||
protected open fun doTestFor(mainFile: File, pathToFiles: Map<String, PsiFile>, intentionAction: IntentionAction, fileText: String) {
|
||||
val mainFilePath = mainFile.name
|
||||
val isApplicableString = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// ${isApplicableDirectiveName()}: ")
|
||||
val isApplicableExpected = isApplicableString == null || isApplicableString == "true"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user