FIR IDE: add HLConvertToBlockBodyIntention
This commit is contained in:
committed by
TeamCityServer
parent
030749eaeb
commit
55e7cc7ea3
+1
@@ -1091,6 +1091,7 @@ fun main(args: Array<String>) {
|
||||
model("intentions/specifyTypeExplicitly", pattern = pattern)
|
||||
model("intentions/importAllMembers", pattern = pattern)
|
||||
model("intentions/importMember", pattern = pattern)
|
||||
model("intentions/convertToBlockBody", pattern = pattern)
|
||||
}
|
||||
|
||||
testClass<AbstractFirShortenRefsTest> {
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
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.AbstractHLIntention
|
||||
import org.jetbrains.kotlin.idea.fir.api.applicator.HLApplicabilityRange
|
||||
import org.jetbrains.kotlin.idea.fir.api.applicator.HLApplicatorInputProvider
|
||||
import org.jetbrains.kotlin.idea.fir.api.applicator.inputProvider
|
||||
import org.jetbrains.kotlin.idea.fir.applicators.ApplicabilityRanges
|
||||
import org.jetbrains.kotlin.idea.formatter.adjustLineIndent
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtClassErrorType
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
import org.jetbrains.kotlin.idea.util.resultingWhens
|
||||
import org.jetbrains.kotlin.idea.util.setType
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
|
||||
class HLConvertToBlockBodyIntention :
|
||||
AbstractHLIntention<KtDeclarationWithBody, HLConvertToBlockBodyIntention.Input>(KtDeclarationWithBody::class, applicator) {
|
||||
|
||||
class Input(
|
||||
val returnType: KtType,
|
||||
val returnTypeIsUnit: Boolean,
|
||||
val returnTypeIsNothing: Boolean,
|
||||
val returnTypeString: String,
|
||||
val returnTypeClassId: ClassId?,
|
||||
val bodyTypeIsUnit: Boolean,
|
||||
val bodyTypeIsNothing: Boolean,
|
||||
val reformat: Boolean,
|
||||
) : HLApplicatorInput
|
||||
|
||||
override fun allowCaretInsideElement(element: PsiElement) = element !is KtDeclaration && super.allowCaretInsideElement(element)
|
||||
|
||||
override val applicabilityRange: HLApplicabilityRange<KtDeclarationWithBody> get() = ApplicabilityRanges.SELF
|
||||
|
||||
override val inputProvider: HLApplicatorInputProvider<KtDeclarationWithBody, Input>
|
||||
get() = inputProvider { psi ->
|
||||
if (psi is KtNamedFunction) {
|
||||
val returnType = psi.getReturnKtType()
|
||||
if (!psi.hasDeclaredReturnType() && returnType is KtClassErrorType) return@inputProvider null
|
||||
}
|
||||
createInputForDeclaration(psi, true)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun KtAnalysisSession.createInputForDeclaration(declaration: KtDeclarationWithBody, reformat: Boolean): Input? {
|
||||
val body = declaration.bodyExpression ?: return null
|
||||
val returnType = declaration.getReturnKtType().approximateToSuperPublicDenotableOrSelf()
|
||||
val bodyType = body.getKtType()
|
||||
return Input(
|
||||
returnType,
|
||||
returnType.isUnit,
|
||||
returnType.isNothing,
|
||||
returnType.render(),
|
||||
returnType.expandedClassSymbol?.classIdIfNonLocal,
|
||||
bodyType.isUnit,
|
||||
bodyType.isNothing,
|
||||
reformat,
|
||||
)
|
||||
}
|
||||
|
||||
val applicator = applicator<KtDeclarationWithBody, Input> {
|
||||
familyAndActionName(KotlinBundle.lazyMessage(("convert.to.block.body")))
|
||||
isApplicableByPsi { (it is KtNamedFunction || it is KtPropertyAccessor) && !it.hasBlockBody() && it.hasBody() }
|
||||
applyTo { declaration, input ->
|
||||
val body = declaration.bodyExpression!!
|
||||
|
||||
val newBody = when (declaration) {
|
||||
is KtNamedFunction -> {
|
||||
if (!declaration.hasDeclaredReturnType() && !input.returnTypeIsUnit) {
|
||||
declaration.setType(input.returnTypeString, input.returnTypeClassId)
|
||||
}
|
||||
generateBody(body, input, !input.returnTypeIsUnit && !input.returnTypeIsNothing)
|
||||
}
|
||||
|
||||
is KtPropertyAccessor -> {
|
||||
val parent = declaration.parent
|
||||
if (parent is KtProperty && parent.typeReference == null) {
|
||||
parent.setType(input.returnTypeString, input.returnTypeClassId)
|
||||
}
|
||||
|
||||
generateBody(body, input, declaration.isGetter)
|
||||
}
|
||||
|
||||
else -> throw RuntimeException("Unknown declaration type: $declaration")
|
||||
}
|
||||
|
||||
declaration.equalsToken!!.delete()
|
||||
val replaced = body.replace(newBody)
|
||||
if (input.reformat) declaration.containingKtFile.adjustLineIndent(replaced.startOffset, replaced.endOffset)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateBody(body: KtExpression, input: Input, returnsValue: Boolean): KtExpression {
|
||||
val factory = KtPsiFactory(body)
|
||||
if (input.bodyTypeIsUnit && body is KtNameReferenceExpression) return factory.createEmptyBody()
|
||||
val unitWhenAsResult = input.bodyTypeIsUnit && body.resultingWhens().isNotEmpty()
|
||||
val needReturn = returnsValue && (!input.bodyTypeIsUnit && !input.bodyTypeIsNothing)
|
||||
return if (needReturn || unitWhenAsResult) {
|
||||
val annotatedExpr = body as? KtAnnotatedExpression
|
||||
val returnedExpr = annotatedExpr?.baseExpression ?: body
|
||||
val block = factory.createSingleStatementBlock(factory.createExpressionByPattern("return $0", returnedExpr))
|
||||
val statement = block.firstStatement
|
||||
annotatedExpr?.annotationEntries?.forEach {
|
||||
block.addBefore(it, statement)
|
||||
block.addBefore(factory.createNewLine(), statement)
|
||||
}
|
||||
block
|
||||
} else {
|
||||
factory.createSingleStatementBlock(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.util
|
||||
|
||||
import org.jetbrains.kotlin.idea.frontend.api.analyse
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.HackToForceAllowRunningAnalyzeOnEDT
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.hackyAllowRunningOnEdt
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
|
||||
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class)
|
||||
fun KtCallableDeclaration.setType(typeString: String, classId: ClassId?, shortenReferences: Boolean = true) {
|
||||
val typeReference = KtPsiFactory(project).createType(typeString)
|
||||
setTypeReference(typeReference)
|
||||
if (shortenReferences && classId != null) {
|
||||
hackyAllowRunningOnEdt {
|
||||
analyse(this) {
|
||||
collectPossibleReferenceShortenings(
|
||||
containingKtFile,
|
||||
getTypeReference()!!.textRange,
|
||||
).invokeShortening()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -7,7 +7,9 @@ package org.jetbrains.kotlin.idea.fir.intentions
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.idea.fir.invalidateCaches
|
||||
import org.jetbrains.kotlin.idea.intentions.AbstractIntentionTest
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.utils.IgnoreTests
|
||||
import java.io.File
|
||||
|
||||
@@ -30,6 +32,11 @@ abstract class AbstractHLIntentionTest : AbstractIntentionTest() {
|
||||
override fun checkForErrorsAfter(fileText: String) {}
|
||||
override fun checkForErrorsBefore(fileText: String) {}
|
||||
|
||||
override fun tearDown() {
|
||||
project.invalidateCaches(file as? KtFile)
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val AFTER_FIR_EXTENSION = ".after.fir"
|
||||
}
|
||||
|
||||
Generated
+168
@@ -679,4 +679,172 @@ public class HLIntentionTestGenerated extends AbstractHLIntentionTest {
|
||||
runTest("idea/testData/intentions/importMember/TopLevelFun.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/convertToBlockBody")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ConvertToBlockBody extends AbstractHLIntentionTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("addSpace.kt")
|
||||
public void testAddSpace() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/addSpace.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("adjustLineIndent.kt")
|
||||
public void testAdjustLineIndent() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/adjustLineIndent.kt");
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInConvertToBlockBody() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/intentions/convertToBlockBody"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("annotatedExpr.kt")
|
||||
public void testAnnotatedExpr() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/annotatedExpr.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("annotatedExpr2.kt")
|
||||
public void testAnnotatedExpr2() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/annotatedExpr2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("annotatedExprInParentheses.kt")
|
||||
public void testAnnotatedExprInParentheses() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/annotatedExprInParentheses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("explicitlyNonUnitFun.kt")
|
||||
public void testExplicitlyNonUnitFun() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/explicitlyNonUnitFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("explicitlyTypedFunWithUnresolvedExpression.kt")
|
||||
public void testExplicitlyTypedFunWithUnresolvedExpression() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/explicitlyTypedFunWithUnresolvedExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("explicitlyTypedFunWithUnresolvedType.kt")
|
||||
public void testExplicitlyTypedFunWithUnresolvedType() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/explicitlyTypedFunWithUnresolvedType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("explicitlyUnitFun.kt")
|
||||
public void testExplicitlyUnitFun() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/explicitlyUnitFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("explicitlyUnitFunWithUnresolvedExpression.kt")
|
||||
public void testExplicitlyUnitFunWithUnresolvedExpression() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/explicitlyUnitFunWithUnresolvedExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("funWithCustomUnitClass.kt")
|
||||
public void testFunWithCustomUnitClass() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/funWithCustomUnitClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("funWithThrow.kt")
|
||||
public void testFunWithThrow() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/funWithThrow.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("funWithUnit.kt")
|
||||
public void testFunWithUnit() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/funWithUnit.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("getter.kt")
|
||||
public void testGetter() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/getter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("getterTypeInferred.kt")
|
||||
public void testGetterTypeInferred() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/getterTypeInferred.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("getterWithThrow.kt")
|
||||
public void testGetterWithThrow() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/getterWithThrow.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifWhenUnit.kt")
|
||||
public void testIfWhenUnit() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/ifWhenUnit.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitlyNonUnitFun.kt")
|
||||
public void testImplicitlyNonUnitFun() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/implicitlyNonUnitFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitlyNonUnitFun2.kt")
|
||||
public void testImplicitlyNonUnitFun2() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/implicitlyNonUnitFun2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitlyTypedFunWithUnresolvedType.kt")
|
||||
public void testImplicitlyTypedFunWithUnresolvedType() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/implicitlyTypedFunWithUnresolvedType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitlyUnitFun.kt")
|
||||
public void testImplicitlyUnitFun() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/implicitlyUnitFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("labeledExpr.kt")
|
||||
public void testLabeledExpr() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/labeledExpr.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("labeledExprInParentheses.kt")
|
||||
public void testLabeledExprInParentheses() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/labeledExprInParentheses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nothingFun.kt")
|
||||
public void testNothingFun() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/nothingFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("overrideWithPlatformType.kt")
|
||||
public void testOverrideWithPlatformType() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/overrideWithPlatformType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("setter.kt")
|
||||
public void testSetter() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/setter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("valueIsAnonymousObject.kt")
|
||||
public void testValueIsAnonymousObject() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/valueIsAnonymousObject.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("valueIsAnonymousObject2.kt")
|
||||
public void testValueIsAnonymousObject2() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/valueIsAnonymousObject2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("valueIsAnonymousObject3.kt")
|
||||
public void testValueIsAnonymousObject3() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/valueIsAnonymousObject3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("valueIsAnonymousObject4.kt")
|
||||
public void testValueIsAnonymousObject4() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/valueIsAnonymousObject4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("whenUnit.kt")
|
||||
public void testWhenUnit() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/whenUnit.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -6,6 +6,8 @@
|
||||
package org.jetbrains.kotlin.idea.frontend.api.components
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtTypeAliasSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
|
||||
@@ -37,12 +39,25 @@ interface KtTypeInfoProviderMixIn : KtAnalysisSessionMixIn {
|
||||
val KtType.isString: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.STRING)
|
||||
val KtType.isCharSequence: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.CHAR_SEQUENCE)
|
||||
val KtType.isAny: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.ANY)
|
||||
val KtType.isNothing: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.NOTHING)
|
||||
|
||||
val KtType.isUInt: Boolean get() = isClassTypeWithClassId(StandardNames.FqNames.uInt)
|
||||
val KtType.isULong: Boolean get() = isClassTypeWithClassId(StandardNames.FqNames.uLong)
|
||||
val KtType.isUShort: Boolean get() = isClassTypeWithClassId(StandardNames.FqNames.uShort)
|
||||
val KtType.isUByte: Boolean get() = isClassTypeWithClassId(StandardNames.FqNames.uByte)
|
||||
|
||||
/** Gets the class symbol backing the given type, if available. */
|
||||
val KtType.expandedClassSymbol: KtClassOrObjectSymbol?
|
||||
get() {
|
||||
return when (this) {
|
||||
is KtNonErrorClassType -> when (val classSymbol = classSymbol) {
|
||||
is KtClassOrObjectSymbol -> classSymbol
|
||||
is KtTypeAliasSymbol -> classSymbol.expandedType.expandedClassSymbol
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun KtType.isClassTypeWithClassId(classId: ClassId): Boolean {
|
||||
if (this !is KtNonErrorClassType) return false
|
||||
return this.classId == classId
|
||||
@@ -85,5 +100,6 @@ object DefaultTypeClassIds {
|
||||
val STRING = ClassId.topLevel(StandardNames.FqNames.string.toSafe())
|
||||
val CHAR_SEQUENCE = ClassId.topLevel(StandardNames.FqNames.charSequence.toSafe())
|
||||
val ANY = ClassId.topLevel(StandardNames.FqNames.any.toSafe())
|
||||
val NOTHING = ClassId.topLevel(StandardNames.FqNames.nothing.toSafe())
|
||||
val PRIMITIVES = setOf(INT, LONG, SHORT, BYTE, FLOAT, DOUBLE, CHAR, BOOLEAN)
|
||||
}
|
||||
|
||||
+2
@@ -33,6 +33,8 @@ interface KtTypeProviderMixIn : KtAnalysisSessionMixIn {
|
||||
fun KtType.approximateToSuperPublicDenotable(): KtType? =
|
||||
analysisSession.typeProvider.approximateToSuperPublicDenotableType(this)
|
||||
|
||||
fun KtType.approximateToSuperPublicDenotableOrSelf(): KtType = approximateToSuperPublicDenotable() ?: this
|
||||
|
||||
fun KtNamedClassOrObjectSymbol.buildSelfClassType(): KtType =
|
||||
analysisSession.typeProvider.buildSelfClassType(this)
|
||||
|
||||
|
||||
@@ -52,3 +52,11 @@ fun PsiClass.classIdIfNonLocal(): ClassId? {
|
||||
return ClassId(packageFqName, FqName(classesNames.joinToString(separator = ".")), false)
|
||||
}
|
||||
|
||||
fun KtExpression.resultingWhens(): List<KtWhenExpression> = when (this) {
|
||||
is KtWhenExpression -> listOf(this) + entries.map { it.expression?.resultingWhens() ?: listOf() }.flatten()
|
||||
is KtIfExpression -> (then?.resultingWhens() ?: listOf()) + (`else`?.resultingWhens() ?: listOf())
|
||||
is KtBinaryExpression -> (left?.resultingWhens() ?: listOf()) + (right?.resultingWhens() ?: listOf())
|
||||
is KtUnaryExpression -> this.baseExpression?.resultingWhens() ?: listOf()
|
||||
is KtBlockExpression -> statements.lastOrNull()?.resultingWhens() ?: listOf()
|
||||
else -> listOf()
|
||||
}
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun foo()<spot>: String {
|
||||
return "abc"
|
||||
}</spot>
|
||||
+1
@@ -0,0 +1 @@
|
||||
fun foo() <spot>= "abc"</spot>
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention converts an expression body (single expression after <b>=</b> sign) to a block body with a return statement.
|
||||
</body>
|
||||
</html>
|
||||
@@ -30,5 +30,10 @@
|
||||
<className>org.jetbrains.kotlin.idea.fir.intentions.HLImportMemberIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.fir.intentions.HLConvertToBlockBodyIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -24,8 +24,8 @@ import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.idea.core.setType
|
||||
import org.jetbrains.kotlin.idea.core.util.isOneLiner
|
||||
import org.jetbrains.kotlin.idea.intentions.hasResultingIfWithoutElse
|
||||
import org.jetbrains.kotlin.idea.intentions.resultingWhens
|
||||
import org.jetbrains.kotlin.idea.util.CommentSaver
|
||||
import org.jetbrains.kotlin.idea.util.resultingWhens
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.core.setType
|
||||
import org.jetbrains.kotlin.idea.formatter.adjustLineIndent
|
||||
import org.jetbrains.kotlin.idea.util.resultingWhens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
|
||||
@@ -124,15 +124,6 @@ fun KtExpression.negate(reformat: Boolean = true): KtExpression {
|
||||
return KtPsiFactory(this).createExpressionByPattern("!$0", this, reformat = reformat)
|
||||
}
|
||||
|
||||
fun KtExpression.resultingWhens(): List<KtWhenExpression> = when (this) {
|
||||
is KtWhenExpression -> listOf(this) + entries.map { it.expression?.resultingWhens() ?: listOf() }.flatten()
|
||||
is KtIfExpression -> (then?.resultingWhens() ?: listOf()) + (`else`?.resultingWhens() ?: listOf())
|
||||
is KtBinaryExpression -> (left?.resultingWhens() ?: listOf()) + (right?.resultingWhens() ?: listOf())
|
||||
is KtUnaryExpression -> this.baseExpression?.resultingWhens() ?: listOf()
|
||||
is KtBlockExpression -> statements.lastOrNull()?.resultingWhens() ?: listOf()
|
||||
else -> listOf()
|
||||
}
|
||||
|
||||
fun KtExpression?.hasResultingIfWithoutElse(): Boolean = when (this) {
|
||||
is KtIfExpression -> `else` == null || then.hasResultingIfWithoutElse() || `else`.hasResultingIfWithoutElse()
|
||||
is KtWhenExpression -> entries.any { it.expression.hasResultingIfWithoutElse() }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.fir.intentions.HLConvertToBlockBodyIntention
|
||||
@@ -0,0 +1,5 @@
|
||||
@Target(AnnotationTarget.EXPRESSION)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class ann
|
||||
|
||||
fun foo(): Int = <caret>(@ann 1)
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
@Target(AnnotationTarget.EXPRESSION)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class ann
|
||||
|
||||
fun foo(): Int {
|
||||
return (@ann 1)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(): String {
|
||||
return java.io.File("x").getAbsolutePath()
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun <caret>foo() = java.io.File("x").list()
|
||||
@@ -0,0 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(): Array<out String>? {
|
||||
return java.io.File("x").list()
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(): Array<out String?>? {
|
||||
return java.io.File("x").list()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
fun foo(): Int = <caret>(ann@ 1)
|
||||
@@ -0,0 +1,3 @@
|
||||
fun foo(): Int {
|
||||
return (ann@ 1)
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
interface I1
|
||||
interface I2
|
||||
|
||||
fun f() {
|
||||
fun g(): I1 {
|
||||
return object : I1, I2 { }
|
||||
}
|
||||
}
|
||||
@@ -7119,6 +7119,11 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/annotatedExpr2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("annotatedExprInParentheses.kt")
|
||||
public void testAnnotatedExprInParentheses() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/annotatedExprInParentheses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("explicitlyNonUnitFun.kt")
|
||||
public void testExplicitlyNonUnitFun() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/explicitlyNonUnitFun.kt");
|
||||
@@ -7184,6 +7189,11 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/implicitlyNonUnitFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitlyNonUnitFun2.kt")
|
||||
public void testImplicitlyNonUnitFun2() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/implicitlyNonUnitFun2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitlyTypedFunWithUnresolvedType.kt")
|
||||
public void testImplicitlyTypedFunWithUnresolvedType() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/implicitlyTypedFunWithUnresolvedType.kt");
|
||||
@@ -7199,6 +7209,11 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/labeledExpr.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("labeledExprInParentheses.kt")
|
||||
public void testLabeledExprInParentheses() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/labeledExprInParentheses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nothingFun.kt")
|
||||
public void testNothingFun() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToBlockBody/nothingFun.kt");
|
||||
|
||||
Reference in New Issue
Block a user