Split ReplaceSingleLineLetInspection to SimpleRedundantLet & ComplexRedundantLet

Relates to #KT-32010
This commit is contained in:
Dmitry Gridin
2019-06-24 18:56:43 +07:00
parent 8e0bbe7d4a
commit b97aaf0f71
183 changed files with 1361 additions and 398 deletions
+12 -3
View File
@@ -2233,12 +2233,21 @@
language="kotlin"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ReplaceSingleLineLetInspection"
displayName="Replace single line .let"
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.SimpleRedundantLetInspection"
displayName="Redundant `let` call"
groupPath="Kotlin"
groupName="Redundant constructs"
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"
/>
@@ -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>
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.idea.caches.resolve.analyze
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.lazy.BodyResolveMode
class ReplaceSingleLineLetInspection : AbstractApplicabilityBasedInspection<KtCallExpression>(
abstract class RedundantLetInspection : AbstractApplicabilityBasedInspection<KtCallExpression>(
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))
ProblemHighlightType.GENERIC_ERROR_OR_WARNING
else
ProblemHighlightType.INFORMATION
final override val defaultFixText = "Remove `let` call"
override val defaultFixText = "Remove redundant '.let' call"
override fun isApplicable(element: KtCallExpression): Boolean {
final override fun isApplicable(element: KtCallExpression): Boolean {
if (!element.isLetMethodCall()) return false
val lambdaExpression = element.lambdaArguments.firstOrNull()?.getLambdaExpression() ?: return false
val parameterName = lambdaExpression.getParameterName() ?: return false
return when (val bodyExpression = lambdaExpression.bodyExpression?.children?.singleOrNull() ?: return false) {
is KtBinaryExpression ->
element.parent !is KtSafeQualifiedExpression && bodyExpression.isApplicable(parameterName)
is KtDotQualifiedExpression ->
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
}
return myIsApplicable(
element,
lambdaExpression.bodyExpression?.children?.singleOrNull() ?: return false,
lambdaExpression,
parameterName
)
}
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
when (val bodyExpression = lambdaExpression.bodyExpression?.children?.singleOrNull() ?: return) {
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) {
val left = left ?: return
val factory = KtPsiFactory(element.project)
@@ -21,7 +21,7 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.idea.caches.resolve.analyze
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.branchedTransformations.convertToIfNotNullExpression
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.introduceValueForCondition
@@ -87,9 +87,9 @@ class SafeAccessToIfThenIntention : SelfTargetingRangeIntention<KtSafeQualifiedE
private fun KtIfExpression.removeRedundantLetCallIfPossible(editor: Editor?): Boolean {
val callExpression = (then as? KtQualifiedExpression)?.callExpression ?: return false
if (callExpression.calleeExpression?.text != "let") return false
val replaceSingleLineLetInspection = ReplaceSingleLineLetInspection()
if (!replaceSingleLineLetInspection.isApplicable(callExpression)) return false
replaceSingleLineLetInspection.applyTo(callExpression, project, editor)
val redundantLetInspection = ComplexRedundantLetInspection()
if (!redundantLetInspection.isApplicable(callExpression)) return false
redundantLetInspection.applyTo(callExpression, project, editor)
return true
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.inspections.ComplexRedundantLetInspection
@@ -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("")
}
}
@@ -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