Introduce "use expression body" inspection #KT-16063 Fixed
Converted from the relevant intention Reported cases: one-liners, whens Also, more exact caret detection in local inspection tests
This commit is contained in:
committed by
Mikhail Glukhikh
parent
fe9d3f16d3
commit
d08b18f5f8
@@ -0,0 +1,5 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
This inspection reports returns (one-liners or whens) replaceable by expression body syntax.
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -853,11 +853,6 @@
|
|||||||
<category>Kotlin</category>
|
<category>Kotlin</category>
|
||||||
</intentionAction>
|
</intentionAction>
|
||||||
|
|
||||||
<intentionAction>
|
|
||||||
<className>org.jetbrains.kotlin.idea.intentions.ConvertToExpressionBodyIntention</className>
|
|
||||||
<category>Kotlin</category>
|
|
||||||
</intentionAction>
|
|
||||||
|
|
||||||
<intentionAction>
|
<intentionAction>
|
||||||
<className>org.jetbrains.kotlin.idea.intentions.ConvertToBlockBodyIntention</className>
|
<className>org.jetbrains.kotlin.idea.intentions.ConvertToBlockBodyIntention</className>
|
||||||
<category>Kotlin</category>
|
<category>Kotlin</category>
|
||||||
@@ -2376,6 +2371,15 @@
|
|||||||
language="kotlin"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.UseExpressionBodyInspection"
|
||||||
|
displayName="Expression body syntax is preferable here"
|
||||||
|
groupPath="Kotlin"
|
||||||
|
groupName="Style issues"
|
||||||
|
enabledByDefault="true"
|
||||||
|
level="WEAK WARNING"
|
||||||
|
language="kotlin"
|
||||||
|
/>
|
||||||
|
|
||||||
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
||||||
|
|
||||||
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
||||||
|
|||||||
@@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.inspections
|
||||||
|
|
||||||
|
import com.intellij.codeInspection.LocalQuickFix
|
||||||
|
import com.intellij.codeInspection.ProblemDescriptor
|
||||||
|
import com.intellij.codeInspection.ProblemHighlightType
|
||||||
|
import com.intellij.codeInspection.ProblemHighlightType.GENERIC_ERROR_OR_WARNING
|
||||||
|
import com.intellij.codeInspection.ProblemHighlightType.INFORMATION
|
||||||
|
import com.intellij.codeInspection.ProblemsHolder
|
||||||
|
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.idea.core.canOmitDeclaredType
|
||||||
|
import org.jetbrains.kotlin.idea.core.replaced
|
||||||
|
import org.jetbrains.kotlin.idea.core.setType
|
||||||
|
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.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
|
||||||
|
class UseExpressionBodyInspection(private val convertEmptyToUnit: Boolean) : AbstractKotlinInspection() {
|
||||||
|
|
||||||
|
constructor(): this(convertEmptyToUnit = true)
|
||||||
|
|
||||||
|
private data class Status(val toHighlight: PsiElement?, val subject: String, val highlightType: ProblemHighlightType)
|
||||||
|
|
||||||
|
fun isActiveFor(declaration: KtDeclarationWithBody) = statusFor(declaration) != null
|
||||||
|
|
||||||
|
private fun statusFor(declaration: KtDeclarationWithBody): Status? {
|
||||||
|
if (declaration is KtConstructor<*>) return null
|
||||||
|
|
||||||
|
val valueStatement = declaration.findValueStatement() ?: return null
|
||||||
|
val value = valueStatement.getValue()
|
||||||
|
if (value.anyDescendantOfType<KtReturnExpression>(
|
||||||
|
canGoInside = { it !is KtFunctionLiteral && it !is KtNamedFunction && it !is KtPropertyAccessor }
|
||||||
|
)) return null
|
||||||
|
|
||||||
|
val toHighlight = valueStatement.toHighlight()
|
||||||
|
return when {
|
||||||
|
valueStatement !is KtReturnExpression -> Status(toHighlight, "block body", INFORMATION)
|
||||||
|
valueStatement.returnedExpression is KtWhenExpression -> Status(toHighlight, "'return when'", GENERIC_ERROR_OR_WARNING)
|
||||||
|
valueStatement.isOneLiner() -> Status(toHighlight, "one-line return", GENERIC_ERROR_OR_WARNING)
|
||||||
|
else -> Status(toHighlight, "return", INFORMATION)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) =
|
||||||
|
object : KtVisitorVoid() {
|
||||||
|
override fun visitDeclaration(declaration: KtDeclaration) {
|
||||||
|
super.visitDeclaration(declaration)
|
||||||
|
|
||||||
|
declaration as? KtDeclarationWithBody ?: return
|
||||||
|
val (toHighlight, suffix, highlightType) = statusFor(declaration) ?: return
|
||||||
|
|
||||||
|
val problemDescriptor = holder.manager.createProblemDescriptor(
|
||||||
|
declaration,
|
||||||
|
toHighlight?.textRange?.shiftRight(-declaration.startOffset),
|
||||||
|
"Use expression body instead of $suffix",
|
||||||
|
highlightType,
|
||||||
|
isOnTheFly,
|
||||||
|
ConvertToExpressionBodyFix()
|
||||||
|
)
|
||||||
|
holder.registerProblem(problemDescriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtExpression.isOneLiner(): Boolean {
|
||||||
|
val file = containingFile?.virtualFile ?: return false
|
||||||
|
val document = FileDocumentManager.getInstance().getDocument(file) ?: return false
|
||||||
|
return document.getLineNumber(textRange.startOffset) == document.getLineNumber(textRange.endOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtDeclarationWithBody.findValueStatement(): KtExpression? {
|
||||||
|
val body = blockExpression() ?: return null
|
||||||
|
return body.findValueStatement()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtDeclarationWithBody.blockExpression() = when (this) {
|
||||||
|
is KtFunctionLiteral -> null
|
||||||
|
else -> if (!hasBlockBody()) null else bodyExpression as? KtBlockExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtBlockExpression.findValueStatement(): KtExpression? {
|
||||||
|
val bodyStatements = statements
|
||||||
|
if (bodyStatements.isEmpty()) {
|
||||||
|
return if (convertEmptyToUnit) KtPsiFactory(this).createExpression("Unit") else null
|
||||||
|
}
|
||||||
|
val statement = bodyStatements.singleOrNull() ?: return null
|
||||||
|
when (statement) {
|
||||||
|
is KtReturnExpression -> {
|
||||||
|
return statement
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: IMO this is not good code, there should be a way to detect that KtExpression does not have value
|
||||||
|
is KtDeclaration, is KtLoopExpression -> return null
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
// assignment does not have value
|
||||||
|
if (statement is KtBinaryExpression && statement.operationToken in KtTokens.ALL_ASSIGNMENTS) return null
|
||||||
|
|
||||||
|
val context = statement.analyze()
|
||||||
|
val expressionType = context.getType(statement) ?: return null
|
||||||
|
val isUnit = KotlinBuiltIns.isUnit(expressionType)
|
||||||
|
if (!isUnit && !KotlinBuiltIns.isNothing(expressionType)) return null
|
||||||
|
if (isUnit) {
|
||||||
|
if (statement.hasResultingIfWithoutElse()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val resultingWhens = statement.resultingWhens()
|
||||||
|
if (resultingWhens.any { it.elseExpression == null && context.get(BindingContext.EXHAUSTIVE_WHEN, it) != true }) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return statement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtExpression.getValue() = when (this) {
|
||||||
|
is KtReturnExpression -> returnedExpression
|
||||||
|
else -> null
|
||||||
|
} ?: this
|
||||||
|
|
||||||
|
private fun KtExpression.toHighlight(): PsiElement? = when (this) {
|
||||||
|
is KtReturnExpression -> returnKeyword
|
||||||
|
is KtCallExpression -> calleeExpression
|
||||||
|
is KtQualifiedExpression -> selectorExpression?.toHighlight()
|
||||||
|
is KtObjectLiteralExpression -> objectDeclaration.getObjectKeyword()
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun simplify(declaration: KtDeclarationWithBody, canDeleteTypeRef: Boolean) {
|
||||||
|
val deleteTypeHandler: (KtCallableDeclaration) -> Unit = {
|
||||||
|
it.deleteChildRange(it.colon!!, it.typeReference!!)
|
||||||
|
}
|
||||||
|
simplify(declaration, deleteTypeHandler.takeIf { canDeleteTypeRef })
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun simplify(declaration: KtDeclarationWithBody, deleteTypeHandler: ((KtCallableDeclaration) -> Unit)?) {
|
||||||
|
val block = declaration.blockExpression() ?: return
|
||||||
|
val valueStatement = block.findValueStatement() ?: return
|
||||||
|
val value = valueStatement.getValue()
|
||||||
|
|
||||||
|
if (!declaration.hasDeclaredReturnType() && declaration is KtNamedFunction && block.statements.isNotEmpty()) {
|
||||||
|
val valueType = value.analyze().getType(value)
|
||||||
|
if (valueType == null || !KotlinBuiltIns.isUnit(valueType)) {
|
||||||
|
declaration.setType(KotlinBuiltIns.FQ_NAMES.unit.asString(), shortenReferences = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val body = declaration.bodyExpression!!
|
||||||
|
|
||||||
|
val commentSaver = CommentSaver(body)
|
||||||
|
|
||||||
|
declaration.addBefore(KtPsiFactory(declaration).createEQ(), body)
|
||||||
|
val newBody = body.replaced(value)
|
||||||
|
|
||||||
|
commentSaver.restore(newBody)
|
||||||
|
|
||||||
|
if (deleteTypeHandler != null && declaration is KtCallableDeclaration) {
|
||||||
|
if (declaration.hasDeclaredReturnType() && declaration.canOmitDeclaredType(newBody, canChangeTypeToSubtype = true)) {
|
||||||
|
deleteTypeHandler(declaration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ConvertToExpressionBodyFix : LocalQuickFix {
|
||||||
|
override fun getFamilyName() = name
|
||||||
|
|
||||||
|
override fun getName() = "Convert to expression body"
|
||||||
|
|
||||||
|
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||||
|
val declaration = descriptor.psiElement as? KtDeclarationWithBody ?: return
|
||||||
|
simplify(declaration) {
|
||||||
|
val typeRef = it.typeReference!!
|
||||||
|
val colon = it.colon!!
|
||||||
|
it.findExistingEditor()?.apply {
|
||||||
|
selectionModel.setSelection(colon.startOffset, typeRef.endOffset)
|
||||||
|
caretModel.moveToOffset(typeRef.endOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jetbrains.kotlin.idea.intentions
|
|
||||||
|
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
|
||||||
import org.jetbrains.kotlin.idea.core.canOmitDeclaredType
|
|
||||||
import org.jetbrains.kotlin.idea.core.replaced
|
|
||||||
import org.jetbrains.kotlin.idea.core.setType
|
|
||||||
import org.jetbrains.kotlin.idea.util.CommentSaver
|
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
|
||||||
|
|
||||||
class ConvertToExpressionBodyIntention(
|
|
||||||
val convertEmptyToUnit: Boolean = true
|
|
||||||
) : SelfTargetingOffsetIndependentIntention<KtDeclarationWithBody>(
|
|
||||||
KtDeclarationWithBody::class.java, "Convert to expression body"
|
|
||||||
) {
|
|
||||||
override fun isApplicableTo(element: KtDeclarationWithBody): Boolean {
|
|
||||||
if (element is KtConstructor<*>) return false
|
|
||||||
val value = calcValue(element) ?: return false
|
|
||||||
return !value.anyDescendantOfType<KtReturnExpression>(
|
|
||||||
canGoInside = { it !is KtFunctionLiteral && it !is KtNamedFunction && it !is KtPropertyAccessor }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun allowCaretInsideElement(element: PsiElement) = element !is KtDeclaration
|
|
||||||
|
|
||||||
override fun applyTo(element: KtDeclarationWithBody, editor: Editor?) {
|
|
||||||
applyTo(element) {
|
|
||||||
val typeRef = it.typeReference!!
|
|
||||||
val colon = it.colon!!
|
|
||||||
editor?.apply {
|
|
||||||
selectionModel.setSelection(colon.startOffset, typeRef.endOffset)
|
|
||||||
caretModel.moveToOffset(typeRef.endOffset)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun applyTo(declaration: KtDeclarationWithBody, canDeleteTypeRef: Boolean) {
|
|
||||||
val deleteTypeHandler: (KtCallableDeclaration) -> Unit = {
|
|
||||||
it.deleteChildRange(it.colon!!, it.typeReference!!)
|
|
||||||
}
|
|
||||||
applyTo(declaration, deleteTypeHandler.takeIf { canDeleteTypeRef })
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun applyTo(declaration: KtDeclarationWithBody, deleteTypeHandler: ((KtCallableDeclaration) -> Unit)?) {
|
|
||||||
val block = declaration.blockExpression() ?: return
|
|
||||||
val value = calcValue(block)!!
|
|
||||||
|
|
||||||
if (!declaration.hasDeclaredReturnType() && declaration is KtNamedFunction && block.statements.isNotEmpty()) {
|
|
||||||
val valueType = value.analyze().getType(value)
|
|
||||||
if (valueType == null || !KotlinBuiltIns.isUnit(valueType)) {
|
|
||||||
declaration.setType(KotlinBuiltIns.FQ_NAMES.unit.asString(), shortenReferences = true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val body = declaration.bodyExpression!!
|
|
||||||
|
|
||||||
val commentSaver = CommentSaver(body)
|
|
||||||
|
|
||||||
declaration.addBefore(KtPsiFactory(declaration).createEQ(), body)
|
|
||||||
val newBody = body.replaced(value)
|
|
||||||
|
|
||||||
commentSaver.restore(newBody)
|
|
||||||
|
|
||||||
if (deleteTypeHandler != null && declaration is KtCallableDeclaration) {
|
|
||||||
if (declaration.hasDeclaredReturnType() && declaration.canOmitDeclaredType(newBody, canChangeTypeToSubtype = true)) {
|
|
||||||
deleteTypeHandler(declaration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun KtDeclarationWithBody.blockExpression() = when (this) {
|
|
||||||
is KtFunctionLiteral -> null
|
|
||||||
else -> {
|
|
||||||
val body = bodyExpression
|
|
||||||
if (!hasBlockBody() || body !is KtBlockExpression) null else body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun calcValue(declaration: KtDeclarationWithBody): KtExpression? {
|
|
||||||
val body = declaration.blockExpression() ?: return null
|
|
||||||
return calcValue(body)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun calcValue(body: KtBlockExpression): KtExpression? {
|
|
||||||
val bodyStatements = body.statements
|
|
||||||
if (bodyStatements.isEmpty()) {
|
|
||||||
return if (convertEmptyToUnit) KtPsiFactory(body).createExpression("Unit") else null
|
|
||||||
}
|
|
||||||
val statement = bodyStatements.singleOrNull() ?: return null
|
|
||||||
when (statement) {
|
|
||||||
is KtReturnExpression -> {
|
|
||||||
return statement.returnedExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: IMO this is not good code, there should be a way to detect that JetExpression does not have value
|
|
||||||
is KtDeclaration, is KtLoopExpression -> return null // is JetExpression but does not have value
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
if (statement is KtBinaryExpression && statement.operationToken in KtTokens.ALL_ASSIGNMENTS) return null // assignment does not have value
|
|
||||||
|
|
||||||
val context = statement.analyze()
|
|
||||||
val expressionType = context.getType(statement) ?: return null
|
|
||||||
val isUnit = KotlinBuiltIns.isUnit(expressionType)
|
|
||||||
if (!isUnit && !KotlinBuiltIns.isNothing(expressionType)) return null
|
|
||||||
if (isUnit) {
|
|
||||||
if (statement.hasResultingIfWithoutElse()) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
val resultingWhens = statement.resultingWhens()
|
|
||||||
if (resultingWhens.any { it.elseExpression == null && context.get(BindingContext.EXHAUSTIVE_WHEN, it) != true }) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return statement
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
|||||||
import org.jetbrains.kotlin.idea.core.replaced
|
import org.jetbrains.kotlin.idea.core.replaced
|
||||||
import org.jetbrains.kotlin.idea.core.setVisibility
|
import org.jetbrains.kotlin.idea.core.setVisibility
|
||||||
import org.jetbrains.kotlin.idea.inspections.RedundantSamConstructorInspection
|
import org.jetbrains.kotlin.idea.inspections.RedundantSamConstructorInspection
|
||||||
|
import org.jetbrains.kotlin.idea.inspections.UseExpressionBodyInspection
|
||||||
import org.jetbrains.kotlin.idea.intentions.*
|
import org.jetbrains.kotlin.idea.intentions.*
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnAsymmetricallyIntention
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnAsymmetricallyIntention
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnIntention
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnIntention
|
||||||
@@ -78,8 +79,7 @@ object J2KPostProcessingRegistrar {
|
|||||||
_processings.add(UnresolvedVariableReferenceFromInitializerToThisReferenceProcessing())
|
_processings.add(UnresolvedVariableReferenceFromInitializerToThisReferenceProcessing())
|
||||||
_processings.add(RemoveRedundantSamAdaptersProcessing())
|
_processings.add(RemoveRedundantSamAdaptersProcessing())
|
||||||
_processings.add(RemoveRedundantCastToNullableProcessing())
|
_processings.add(RemoveRedundantCastToNullableProcessing())
|
||||||
|
_processings.add(UseExpressionBodyProcessing())
|
||||||
registerIntentionBasedProcessing(ConvertToExpressionBodyIntention(convertEmptyToUnit = false)) { it is KtPropertyAccessor }
|
|
||||||
|
|
||||||
registerIntentionBasedProcessing(FoldInitializerAndIfToElvisIntention())
|
registerIntentionBasedProcessing(FoldInitializerAndIfToElvisIntention())
|
||||||
|
|
||||||
@@ -256,6 +256,19 @@ object J2KPostProcessingRegistrar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class UseExpressionBodyProcessing : J2kPostProcessing {
|
||||||
|
override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||||
|
if (element !is KtPropertyAccessor) return null
|
||||||
|
|
||||||
|
val inspection = UseExpressionBodyInspection(convertEmptyToUnit = false)
|
||||||
|
if (!inspection.isActiveFor(element)) return null
|
||||||
|
|
||||||
|
return {
|
||||||
|
inspection.simplify(element, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class RemoveRedundantCastToNullableProcessing : J2kPostProcessing {
|
private class RemoveRedundantCastToNullableProcessing : J2kPostProcessing {
|
||||||
override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? {
|
override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||||
if (element !is KtBinaryExpressionWithTypeRHS) return null
|
if (element !is KtBinaryExpressionWithTypeRHS) return null
|
||||||
|
|||||||
+4
-4
@@ -25,7 +25,7 @@ import com.intellij.refactoring.BaseRefactoringProcessor
|
|||||||
import org.jetbrains.kotlin.builtins.isFunctionType
|
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.idea.core.*
|
import org.jetbrains.kotlin.idea.core.*
|
||||||
import org.jetbrains.kotlin.idea.intentions.ConvertToExpressionBodyIntention
|
import org.jetbrains.kotlin.idea.inspections.UseExpressionBodyInspection
|
||||||
import org.jetbrains.kotlin.idea.intentions.InfixCallToOrdinaryIntention
|
import org.jetbrains.kotlin.idea.intentions.InfixCallToOrdinaryIntention
|
||||||
import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention
|
import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention
|
||||||
import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArgumentsIntention
|
import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArgumentsIntention
|
||||||
@@ -552,11 +552,11 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (generatorOptions.allowExpressionBody) {
|
if (generatorOptions.allowExpressionBody) {
|
||||||
val convertToExpressionBody = ConvertToExpressionBodyIntention()
|
|
||||||
val bodyExpression = body.statements.singleOrNull()
|
val bodyExpression = body.statements.singleOrNull()
|
||||||
val bodyOwner = body.parent as KtDeclarationWithBody
|
val bodyOwner = body.parent as KtDeclarationWithBody
|
||||||
if (bodyExpression != null && !bodyExpression.isMultiLine() && convertToExpressionBody.isApplicableTo(bodyOwner)) {
|
val useExpressionBodyInspection = UseExpressionBodyInspection()
|
||||||
convertToExpressionBody.applyTo(bodyOwner, !descriptor.returnType.isFlexible())
|
if (bodyExpression != null && !bodyExpression.isMultiLine() && useExpressionBodyInspection.isActiveFor(bodyOwner)) {
|
||||||
|
useExpressionBodyInspection.simplify(bodyOwner, !descriptor.returnType.isFlexible())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|
||||||
val x = listOf(1, 2, 3).filter <caret>{ it % 2 != 0 }.isEmpty()
|
val x = listOf(1, 2, 3).filte<caret>r { it % 2 != 0 }.isEmpty()
|
||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|
||||||
val x = listOf(1, 2, 3).filter <caret>{ it % 2 != 0 }.isNotEmpty()
|
val x = listOf(1, 2, 3).filte<caret>r { it % 2 != 0 }.isNotEmpty()
|
||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|
||||||
val x = listOf("1", "").filter <caret>{ element -> element.isNotEmpty() }.last()
|
val x = listOf("1", "").filte<caret>r { element -> element.isNotEmpty() }.last()
|
||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|
||||||
val x = "5abc".filter { it.isDigit() }.<caret>singleOrNull()
|
val x = "5abc".<caret>filter { it.isDigit() }.singleOrNull()
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
val x = listOf(1, 2, 3).map { "$it*$it" }.<caret>joinTo(buffer = sb, prefix = "= ", separator = " + ")
|
val x = listOf(1, 2, 3).<caret>map { "$it*$it" }.joinTo(buffer = sb, prefix = "= ", separator = " + ")
|
||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|
||||||
val x = listOf(1, 2, 3).map { "$it*$it" }.<caret>joinToString(prefix = "= ", separator = " + ")
|
val x = listOf(1, 2, 3).<caret>map { "$it*$it" }.joinToString(prefix = "= ", separator = " + ")
|
||||||
Vendored
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|
||||||
val x = listOf(1, 2, 3).map(Int::toString).<caret>joinToString(prefix = "= ", separator = " + ")
|
val x = listOf(1, 2, 3).<caret>map(Int::toString).joinToString(prefix = "= ", separator = " + ")
|
||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|
||||||
val x = listOf(1, 0, 2).map { if (it != 0) it else null }.<caret>filterNotNull()
|
val x = listOf(1, 0, 2).<caret>map { if (it != 0) it else null }.filterNotNull()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fun foo(vararg x: String) {}
|
fun foo(vararg x: String) {}
|
||||||
|
|
||||||
fun bar() {
|
fun bar() {
|
||||||
foo(*arrayOf<caret>("abc"))
|
foo(*<caret>arrayOf("abc"))
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fun foo(vararg x: Boolean) {}
|
fun foo(vararg x: Boolean) {}
|
||||||
|
|
||||||
fun bar() {
|
fun bar() {
|
||||||
foo(*booleanArrayOf<caret>(true, true))
|
foo(*<caret>booleanArrayOf(true, true))
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fun foo(vararg x: Byte) {}
|
fun foo(vararg x: Byte) {}
|
||||||
|
|
||||||
fun bar() {
|
fun bar() {
|
||||||
foo(*byteArrayOf<caret>(1))
|
foo(*<caret>byteArrayOf(1))
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fun foo(vararg x: Char) {}
|
fun foo(vararg x: Char) {}
|
||||||
|
|
||||||
fun bar() {
|
fun bar() {
|
||||||
foo(*charArrayOf<caret>('a', 'b'))
|
foo(*<caret>charArrayOf('a', 'b'))
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fun foo(vararg x: Double) {}
|
fun foo(vararg x: Double) {}
|
||||||
|
|
||||||
fun bar() {
|
fun bar() {
|
||||||
foo(*doubleArrayOf<caret>(1.0))
|
foo(*<caret>doubleArrayOf(1.0))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fun foo(vararg x: String) {}
|
fun foo(vararg x: String) {}
|
||||||
|
|
||||||
fun bar() {
|
fun bar() {
|
||||||
foo(*emptyArray<caret><String>())
|
foo(*<caret>emptyArray<String>())
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fun foo(vararg x: Float) {}
|
fun foo(vararg x: Float) {}
|
||||||
|
|
||||||
fun bar() {
|
fun bar() {
|
||||||
foo(*floatArrayOf<caret>(1.0f))
|
foo(<caret>*floatArrayOf(1.0f))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fun foo(vararg x: Int) {}
|
fun foo(vararg x: Int) {}
|
||||||
|
|
||||||
fun bar() {
|
fun bar() {
|
||||||
foo(*intArrayOf<caret>(1))
|
foo(<caret>*intArrayOf(1))
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fun foo(vararg x: Long) {}
|
fun foo(vararg x: Long) {}
|
||||||
|
|
||||||
fun bar() {
|
fun bar() {
|
||||||
foo(*longArrayOf<caret>(1L))
|
foo(<caret>*longArrayOf(1L))
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fun foo(vararg x: String) {}
|
fun foo(vararg x: String) {}
|
||||||
|
|
||||||
fun bar() {
|
fun bar() {
|
||||||
foo(*arrayOf<caret>("abc", "def"))
|
foo(<caret>*arrayOf("abc", "def"))
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fun foo(vararg x: String) {}
|
fun foo(vararg x: String) {}
|
||||||
|
|
||||||
fun bar() {
|
fun bar() {
|
||||||
foo(*arrayOf<caret>("abc", "def"), "ghi", "jkl")
|
foo(<caret>*arrayOf("abc", "def"), "ghi", "jkl")
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fun foo(vararg x: String) {}
|
fun foo(vararg x: String) {}
|
||||||
|
|
||||||
fun bar() {
|
fun bar() {
|
||||||
foo(*arrayOf<caret>(elements = "abc"))
|
foo(<caret>*arrayOf(elements = "abc"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fun foo(vararg x: String) {}
|
fun foo(vararg x: String) {}
|
||||||
|
|
||||||
fun bar() {
|
fun bar() {
|
||||||
foo(*arrayOf<caret>())
|
foo(<caret>*arrayOf())
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fun foo(vararg x: Short) {}
|
fun foo(vararg x: Short) {}
|
||||||
|
|
||||||
fun bar() {
|
fun bar() {
|
||||||
foo(*shortArrayOf<caret>(1))
|
foo(<caret>*shortArrayOf(1))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.kotlin.idea.inspections.UseExpressionBodyInspection
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
var x = 0
|
var x = 0
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
var a = 1
|
var a = 1
|
||||||
var b = 2
|
var b = 2
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
class C {
|
class C {
|
||||||
constructor()<caret> {}
|
constructor()<caret> {}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
<caret>val v = 1
|
<caret>val v = 1
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
fun nullable() {}
|
fun nullable() {}
|
||||||
|
|
||||||
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
enum class AccessMode { READ, WRITE, RW }
|
enum class AccessMode { READ, WRITE, RW }
|
||||||
fun whenExpr(access: AccessMode) {
|
fun whenExpr(access: AccessMode) {
|
||||||
<caret>println("result") ?: when (access) {
|
<caret>println("result") ?: when (access) {
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|
||||||
fun foo(): List<String> {
|
fun foo(): List<String> {
|
||||||
return emptyList()<caret>
|
ret<caret>urn emptyList()
|
||||||
}
|
}
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo(): List<String> = emptyList()
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
fun foo(p: Boolean): String {
|
fun foo(p: Boolean): String {
|
||||||
<caret>return bar() ?: return "a"
|
<caret>return bar() ?: return "a"
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
fun foo(p: Boolean): String {
|
fun foo(p: Boolean): String {
|
||||||
if (p) {
|
if (p) {
|
||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
fun foo() = <caret>"abc"
|
fun foo() = <caret>"abc"
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun foo(): String {
|
||||||
|
<caret>return "abc"
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
<caret>bar()
|
<caret>bar()
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
fun foo(handler: () -> Unit) { }
|
fun foo(handler: () -> Unit) { }
|
||||||
|
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
val foo: String
|
||||||
|
get() {
|
||||||
|
<caret>return "abc"
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
fun bar() {}
|
fun bar() {}
|
||||||
|
|
||||||
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
enum class AccessMode { READ, WRITE, RW }
|
enum class AccessMode { READ, WRITE, RW }
|
||||||
fun whenExpr(mode: Boolean, access: AccessMode) {
|
fun whenExpr(mode: Boolean, access: AccessMode) {
|
||||||
<caret>if (mode) {
|
<caret>if (mode) {
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
fun foo(): String {
|
fun foo(): String {
|
||||||
val v = 1
|
val v = 1
|
||||||
+2
-2
@@ -3,7 +3,7 @@ open class A {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class B : A() {
|
class B : A() {
|
||||||
public override fun <caret>foo(): String {
|
public override fun foo(): String {
|
||||||
return "abc"
|
<caret>return "abc"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
<caret>return
|
<caret>return
|
||||||
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
enum class AccessMode { READ, WRITE, RW }
|
enum class AccessMode { READ, WRITE, RW }
|
||||||
fun whenExpr(mode: Boolean, access: AccessMode) {
|
fun whenExpr(mode: Boolean, access: AccessMode) {
|
||||||
<caret>when (access) {
|
<caret>when (access) {
|
||||||
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
enum class AccessMode { READ, WRITE, RW }
|
enum class AccessMode { READ, WRITE, RW }
|
||||||
fun whenExpr(access: AccessMode) {
|
fun whenExpr(access: AccessMode) {
|
||||||
<caret>when (access) {
|
<caret>when (access) {
|
||||||
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
enum class AccessMode { READ, WRITE, RW }
|
enum class AccessMode { READ, WRITE, RW }
|
||||||
fun whenExpr(mode: Boolean, access: AccessMode) {
|
fun whenExpr(mode: Boolean, access: AccessMode) {
|
||||||
<caret>when (access) {
|
<caret>when (access) {
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// IS_APPLICABLE: false
|
// PROBLEM: none
|
||||||
|
|
||||||
fun foo(p: Boolean): String {
|
fun foo(p: Boolean): String {
|
||||||
<caret>while(true) { }
|
<caret>while(true) { }
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// HIGHLIGHT: INFORMATION
|
||||||
|
|
||||||
|
fun abs(x: Int): Int {
|
||||||
|
// No highlighting here
|
||||||
|
<caret>return if (x > 0) {
|
||||||
|
x
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
-x
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// HIGHLIGHT: INFORMATION
|
||||||
|
|
||||||
|
fun abs(x: Int): Int = // No highlighting here
|
||||||
|
if (x > 0) {
|
||||||
|
x
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
-x
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user