FIR IDE: add quickfix to change function return type
This commit is contained in:
committed by
teamcityserver
parent
b5994fa3a9
commit
71c5c9f6c5
+1
@@ -418,6 +418,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
val RETURN_TYPE_MISMATCH by error<KtExpression>(PositioningStrategy.WHOLE_ELEMENT) {
|
||||
parameter<ConeKotlinType>("expectedType")
|
||||
parameter<ConeKotlinType>("actualType")
|
||||
parameter<FirSimpleFunction>("targetFunction")
|
||||
}
|
||||
|
||||
val CYCLIC_GENERIC_UPPER_BOUND by error<PsiElement>()
|
||||
|
||||
+2
-1
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirEnumEntry
|
||||
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
@@ -288,7 +289,7 @@ object FirErrors {
|
||||
val REIFIED_TYPE_PARAMETER_NO_INLINE by error0<KtTypeParameter>(SourceElementPositioningStrategies.REIFIED_MODIFIER)
|
||||
val TYPE_PARAMETERS_NOT_ALLOWED by error0<KtDeclaration>(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST)
|
||||
val TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER by error0<KtTypeParameter>()
|
||||
val RETURN_TYPE_MISMATCH by error2<KtExpression, ConeKotlinType, ConeKotlinType>(SourceElementPositioningStrategies.WHOLE_ELEMENT)
|
||||
val RETURN_TYPE_MISMATCH by error3<KtExpression, ConeKotlinType, ConeKotlinType, FirSimpleFunction>(SourceElementPositioningStrategies.WHOLE_ELEMENT)
|
||||
val CYCLIC_GENERIC_UPPER_BOUND by error0<PsiElement>()
|
||||
val DEPRECATED_TYPE_PARAMETER_SYNTAX by error0<KtDeclaration>(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST)
|
||||
val MISPLACED_TYPE_PARAMETER_CONSTRAINTS by warning0<KtTypeParameter>()
|
||||
|
||||
+4
-1
@@ -36,7 +36,10 @@ object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() {
|
||||
if (resultExpression.isNullLiteral && functionReturnType.nullability == ConeNullability.NOT_NULL) {
|
||||
reporter.reportOn(resultExpression.source, NULL_FOR_NONNULL_TYPE, context)
|
||||
} else {
|
||||
reporter.report(RETURN_TYPE_MISMATCH.on(returnExpressionSource, functionReturnType, returnExpressionType), context)
|
||||
reporter.report(
|
||||
RETURN_TYPE_MISMATCH.on(returnExpressionSource, functionReturnType, returnExpressionType, targetElement),
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -667,7 +667,7 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
||||
|
||||
map.put(TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER, "Type parameter of a property must be used in its receiver type")
|
||||
|
||||
map.put(RETURN_TYPE_MISMATCH, "Return type mismatch: expected {0}, actual {1}", RENDER_TYPE, RENDER_TYPE)
|
||||
map.put(RETURN_TYPE_MISMATCH, "Return type mismatch: expected {0}, actual {1}", RENDER_TYPE, RENDER_TYPE, NOT_RENDERED)
|
||||
|
||||
map.put(CYCLIC_GENERIC_UPPER_BOUND, "Type parameter has cyclic upper bounds")
|
||||
|
||||
|
||||
+2
@@ -1148,6 +1148,8 @@ fun main(args: Array<String>) {
|
||||
model("quickfix/variables/changeMutability", pattern = pattern, filenameStartsLowerCase = true)
|
||||
model("quickfix/when", pattern = pattern, filenameStartsLowerCase = true)
|
||||
model("quickfix/wrapWithSafeLetCall", pattern = pattern, filenameStartsLowerCase = true)
|
||||
model("quickfix/typeMismatch/componentFunctionReturnTypeMismatch", pattern = pattern, filenameStartsLowerCase = true)
|
||||
model("quickfix/typeMismatch/typeMismatchOnReturnedExpression", pattern = pattern, filenameStartsLowerCase = true)
|
||||
}
|
||||
|
||||
testClass<AbstractHighLevelQuickFixMultiFileTest> {
|
||||
|
||||
@@ -12,8 +12,6 @@ import org.jetbrains.kotlin.idea.fir.api.fixes.KtQuickFixesListBuilder
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic
|
||||
import org.jetbrains.kotlin.idea.quickfix.fixes.*
|
||||
import org.jetbrains.kotlin.idea.quickfix.fixes.InitializePropertyQuickFixFactory
|
||||
import org.jetbrains.kotlin.idea.quickfix.fixes.ChangeTypeQuickFix
|
||||
import org.jetbrains.kotlin.idea.quickfix.fixes.ReplaceCallFixFactories
|
||||
|
||||
class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
|
||||
private val modifiers = KtQuickFixesListBuilder.registerPsiQuickFix {
|
||||
@@ -78,9 +76,9 @@ class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
|
||||
}
|
||||
|
||||
private val overrides = KtQuickFixesListBuilder.registerPsiQuickFix {
|
||||
registerApplicator(ChangeTypeQuickFix.changeFunctionReturnTypeOnOverride)
|
||||
registerApplicator(ChangeTypeQuickFix.changePropertyReturnTypeOnOverride)
|
||||
registerApplicator(ChangeTypeQuickFix.changeVariableReturnTypeOnOverride)
|
||||
registerApplicator(ChangeTypeQuickFixFactories.changeFunctionReturnTypeOnOverride)
|
||||
registerApplicator(ChangeTypeQuickFixFactories.changePropertyReturnTypeOnOverride)
|
||||
registerApplicator(ChangeTypeQuickFixFactories.changeVariableReturnTypeOnOverride)
|
||||
registerApplicator(MemberNotImplementedQuickfixFactories.abstractMemberNotImplemented)
|
||||
registerApplicator(MemberNotImplementedQuickfixFactories.abstractClassMemberNotImplemented)
|
||||
registerApplicator(MemberNotImplementedQuickfixFactories.manyInterfacesMemberNotImplemented)
|
||||
@@ -120,6 +118,11 @@ class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
|
||||
registerApplicator(WrapWithSafeLetCallFixFactories.forArgumentTypeMismatch)
|
||||
}
|
||||
|
||||
private val returnTypes = KtQuickFixesListBuilder.registerPsiQuickFix {
|
||||
registerApplicator(ChangeTypeQuickFixFactories.componentFunctionReturnTypeMismatch)
|
||||
registerApplicator(ChangeTypeQuickFixFactories.returnTypeMismatch)
|
||||
}
|
||||
|
||||
override val list: KtQuickFixesList = KtQuickFixesList.createCombined(
|
||||
modifiers,
|
||||
propertyInitialization,
|
||||
@@ -127,5 +130,6 @@ class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
|
||||
imports,
|
||||
mutability,
|
||||
expressions,
|
||||
returnTypes
|
||||
)
|
||||
}
|
||||
|
||||
+87
-17
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.quickfix.fixes
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.parentOfType
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.api.applicator.HLApplicatorInput
|
||||
import org.jetbrains.kotlin.idea.api.applicator.applicator
|
||||
import org.jetbrains.kotlin.idea.fir.api.fixes.HLApplicatorTargetWithInput
|
||||
@@ -16,17 +17,24 @@ import org.jetbrains.kotlin.idea.fir.applicators.CallableReturnTypeUpdaterApplic
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.diagnostics.KtDiagnosticWithPsi
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtPropertySymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithMembers
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.psiSafe
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtClassType
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
import org.jetbrains.kotlin.idea.quickfix.ChangeCallableReturnTypeFix
|
||||
import org.jetbrains.kotlin.idea.quickfix.ChangeTypeFixUtils
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||
|
||||
object ChangeTypeQuickFix {
|
||||
object ChangeTypeQuickFixFactories {
|
||||
val applicator = applicator<KtCallableDeclaration, Input> {
|
||||
familyName(CallableReturnTypeUpdaterApplicator.applicator.getFamilyName())
|
||||
|
||||
actionName { declaration, (updateBaseFunction, type) ->
|
||||
val presentation = getPresentation(updateBaseFunction, declaration)
|
||||
actionName { declaration, (targetType, type) ->
|
||||
val presentation = getPresentation(targetType, declaration)
|
||||
getActionName(declaration, presentation, type)
|
||||
}
|
||||
|
||||
@@ -39,7 +47,7 @@ object ChangeTypeQuickFix {
|
||||
declaration: KtCallableDeclaration,
|
||||
presentation: String?,
|
||||
type: CallableReturnTypeUpdaterApplicator.Type
|
||||
) = ChangeCallableReturnTypeFix.StringPresentation.getTextForQuickFix(
|
||||
) = ChangeTypeFixUtils.getTextForQuickFix(
|
||||
declaration,
|
||||
presentation,
|
||||
type.isUnit,
|
||||
@@ -47,21 +55,48 @@ object ChangeTypeQuickFix {
|
||||
)
|
||||
|
||||
private fun getPresentation(
|
||||
updateBaseFunction: Boolean,
|
||||
targetType: TargetType,
|
||||
declaration: KtCallableDeclaration
|
||||
) = when {
|
||||
updateBaseFunction -> {
|
||||
val containerName = declaration.parentOfType<KtNamedDeclaration>()?.nameAsName?.takeUnless { it.isSpecial }
|
||||
ChangeCallableReturnTypeFix.StringPresentation.baseFunctionOrConstructorParameterPresentation(
|
||||
declaration,
|
||||
containerName
|
||||
): String? {
|
||||
return when (targetType) {
|
||||
TargetType.CURRENT_DECLARATION -> null
|
||||
TargetType.BASE_DECLARATION -> KotlinBundle.message(
|
||||
"fix.change.return.type.presentation.base",
|
||||
declaration.presentationForQuickfix ?: return null
|
||||
)
|
||||
TargetType.ENCLOSING_DECLARATION -> KotlinBundle.message(
|
||||
"fix.change.return.type.presentation.enclosing",
|
||||
declaration.presentationForQuickfix ?: return KotlinBundle.message("fix.change.return.type.presentation.enclosing.function")
|
||||
)
|
||||
TargetType.CALLED_FUNCTION -> {
|
||||
val presentation =
|
||||
declaration.presentationForQuickfix
|
||||
?: return KotlinBundle.message("fix.change.return.type.presentation.called.function")
|
||||
when (declaration) {
|
||||
is KtParameter -> KotlinBundle.message("fix.change.return.type.presentation.accessed", presentation)
|
||||
else -> KotlinBundle.message("fix.change.return.type.presentation.called", presentation)
|
||||
}
|
||||
}
|
||||
TargetType.VARIABLE -> return "'${declaration.name}'"
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
private val KtCallableDeclaration.presentationForQuickfix: String?
|
||||
get() {
|
||||
val containerName = parentOfType<KtNamedDeclaration>()?.nameAsName?.takeUnless { it.isSpecial }
|
||||
return ChangeTypeFixUtils.functionOrConstructorParameterPresentation(this, containerName?.asString())
|
||||
}
|
||||
|
||||
enum class TargetType {
|
||||
CURRENT_DECLARATION,
|
||||
BASE_DECLARATION,
|
||||
ENCLOSING_DECLARATION,
|
||||
CALLED_FUNCTION,
|
||||
VARIABLE,
|
||||
}
|
||||
|
||||
data class Input(
|
||||
val updateBaseFunction: Boolean,
|
||||
val targetType: TargetType,
|
||||
val type: CallableReturnTypeUpdaterApplicator.Type
|
||||
) : HLApplicatorInput {
|
||||
override fun isValidFor(psi: PsiElement): Boolean = type.isValidFor(psi)
|
||||
@@ -82,6 +117,31 @@ object ChangeTypeQuickFix {
|
||||
it.variable as? KtPropertySymbol
|
||||
}
|
||||
|
||||
val returnTypeMismatch =
|
||||
diagnosticFixFactory<KtFirDiagnostic.ReturnTypeMismatch, KtCallableDeclaration, Input>(applicator) { diagnostic ->
|
||||
val function = diagnostic.targetFunction.psi as? KtCallableDeclaration ?: return@diagnosticFixFactory emptyList()
|
||||
listOf(function withInput Input(TargetType.ENCLOSING_DECLARATION, createTypeInfo(diagnostic.actualType)))
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
val componentFunctionReturnTypeMismatch =
|
||||
diagnosticFixFactory<KtFirDiagnostic.ComponentFunctionReturnTypeMismatch, KtCallableDeclaration, Input>(applicator) { diagnostic ->
|
||||
val entryWithWrongType =
|
||||
getDestructuringDeclarationEntryThatTypeMismatchComponentFunction(
|
||||
diagnostic.componentFunctionName,
|
||||
diagnostic.psi
|
||||
)
|
||||
?: return@diagnosticFixFactory emptyList()
|
||||
buildList<HLApplicatorTargetWithInput<KtCallableDeclaration, Input>> {
|
||||
add(entryWithWrongType withInput Input(TargetType.VARIABLE, createTypeInfo(diagnostic.destructingType)))
|
||||
val classSymbol = (diagnostic.psi.getKtType() as? KtClassType)?.classSymbol as? KtSymbolWithMembers ?: return@buildList
|
||||
val componentFunction = classSymbol.getMemberScope()
|
||||
.getCallableSymbols { it == diagnostic.componentFunctionName }
|
||||
.firstOrNull()?.psi as? KtCallableDeclaration
|
||||
?: return@buildList
|
||||
add(componentFunction withInput Input(TargetType.CALLED_FUNCTION, createTypeInfo(diagnostic.expectedType)))
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <DIAGNOSTIC : KtDiagnosticWithPsi<KtNamedDeclaration>> changeReturnTypeOnOverride(
|
||||
crossinline getCallableSymbol: (DIAGNOSTIC) -> KtCallableSymbol?
|
||||
@@ -100,7 +160,7 @@ object ChangeTypeQuickFix {
|
||||
): HLApplicatorTargetWithInput<PSI, Input>? {
|
||||
val lowerSuperType = findLowerBoundOfOverriddenCallablesReturnTypes(callable) ?: return null
|
||||
val changeToTypeInfo = createTypeInfo(lowerSuperType)
|
||||
return declaration withInput Input(updateBaseFunction = false, changeToTypeInfo)
|
||||
return declaration withInput Input(TargetType.CURRENT_DECLARATION, changeToTypeInfo)
|
||||
}
|
||||
|
||||
private fun KtAnalysisSession.createChangeOverriddenFunctionQuickFix(
|
||||
@@ -110,7 +170,8 @@ object ChangeTypeQuickFix {
|
||||
val singleNonMatchingOverriddenFunction = findSingleNonMatchingOverriddenFunction(callable, type) ?: return null
|
||||
val singleMatchingOverriddenFunctionPsi = singleNonMatchingOverriddenFunction.psiSafe<KtCallableDeclaration>() ?: return null
|
||||
val changeToTypeInfo = createTypeInfo(type)
|
||||
return singleMatchingOverriddenFunctionPsi withInput Input(updateBaseFunction = true, changeToTypeInfo)
|
||||
if (!singleMatchingOverriddenFunctionPsi.isWritable) return null
|
||||
return singleMatchingOverriddenFunctionPsi withInput Input(TargetType.BASE_DECLARATION, changeToTypeInfo)
|
||||
}
|
||||
|
||||
private fun KtAnalysisSession.findSingleNonMatchingOverriddenFunction(
|
||||
@@ -143,4 +204,13 @@ object ChangeTypeQuickFix {
|
||||
}
|
||||
return lowestType
|
||||
}
|
||||
|
||||
private fun getDestructuringDeclarationEntryThatTypeMismatchComponentFunction(
|
||||
componentName: Name,
|
||||
rhsExpression: KtExpression
|
||||
): KtDestructuringDeclarationEntry? {
|
||||
val componentIndex = componentName.asString().removePrefix("component").toIntOrNull() ?: return null
|
||||
val destructuringDeclaration = rhsExpression.getParentOfType<KtDestructuringDeclaration>(strict = true) ?: return null
|
||||
return destructuringDeclaration.entries[componentIndex - 1]
|
||||
}
|
||||
}
|
||||
Generated
+136
@@ -1813,4 +1813,140 @@ public class HighLevelQuickFixTestGenerated extends AbstractHighLevelQuickFixTes
|
||||
runTest("idea/testData/quickfix/wrapWithSafeLetCall/wrapAllNonNullablePositions6.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ComponentFunctionReturnTypeMismatch extends AbstractHighLevelQuickFixTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInComponentFunctionReturnTypeMismatch() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("componentFunctionReturnTypeMismatch1.kt")
|
||||
public void testComponentFunctionReturnTypeMismatch1() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/componentFunctionReturnTypeMismatch1.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("componentFunctionReturnTypeMismatch2.kt")
|
||||
public void testComponentFunctionReturnTypeMismatch2() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/componentFunctionReturnTypeMismatch2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("componentFunctionReturnTypeMismatch3.kt")
|
||||
public void testComponentFunctionReturnTypeMismatch3() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/componentFunctionReturnTypeMismatch3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("componentFunctionReturnTypeMismatch4.kt")
|
||||
public void testComponentFunctionReturnTypeMismatch4() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/componentFunctionReturnTypeMismatch4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("componentFunctionReturnTypeMismatch5.kt")
|
||||
public void testComponentFunctionReturnTypeMismatch5() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/componentFunctionReturnTypeMismatch5.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("dataClass.kt")
|
||||
public void testDataClass() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/dataClass.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class TypeMismatchOnReturnedExpression extends AbstractHighLevelQuickFixTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInTypeMismatchOnReturnedExpression() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("assignmentTypeMismatch.kt")
|
||||
public void testAssignmentTypeMismatch() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/assignmentTypeMismatch.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("changeFunctionReturnTypeToFunctionType.kt")
|
||||
public void testChangeFunctionReturnTypeToFunctionType() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/changeFunctionReturnTypeToFunctionType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("changeFunctionReturnTypeToMatchReturnTypeOfReturnedLiteral.kt")
|
||||
public void testChangeFunctionReturnTypeToMatchReturnTypeOfReturnedLiteral() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/changeFunctionReturnTypeToMatchReturnTypeOfReturnedLiteral.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("dontChangeFunctionReturnTypeToErrorType.kt")
|
||||
public void testDontChangeFunctionReturnTypeToErrorType() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/dontChangeFunctionReturnTypeToErrorType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("literalPropertyWithGetter.kt")
|
||||
public void testLiteralPropertyWithGetter() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/literalPropertyWithGetter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multiFakeOverride.kt")
|
||||
public void testMultiFakeOverride() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/multiFakeOverride.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multiFakeOverrideForOperatorConvention.kt")
|
||||
public void testMultiFakeOverrideForOperatorConvention() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/multiFakeOverrideForOperatorConvention.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nonLocalReturnRuntime.kt")
|
||||
public void testNonLocalReturnRuntime() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/nonLocalReturnRuntime.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nonLocalReturnWithLabelRuntime.kt")
|
||||
public void testNonLocalReturnWithLabelRuntime() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/nonLocalReturnWithLabelRuntime.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notApplicableToConstructor.kt")
|
||||
public void testNotApplicableToConstructor() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/notApplicableToConstructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyGetterInitializerTypeMismatch.kt")
|
||||
public void testPropertyGetterInitializerTypeMismatch() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/propertyGetterInitializerTypeMismatch.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("returnedExpressionTypeMismatchFunctionParameterType.kt")
|
||||
public void testReturnedExpressionTypeMismatchFunctionParameterType() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/returnedExpressionTypeMismatchFunctionParameterType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("typeMismatchInIfStatementReturnedByFunction.kt")
|
||||
public void testTypeMismatchInIfStatementReturnedByFunction() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/typeMismatchInIfStatementReturnedByFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("typeMismatchInIfStatementReturnedByLiteral.kt")
|
||||
public void testTypeMismatchInIfStatementReturnedByLiteral() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/typeMismatchInIfStatementReturnedByLiteral.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("typeMismatchInInitializer.kt")
|
||||
public void testTypeMismatchInInitializer() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/typeMismatchInInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("typeMismatchInReturnStatement.kt")
|
||||
public void testTypeMismatchInReturnStatement() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/typeMismatchInReturnStatement.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -217,6 +217,11 @@ private object FirToKtConversionCreator {
|
||||
KtSymbol::class.createType(),
|
||||
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirDeclaration")
|
||||
),
|
||||
FirSimpleFunction::class to HLFunctionCallConversion(
|
||||
"firSymbolBuilder.buildSymbol({0})",
|
||||
KtSymbol::class.createType(),
|
||||
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirSimpleFunction")
|
||||
),
|
||||
FirNamedFunctionSymbol::class to HLFunctionCallConversion(
|
||||
"firSymbolBuilder.functionLikeBuilder.buildFunctionSymbol({0}.fir)",
|
||||
KtFunctionLikeSymbol::class.createType(),
|
||||
|
||||
+5
-1
@@ -137,7 +137,11 @@ object FirIdeDeserializedDeclarationSourceProvider {
|
||||
|
||||
private fun KtElement.isCompiled(): Boolean = containingKtFile.isCompiled
|
||||
|
||||
private val allowedFakeElementKinds = setOf(FirFakeSourceElementKind.PropertyFromParameter, FirFakeSourceElementKind.ItLambdaParameter)
|
||||
private val allowedFakeElementKinds = setOf(
|
||||
FirFakeSourceElementKind.PropertyFromParameter,
|
||||
FirFakeSourceElementKind.ItLambdaParameter,
|
||||
FirFakeSourceElementKind.DataClassGeneratedMembers
|
||||
)
|
||||
|
||||
private fun FirElement.getAllowedPsi() = when (val source = source) {
|
||||
null -> null
|
||||
|
||||
+1
@@ -1289,6 +1289,7 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
ReturnTypeMismatchImpl(
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b),
|
||||
firSymbolBuilder.buildSymbol(firDiagnostic.c),
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
|
||||
+1
@@ -910,6 +910,7 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
override val diagnosticClass get() = ReturnTypeMismatch::class
|
||||
abstract val expectedType: KtType
|
||||
abstract val actualType: KtType
|
||||
abstract val targetFunction: KtSymbol
|
||||
}
|
||||
|
||||
abstract class CyclicGenericUpperBound : KtFirDiagnostic<PsiElement>() {
|
||||
|
||||
+1
@@ -1471,6 +1471,7 @@ internal class TypeParameterOfPropertyNotUsedInReceiverImpl(
|
||||
internal class ReturnTypeMismatchImpl(
|
||||
override val expectedType: KtType,
|
||||
override val actualType: KtType,
|
||||
override val targetFunction: KtSymbol,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.ReturnTypeMismatch(), KtAbstractFirDiagnostic<KtExpression> {
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.quickfix
|
||||
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
|
||||
object ChangeTypeFixUtils {
|
||||
fun familyName(): String = KotlinBundle.message("fix.change.return.type.family")
|
||||
|
||||
fun functionOrConstructorParameterPresentation(element: KtCallableDeclaration, containerName: String?): String? {
|
||||
val name = element.name
|
||||
return if (name != null) {
|
||||
val fullName = if (containerName != null) "'${containerName}.$name'" else "'$name'"
|
||||
when (element) {
|
||||
is KtParameter -> KotlinBundle.message("fix.change.return.type.presentation.property", fullName)
|
||||
is KtProperty -> KotlinBundle.message("fix.change.return.type.presentation.property", fullName)
|
||||
else -> KotlinBundle.message("fix.change.return.type.presentation.function", fullName)
|
||||
}
|
||||
} else null
|
||||
}
|
||||
|
||||
|
||||
fun baseFunctionOrConstructorParameterPresentation(presentation: String): String =
|
||||
KotlinBundle.message("fix.change.return.type.presentation.base", presentation)
|
||||
|
||||
fun baseFunctionOrConstructorParameterPresentation(element: KtCallableDeclaration, containerName: String?): String? {
|
||||
val presentation = functionOrConstructorParameterPresentation(element, containerName) ?: return null
|
||||
return baseFunctionOrConstructorParameterPresentation(presentation)
|
||||
}
|
||||
|
||||
fun getTextForQuickFix(
|
||||
element: KtCallableDeclaration,
|
||||
presentation: String?,
|
||||
isUnitType: Boolean,
|
||||
typePresentation: String
|
||||
): String {
|
||||
if (isUnitType && element is KtFunction && element.hasBlockBody()) {
|
||||
return if (presentation == null)
|
||||
KotlinBundle.message("fix.change.return.type.remove.explicit.return.type")
|
||||
else
|
||||
KotlinBundle.message("fix.change.return.type.remove.explicit.return.type.of", presentation)
|
||||
}
|
||||
|
||||
return when (element) {
|
||||
is KtFunction -> {
|
||||
if (presentation != null)
|
||||
KotlinBundle.message("fix.change.return.type.return.type.text.of", presentation, typePresentation)
|
||||
else
|
||||
KotlinBundle.message("fix.change.return.type.return.type.text", typePresentation)
|
||||
}
|
||||
else -> {
|
||||
if (presentation != null)
|
||||
KotlinBundle.message("fix.change.return.type.type.text.of", presentation, typePresentation)
|
||||
else
|
||||
KotlinBundle.message("fix.change.return.type.type.text", typePresentation)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,6 @@ import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
|
||||
import org.jetbrains.kotlin.idea.project.builtIns
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
@@ -74,8 +73,8 @@ abstract class ChangeCallableReturnTypeFix(
|
||||
val element = element!!
|
||||
if (element.name == null) return null
|
||||
val container = element.unsafeResolveToDescriptor().containingDeclaration as? ClassDescriptor
|
||||
val containerName = container?.name?.takeUnless { it.isSpecial }
|
||||
return StringPresentation.functionOrConstructorParameterPresentation(element, containerName)
|
||||
val containerName = container?.name?.takeUnless { it.isSpecial }?.asString()
|
||||
return ChangeTypeFixUtils.functionOrConstructorParameterPresentation(element, containerName)
|
||||
}
|
||||
|
||||
class OnType(element: KtFunction, type: KotlinType) : ChangeCallableReturnTypeFix(element, type), HighPriorityAction {
|
||||
@@ -104,7 +103,7 @@ abstract class ChangeCallableReturnTypeFix(
|
||||
class ForOverridden(element: KtFunction, type: KotlinType) : ChangeCallableReturnTypeFix(element, type) {
|
||||
override fun functionPresentation(): String? {
|
||||
val presentation = super.functionPresentation() ?: return null
|
||||
return StringPresentation.baseFunctionOrConstructorParameterPresentation(presentation)
|
||||
return ChangeTypeFixUtils.baseFunctionOrConstructorParameterPresentation(presentation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,10 +114,10 @@ abstract class ChangeCallableReturnTypeFix(
|
||||
return changeFunctionLiteralReturnTypeFix.text
|
||||
}
|
||||
|
||||
return StringPresentation.getTextForQuickFix(element, functionPresentation(), isUnitType, typePresentation)
|
||||
return ChangeTypeFixUtils.getTextForQuickFix(element, functionPresentation(), isUnitType, typePresentation)
|
||||
}
|
||||
|
||||
override fun getFamilyName(): String = StringPresentation.familyName()
|
||||
override fun getFamilyName(): String = ChangeTypeFixUtils.familyName()
|
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean {
|
||||
return !typeContainsError &&
|
||||
@@ -234,59 +233,5 @@ abstract class ChangeCallableReturnTypeFix(
|
||||
return multiDeclaration.entries[componentIndex - 1]
|
||||
}
|
||||
}
|
||||
|
||||
object StringPresentation {
|
||||
fun familyName(): String = KotlinBundle.message("fix.change.return.type.family")
|
||||
|
||||
fun functionOrConstructorParameterPresentation(element: KtCallableDeclaration, containerName: Name?): String? {
|
||||
val name = element.name
|
||||
return if (name != null) {
|
||||
val fullName = if (containerName != null) "'${containerName.asString()}.$name'" else "'$name'"
|
||||
when (element) {
|
||||
is KtParameter -> KotlinBundle.message("fix.change.return.type.presentation.property", fullName)
|
||||
is KtProperty -> KotlinBundle.message("fix.change.return.type.presentation.property", fullName)
|
||||
else -> KotlinBundle.message("fix.change.return.type.presentation.function", fullName)
|
||||
}
|
||||
} else null
|
||||
}
|
||||
|
||||
|
||||
fun baseFunctionOrConstructorParameterPresentation(presentation: String): String =
|
||||
KotlinBundle.message("fix.change.return.type.presentation.base", presentation)
|
||||
|
||||
fun baseFunctionOrConstructorParameterPresentation(element: KtCallableDeclaration, containerName: Name?): String? {
|
||||
val presentation = functionOrConstructorParameterPresentation(element, containerName) ?: return null
|
||||
return baseFunctionOrConstructorParameterPresentation(presentation)
|
||||
}
|
||||
|
||||
fun getTextForQuickFix(
|
||||
element: KtCallableDeclaration,
|
||||
presentation: String?,
|
||||
isUnitType: Boolean,
|
||||
typePresentation: String
|
||||
): String {
|
||||
if (isUnitType && element is KtFunction && element.hasBlockBody()) {
|
||||
return if (presentation == null)
|
||||
KotlinBundle.message("fix.change.return.type.remove.explicit.return.type")
|
||||
else
|
||||
KotlinBundle.message("fix.change.return.type.remove.explicit.return.type.of", presentation)
|
||||
}
|
||||
|
||||
return when (element) {
|
||||
is KtFunction -> {
|
||||
if (presentation != null)
|
||||
KotlinBundle.message("fix.change.return.type.return.type.text.of", presentation, typePresentation)
|
||||
else
|
||||
KotlinBundle.message("fix.change.return.type.return.type.text", typePresentation)
|
||||
}
|
||||
else -> {
|
||||
if (presentation != null)
|
||||
KotlinBundle.message("fix.change.return.type.type.text.of", presentation, typePresentation)
|
||||
else
|
||||
KotlinBundle.message("fix.change.return.type.type.text", typePresentation)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+2
-1
@@ -4,4 +4,5 @@ fun foo() {
|
||||
var x = 1
|
||||
x += 21<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
/* IGNORE_FIR */
|
||||
|
||||
idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/assignmentTypeMismatch.kt.after
Vendored
+2
-1
@@ -4,4 +4,5 @@ fun foo() {
|
||||
var x = 1
|
||||
x += 21<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
/* IGNORE_FIR */
|
||||
|
||||
Vendored
+2
-1
@@ -1,4 +1,5 @@
|
||||
// "Change type of 'complex' to '(Int) -> Long'" "true"
|
||||
|
||||
val complex: (Int) -> String
|
||||
get() = { it.toLong()<caret> }
|
||||
get() = { it.toLong()<caret> }
|
||||
/* IGNORE_FIR */
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
// "Change type of 'complex' to '(Int) -> Long'" "true"
|
||||
|
||||
val complex: (Int) -> Long
|
||||
get() = { it.toLong() }
|
||||
get() = { it.toLong() }
|
||||
/* IGNORE_FIR */
|
||||
|
||||
+2
-1
@@ -4,4 +4,5 @@ class A {
|
||||
var x: Int
|
||||
get(): Int = if (true) { {42}<caret> } else { {24} }
|
||||
set(i: Int) {}
|
||||
}
|
||||
}
|
||||
/* IGNORE_FIR */
|
||||
|
||||
+2
-1
@@ -3,4 +3,5 @@ fun foo(f: () -> Int) {
|
||||
foo {
|
||||
""<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
/* IGNORE_FIR */
|
||||
|
||||
+2
-1
@@ -9,4 +9,5 @@ fun foo() {
|
||||
if (true) x<caret> else x
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* IGNORE_FIR */
|
||||
|
||||
Reference in New Issue
Block a user