Split ReplaceSingleLineLetInspection to SimpleRedundantLet & ComplexRedundantLet
Relates to #KT-32010
This commit is contained in:
@@ -2233,12 +2233,21 @@
|
|||||||
language="kotlin"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ReplaceSingleLineLetInspection"
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.SimpleRedundantLetInspection"
|
||||||
displayName="Replace single line .let"
|
displayName="Redundant `let` call"
|
||||||
groupPath="Kotlin"
|
groupPath="Kotlin"
|
||||||
groupName="Redundant constructs"
|
groupName="Redundant constructs"
|
||||||
enabledByDefault="true"
|
enabledByDefault="true"
|
||||||
level="INFORMATION"
|
level="WARNING"
|
||||||
|
language="kotlin"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ComplexRedundantLetInspection"
|
||||||
|
displayName="Redundant `let` call could be removed"
|
||||||
|
groupPath="Kotlin"
|
||||||
|
groupName="Redundant constructs"
|
||||||
|
enabledByDefault="true"
|
||||||
|
level="INFO"
|
||||||
language="kotlin"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
This inspection detects a redundant <b>let</b> when it includes only one call with lambda parameter.
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+56
-27
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.idea.inspections
|
|||||||
import com.intellij.codeInspection.ProblemHighlightType
|
import com.intellij.codeInspection.ProblemHighlightType
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||||
@@ -26,44 +27,36 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
|||||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
|
|
||||||
class ReplaceSingleLineLetInspection : AbstractApplicabilityBasedInspection<KtCallExpression>(
|
abstract class RedundantLetInspection : AbstractApplicabilityBasedInspection<KtCallExpression>(
|
||||||
KtCallExpression::class.java
|
KtCallExpression::class.java
|
||||||
) {
|
) {
|
||||||
override fun inspectionText(element: KtCallExpression) = "Replace single line .let"
|
override fun inspectionText(element: KtCallExpression) = "Redundant `let` call could be removed"
|
||||||
|
|
||||||
override fun inspectionHighlightRangeInElement(element: KtCallExpression) = element.calleeExpression?.textRangeIn(element)
|
final override fun inspectionHighlightRangeInElement(element: KtCallExpression) = element.calleeExpression?.textRangeIn(element)
|
||||||
|
|
||||||
override fun inspectionHighlightType(element: KtCallExpression): ProblemHighlightType = if (isSingleLine(element))
|
final override val defaultFixText = "Remove `let` call"
|
||||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING
|
|
||||||
else
|
|
||||||
ProblemHighlightType.INFORMATION
|
|
||||||
|
|
||||||
override val defaultFixText = "Remove redundant '.let' call"
|
final override fun isApplicable(element: KtCallExpression): Boolean {
|
||||||
|
|
||||||
override fun isApplicable(element: KtCallExpression): Boolean {
|
|
||||||
if (!element.isLetMethodCall()) return false
|
if (!element.isLetMethodCall()) return false
|
||||||
val lambdaExpression = element.lambdaArguments.firstOrNull()?.getLambdaExpression() ?: return false
|
val lambdaExpression = element.lambdaArguments.firstOrNull()?.getLambdaExpression() ?: return false
|
||||||
val parameterName = lambdaExpression.getParameterName() ?: return false
|
val parameterName = lambdaExpression.getParameterName() ?: return false
|
||||||
|
|
||||||
return when (val bodyExpression = lambdaExpression.bodyExpression?.children?.singleOrNull() ?: return false) {
|
return myIsApplicable(
|
||||||
is KtBinaryExpression ->
|
element,
|
||||||
element.parent !is KtSafeQualifiedExpression && bodyExpression.isApplicable(parameterName)
|
lambdaExpression.bodyExpression?.children?.singleOrNull() ?: return false,
|
||||||
is KtDotQualifiedExpression ->
|
lambdaExpression,
|
||||||
bodyExpression.isApplicable(parameterName)
|
parameterName
|
||||||
is KtCallExpression ->
|
)
|
||||||
if (element.parent is KtSafeQualifiedExpression) {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
val count = lambdaExpression.functionLiteral.valueParameterReferences(bodyExpression).count()
|
|
||||||
val destructuringDeclaration = lambdaExpression.functionLiteral.valueParameters.firstOrNull()?.destructuringDeclaration
|
|
||||||
count == 0 || (count == 1 && destructuringDeclaration == null)
|
|
||||||
}
|
|
||||||
else ->
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyTo(element: KtCallExpression, project: Project, editor: Editor?) {
|
protected abstract fun myIsApplicable(
|
||||||
|
element: KtCallExpression,
|
||||||
|
bodyExpression: PsiElement,
|
||||||
|
lambdaExpression: KtLambdaExpression,
|
||||||
|
parameterName: String
|
||||||
|
): Boolean
|
||||||
|
|
||||||
|
final override fun applyTo(element: KtCallExpression, project: Project, editor: Editor?) {
|
||||||
val lambdaExpression = element.lambdaArguments.firstOrNull()?.getLambdaExpression() ?: return
|
val lambdaExpression = element.lambdaArguments.firstOrNull()?.getLambdaExpression() ?: return
|
||||||
when (val bodyExpression = lambdaExpression.bodyExpression?.children?.singleOrNull() ?: return) {
|
when (val bodyExpression = lambdaExpression.bodyExpression?.children?.singleOrNull() ?: return) {
|
||||||
is KtDotQualifiedExpression -> bodyExpression.applyTo(element)
|
is KtDotQualifiedExpression -> bodyExpression.applyTo(element)
|
||||||
@@ -73,6 +66,42 @@ class ReplaceSingleLineLetInspection : AbstractApplicabilityBasedInspection<KtCa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SimpleRedundantLetInspection : RedundantLetInspection() {
|
||||||
|
override fun myIsApplicable(
|
||||||
|
element: KtCallExpression,
|
||||||
|
bodyExpression: PsiElement,
|
||||||
|
lambdaExpression: KtLambdaExpression,
|
||||||
|
parameterName: String
|
||||||
|
): Boolean = if (bodyExpression is KtDotQualifiedExpression) bodyExpression.isApplicable(parameterName) else false
|
||||||
|
}
|
||||||
|
|
||||||
|
class ComplexRedundantLetInspection : RedundantLetInspection() {
|
||||||
|
override fun myIsApplicable(
|
||||||
|
element: KtCallExpression,
|
||||||
|
bodyExpression: PsiElement,
|
||||||
|
lambdaExpression: KtLambdaExpression,
|
||||||
|
parameterName: String
|
||||||
|
): Boolean = when (bodyExpression) {
|
||||||
|
is KtBinaryExpression ->
|
||||||
|
element.parent !is KtSafeQualifiedExpression && bodyExpression.isApplicable(parameterName)
|
||||||
|
is KtCallExpression ->
|
||||||
|
if (element.parent is KtSafeQualifiedExpression) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
val count = lambdaExpression.functionLiteral.valueParameterReferences(bodyExpression).count()
|
||||||
|
val destructuringDeclaration = lambdaExpression.functionLiteral.valueParameters.firstOrNull()?.destructuringDeclaration
|
||||||
|
count == 0 || (count == 1 && destructuringDeclaration == null)
|
||||||
|
}
|
||||||
|
else ->
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun inspectionHighlightType(element: KtCallExpression): ProblemHighlightType = if (isSingleLine(element))
|
||||||
|
ProblemHighlightType.GENERIC_ERROR_OR_WARNING
|
||||||
|
else
|
||||||
|
ProblemHighlightType.INFORMATION
|
||||||
|
}
|
||||||
|
|
||||||
private fun KtBinaryExpression.applyTo(element: KtCallExpression) {
|
private fun KtBinaryExpression.applyTo(element: KtCallExpression) {
|
||||||
val left = left ?: return
|
val left = left ?: return
|
||||||
val factory = KtPsiFactory(element.project)
|
val factory = KtPsiFactory(element.project)
|
||||||
+4
-4
@@ -21,7 +21,7 @@ import com.intellij.openapi.editor.Editor
|
|||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
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.inspections.ReplaceSingleLineLetInspection
|
import org.jetbrains.kotlin.idea.inspections.ComplexRedundantLetInspection
|
||||||
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
|
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.convertToIfNotNullExpression
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.convertToIfNotNullExpression
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.introduceValueForCondition
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.introduceValueForCondition
|
||||||
@@ -87,9 +87,9 @@ class SafeAccessToIfThenIntention : SelfTargetingRangeIntention<KtSafeQualifiedE
|
|||||||
private fun KtIfExpression.removeRedundantLetCallIfPossible(editor: Editor?): Boolean {
|
private fun KtIfExpression.removeRedundantLetCallIfPossible(editor: Editor?): Boolean {
|
||||||
val callExpression = (then as? KtQualifiedExpression)?.callExpression ?: return false
|
val callExpression = (then as? KtQualifiedExpression)?.callExpression ?: return false
|
||||||
if (callExpression.calleeExpression?.text != "let") return false
|
if (callExpression.calleeExpression?.text != "let") return false
|
||||||
val replaceSingleLineLetInspection = ReplaceSingleLineLetInspection()
|
val redundantLetInspection = ComplexRedundantLetInspection()
|
||||||
if (!replaceSingleLineLetInspection.isApplicable(callExpression)) return false
|
if (!redundantLetInspection.isApplicable(callExpression)) return false
|
||||||
replaceSingleLineLetInspection.applyTo(callExpression, project, editor)
|
redundantLetInspection.applyTo(callExpression, project, editor)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.kotlin.idea.inspections.ComplexRedundantLetInspection
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// WITH_RUNTIME
|
||||||
|
class Foo(val bar: () -> Int)
|
||||||
|
|
||||||
|
fun bar(foo: Foo?) {
|
||||||
|
foo?.<caret>let { it.bar.invoke() }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// WITH_RUNTIME
|
||||||
|
class A {
|
||||||
|
operator fun invoke() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(a: A) {
|
||||||
|
a.<caret>let { b -> b.invoke() }
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val foo: String? = null
|
||||||
|
foo?.let<caret> {
|
||||||
|
it.length
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val foo: String = ""
|
||||||
|
foo.let<caret> {
|
||||||
|
it.length
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val foo: String? = null
|
||||||
|
foo?.let<caret> {
|
||||||
|
it.to("")
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val foo: String? = null
|
||||||
|
foo?.let<caret> {
|
||||||
|
it.hashCode().hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val foo: String? = null
|
||||||
|
foo?.let<caret> {
|
||||||
|
text ->
|
||||||
|
text.length
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun String.test(): Int {
|
||||||
|
return let<caret> {
|
||||||
|
it.length
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val foo: String? = null
|
||||||
|
foo?.toString()?.let<caret> {
|
||||||
|
it.to("")
|
||||||
|
}?.let {
|
||||||
|
it.to("")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun test(s: String?): Int? {
|
||||||
|
return s?.let<caret> {
|
||||||
|
it.length
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
|
||||||
|
fun Int.foo(): Int {
|
||||||
|
return <caret>let { it.hashCode().hashCode() }
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
|
||||||
|
fun Int.foo(): Int {
|
||||||
|
return <caret>let { it.hashCode() }
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
org.jetbrains.kotlin.idea.inspections.ReplaceSingleLineLetInspection
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.kotlin.idea.inspections.SimpleRedundantLetInspection
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user