Change API in AbstractApplicabilityBasedInspection

Replace all inspectionTarget with inspectionHighlightRangeInElement & change type of element in applyTo from PsiElement to TElement
This commit is contained in:
Dmitry Gridin
2019-06-06 13:33:52 +07:00
parent 1ad7a50087
commit 4f8c0653c5
30 changed files with 164 additions and 212 deletions
@@ -20,7 +20,6 @@ import com.intellij.codeInspection.*
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.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtVisitorVoid import org.jetbrains.kotlin.psi.KtVisitorVoid
@@ -44,7 +43,7 @@ abstract class AbstractApplicabilityBasedInspection<TElement : KtElement>(
if (!isApplicable(element)) return if (!isApplicable(element)) return
holder.registerProblemWithoutOfflineInformation( holder.registerProblemWithoutOfflineInformation(
inspectionTarget(element), element,
inspectionText(element), inspectionText(element),
isOnTheFly, isOnTheFly,
inspectionHighlightType(element), inspectionHighlightType(element),
@@ -55,8 +54,6 @@ abstract class AbstractApplicabilityBasedInspection<TElement : KtElement>(
open fun inspectionHighlightRangeInElement(element: TElement): TextRange? = null open fun inspectionHighlightRangeInElement(element: TElement): TextRange? = null
open fun inspectionTarget(element: TElement): PsiElement = element
open fun inspectionHighlightType(element: TElement): ProblemHighlightType = ProblemHighlightType.GENERIC_ERROR_OR_WARNING open fun inspectionHighlightType(element: TElement): ProblemHighlightType = ProblemHighlightType.GENERIC_ERROR_OR_WARNING
abstract fun inspectionText(element: TElement): String abstract fun inspectionText(element: TElement): String
@@ -67,7 +64,7 @@ abstract class AbstractApplicabilityBasedInspection<TElement : KtElement>(
abstract fun isApplicable(element: TElement): Boolean abstract fun isApplicable(element: TElement): Boolean
abstract fun applyTo(element: PsiElement, project: Project = element.project, editor: Editor? = null) abstract fun applyTo(element: TElement, project: Project = element.project, editor: Editor? = null)
open val startFixInWriteAction = true open val startFixInWriteAction = true
@@ -75,7 +72,8 @@ abstract class AbstractApplicabilityBasedInspection<TElement : KtElement>(
override fun startInWriteAction() = startFixInWriteAction override fun startInWriteAction() = startFixInWriteAction
override fun applyFix(project: Project, descriptor: ProblemDescriptor) { override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.psiElement @Suppress("UNCHECKED_CAST")
val element = descriptor.psiElement as TElement
applyTo(element, project, element.findExistingEditor()) applyTo(element, project, element.findExistingEditor())
} }
@@ -7,17 +7,15 @@ package org.jetbrains.kotlin.idea.inspections
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.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.refactoring.withExpectedActuals import org.jetbrains.kotlin.idea.refactoring.withExpectedActuals
import org.jetbrains.kotlin.idea.util.nameIdentifierTextRangeInThis
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.util.OperatorChecks import org.jetbrains.kotlin.util.OperatorChecks
class AddOperatorModifierInspection : AbstractApplicabilityBasedInspection<KtNamedFunction>(KtNamedFunction::class.java) { class AddOperatorModifierInspection : AbstractApplicabilityBasedInspection<KtNamedFunction>(KtNamedFunction::class.java) {
override fun inspectionHighlightRangeInElement(element: KtNamedFunction) = override fun inspectionHighlightRangeInElement(element: KtNamedFunction) = element.nameIdentifierTextRangeInThis()
element.nameIdentifier?.textRange?.shiftLeft(element.startOffset)
override fun inspectionText(element: KtNamedFunction) = "Function should have 'operator' modifier" override fun inspectionText(element: KtNamedFunction) = "Function should have 'operator' modifier"
@@ -29,8 +27,8 @@ class AddOperatorModifierInspection : AbstractApplicabilityBasedInspection<KtNam
return !functionDescriptor.isOperator && OperatorChecks.check(functionDescriptor).isSuccess return !functionDescriptor.isOperator && OperatorChecks.check(functionDescriptor).isSuccess
} }
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtNamedFunction, project: Project, editor: Editor?) {
for (declaration in (element as KtNamedFunction).withExpectedActuals()) { for (declaration in element.withExpectedActuals()) {
declaration.addModifier(KtTokens.OPERATOR_KEYWORD) declaration.addModifier(KtTokens.OPERATOR_KEYWORD)
} }
} }
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.idea.inspections
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 com.intellij.psi.codeStyle.CodeStyleManager import com.intellij.psi.codeStyle.CodeStyleManager
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
@@ -22,8 +21,8 @@ import org.jetbrains.kotlin.idea.core.moveCaret
import org.jetbrains.kotlin.idea.core.unblockDocument import org.jetbrains.kotlin.idea.core.unblockDocument
import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.util.ImportInsertHelper import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.idea.util.textRangeIn
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -38,14 +37,13 @@ import java.util.*
class DeprecatedCallableAddReplaceWithInspection : AbstractApplicabilityBasedInspection<KtCallableDeclaration>( class DeprecatedCallableAddReplaceWithInspection : AbstractApplicabilityBasedInspection<KtCallableDeclaration>(
KtCallableDeclaration::class.java KtCallableDeclaration::class.java
) { ) {
override fun inspectionText(element: KtCallableDeclaration) = override fun inspectionText(element: KtCallableDeclaration) = "@Deprecated annotation without 'replaceWith' argument"
"@Deprecated annotation without 'replaceWith' argument"
override fun inspectionTarget(element: KtCallableDeclaration): KtAnnotationEntry = override fun inspectionHighlightRangeInElement(element: KtCallableDeclaration) = element.annotationEntries.first {
element.annotationEntries.first { it.shortName == DEPRECATED_NAME } it.shortName == DEPRECATED_NAME
}.textRangeIn(element)
override val defaultFixText = override val defaultFixText = "Add 'replaceWith' argument to specify replacement pattern"
"Add 'replaceWith' argument to specify replacement pattern"
private class ReplaceWith(val expression: String, vararg val imports: String) private class ReplaceWith(val expression: String, vararg val imports: String)
@@ -54,14 +52,13 @@ class DeprecatedCallableAddReplaceWithInspection : AbstractApplicabilityBasedIns
return element.suggestReplaceWith() != null return element.suggestReplaceWith() != null
} }
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtCallableDeclaration, project: Project, editor: Editor?) {
val declaration = element.getParentOfType<KtCallableDeclaration>(strict = true) ?: return val replaceWith = element.suggestReplaceWith()!!
val replaceWith = declaration.suggestReplaceWith()!!
assert('\n' !in replaceWith.expression && '\r' !in replaceWith.expression) { "Formatted expression text should not contain \\n or \\r" } assert('\n' !in replaceWith.expression && '\r' !in replaceWith.expression) { "Formatted expression text should not contain \\n or \\r" }
val annotationEntry = declaration.deprecatedAnnotationWithNoReplaceWith()!! val annotationEntry = element.deprecatedAnnotationWithNoReplaceWith()!!
val psiFactory = KtPsiFactory(declaration) val psiFactory = KtPsiFactory(element)
var escapedText = replaceWith.expression.replace("\\", "\\\\").replace("\"", "\\\"") var escapedText = replaceWith.expression.replace("\\", "\\\\").replace("\"", "\\\"")
@@ -212,8 +209,8 @@ class DeprecatedCallableAddReplaceWithInspection : AbstractApplicabilityBasedIns
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) { override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
val bindingContext = expression.analyze() val bindingContext = expression.analyze()
val target = bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, expression] val target = bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, expression]
?: bindingContext[BindingContext.REFERENCE_TARGET, expression] ?: bindingContext[BindingContext.REFERENCE_TARGET, expression]
?: return ?: return
if (target.isExtension || expression.getReceiverExpression() == null) { if (target.isExtension || expression.getReceiverExpression() == null) {
val fqName = target.importableFqName ?: return val fqName = target.importableFqName ?: return
if (!importHelper.isImportedWithDefault(ImportPath(fqName, false), file) if (!importHelper.isImportedWithDefault(ImportPath(fqName, false), file)
@@ -22,14 +22,13 @@ import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.core.setType import org.jetbrains.kotlin.idea.core.setType
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.elvisPattern import org.jetbrains.kotlin.idea.intentions.branchedTransformations.elvisPattern
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.expressionComparedToNull import org.jetbrains.kotlin.idea.intentions.branchedTransformations.expressionComparedToNull
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.fromIfKeywordToRightParenthesisTextRangeInThis
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.shouldBeTransformed import org.jetbrains.kotlin.idea.intentions.branchedTransformations.shouldBeTransformed
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.textRange
import org.jetbrains.kotlin.idea.util.CommentSaver import org.jetbrains.kotlin.idea.util.CommentSaver
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange
import org.jetbrains.kotlin.psi.psiUtil.siblings import org.jetbrains.kotlin.psi.psiUtil.siblings
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.isError import org.jetbrains.kotlin.types.isError
@@ -43,16 +42,15 @@ class FoldInitializerAndIfToElvisInspection : AbstractApplicabilityBasedInspecti
override val defaultFixText: String = "Replace 'if' with elvis operator" override val defaultFixText: String = "Replace 'if' with elvis operator"
override fun inspectionHighlightRangeInElement(element: KtIfExpression): TextRange? = element.textRange().shiftLeft(element.startOffset) override fun inspectionHighlightRangeInElement(element: KtIfExpression) = element.fromIfKeywordToRightParenthesisTextRangeInThis()
override fun inspectionHighlightType(element: KtIfExpression): ProblemHighlightType = override fun inspectionHighlightType(element: KtIfExpression): ProblemHighlightType =
if (element.shouldBeTransformed()) ProblemHighlightType.GENERIC_ERROR_OR_WARNING else ProblemHighlightType.INFORMATION if (element.shouldBeTransformed()) ProblemHighlightType.GENERIC_ERROR_OR_WARNING else ProblemHighlightType.INFORMATION
override fun isApplicable(element: KtIfExpression): Boolean = Companion.isApplicable(element) override fun isApplicable(element: KtIfExpression): Boolean = Companion.isApplicable(element)
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtIfExpression, project: Project, editor: Editor?) {
val newElvis = Companion.applyTo(element as KtIfExpression) Companion.applyTo(element).right?.textOffset?.let { editor?.caretModel?.moveToOffset(it) }
editor?.caretModel?.moveToOffset(newElvis.right!!.textOffset)
} }
companion object { companion object {
@@ -62,7 +60,7 @@ class FoldInitializerAndIfToElvisInspection : AbstractApplicabilityBasedInspecti
val type = data.ifNullExpression.analyze().getType(data.ifNullExpression) ?: return null val type = data.ifNullExpression.analyze().getType(data.ifNullExpression) ?: return null
if (!type.isNothing()) return null if (!type.isNothing()) return null
return element.textRange() return element.fromIfKeywordToRightParenthesisTextRangeInThis()
} }
fun isApplicable(element: KtIfExpression): Boolean = applicabilityRange(element) != null fun isApplicable(element: KtIfExpression): Boolean = applicabilityRange(element) != null
@@ -5,20 +5,19 @@
package org.jetbrains.kotlin.idea.inspections package org.jetbrains.kotlin.idea.inspections
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 com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.getLastLambdaExpression import org.jetbrains.kotlin.idea.core.getLastLambdaExpression
import org.jetbrains.kotlin.idea.inspections.collections.isMap import org.jetbrains.kotlin.idea.inspections.collections.isMap
import org.jetbrains.kotlin.idea.intentions.callExpression import org.jetbrains.kotlin.idea.intentions.callExpression
import org.jetbrains.kotlin.idea.util.calleeTextRangeInThis
import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtLambdaExpression import org.jetbrains.kotlin.psi.KtLambdaExpression
import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.callUtil.getType import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -41,16 +40,14 @@ class JavaMapForEachInspection : AbstractApplicabilityBasedInspection<KtDotQuali
return resolvedCall.isResolvedWithSamConversions() return resolvedCall.isResolvedWithSamConversions()
} }
override fun inspectionTarget(element: KtDotQualifiedExpression) = element.callExpression?.calleeExpression ?: element override fun inspectionHighlightRangeInElement(element: KtDotQualifiedExpression): TextRange? = element.calleeTextRangeInThis()
override fun inspectionText(element: KtDotQualifiedExpression) = "Java Map.forEach method call should be replaced with Kotlin's forEach" override fun inspectionText(element: KtDotQualifiedExpression) = "Java Map.forEach method call should be replaced with Kotlin's forEach"
override fun inspectionHighlightType(element: KtDotQualifiedExpression) = ProblemHighlightType.GENERIC_ERROR_OR_WARNING
override val defaultFixText = "Replace with Kotlin's forEach" override val defaultFixText = "Replace with Kotlin's forEach"
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtDotQualifiedExpression, project: Project, editor: Editor?) {
val call = element.getStrictParentOfType<KtCallExpression>() ?: return val call = element.callExpression ?: return
val lambda = call.lambda() ?: return val lambda = call.lambda() ?: return
val valueParameters = lambda.valueParameters val valueParameters = lambda.valueParameters
lambda.functionLiteral.valueParameterList?.replace( lambda.functionLiteral.valueParameterList?.replace(
@@ -8,14 +8,12 @@ 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.idea.core.canMoveLambdaOutsideParentheses import org.jetbrains.kotlin.idea.core.canMoveLambdaOutsideParentheses
import org.jetbrains.kotlin.idea.core.getLastLambdaExpression import org.jetbrains.kotlin.idea.core.getLastLambdaExpression
import org.jetbrains.kotlin.idea.core.moveFunctionLiteralOutsideParentheses import org.jetbrains.kotlin.idea.core.moveFunctionLiteralOutsideParentheses
import org.jetbrains.kotlin.idea.util.textRangeIn
import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtValueArgument import org.jetbrains.kotlin.psi.KtValueArgument
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.unpackFunctionLiteral import org.jetbrains.kotlin.psi.unpackFunctionLiteral
@@ -36,19 +34,17 @@ class MoveLambdaOutsideParenthesesInspection : AbstractApplicabilityBasedInspect
override fun isApplicable(element: KtCallExpression) = element.canMoveLambdaOutsideParentheses() override fun isApplicable(element: KtCallExpression) = element.canMoveLambdaOutsideParentheses()
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtCallExpression, project: Project, editor: Editor?) {
val expression = element.getParentOfType<KtCallExpression>(strict = false) ?: return if (element.canMoveLambdaOutsideParentheses()) {
element.moveFunctionLiteralOutsideParentheses()
if (expression.canMoveLambdaOutsideParentheses()) {
expression.moveFunctionLiteralOutsideParentheses()
} }
} }
override fun inspectionText(element: KtCallExpression) = "Lambda argument ${element.verb} be moved out of parentheses" override fun inspectionText(element: KtCallExpression) = "Lambda argument ${element.verb} be moved out of parentheses"
override fun inspectionTarget(element: KtCallExpression): KtElement { override fun inspectionHighlightRangeInElement(element: KtCallExpression) = element.getLastLambdaExpression()
return element.getLastLambdaExpression()?.getStrictParentOfType<KtValueArgument>()?.asElement() ?: element ?.getStrictParentOfType<KtValueArgument>()?.asElement()
} ?.textRangeIn(element)
override val defaultFixText = "Move lambda argument out of parentheses" override val defaultFixText = "Move lambda argument out of parentheses"
} }
@@ -75,7 +75,7 @@ class NullableBooleanElvisInspection : AbstractKotlinInspection(), CleanupLocalI
val prefixExpression = equalityCheckExpression.getParentOfType<KtPrefixExpression>(strict = true) ?: return val prefixExpression = equalityCheckExpression.getParentOfType<KtPrefixExpression>(strict = true) ?: return
val simplifier = SimplifyNegatedBinaryExpressionInspection() val simplifier = SimplifyNegatedBinaryExpressionInspection()
if (simplifier.isApplicable(prefixExpression)) { if (simplifier.isApplicable(prefixExpression)) {
simplifier.applyTo(prefixExpression.operationReference) simplifier.applyTo(prefixExpression)
} }
} }
} }
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.idea.inspections
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.idea.core.canDropBraces import org.jetbrains.kotlin.idea.core.canDropBraces
import org.jetbrains.kotlin.idea.core.dropBraces import org.jetbrains.kotlin.idea.core.dropBraces
import org.jetbrains.kotlin.psi.KtBlockStringTemplateEntry import org.jetbrains.kotlin.psi.KtBlockStringTemplateEntry
@@ -20,7 +19,7 @@ class RemoveCurlyBracesFromTemplateInspection :
override fun isApplicable(element: KtBlockStringTemplateEntry): Boolean = element.canDropBraces() override fun isApplicable(element: KtBlockStringTemplateEntry): Boolean = element.canDropBraces()
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtBlockStringTemplateEntry, project: Project, editor: Editor?) {
(element as KtBlockStringTemplateEntry).dropBraces() element.dropBraces()
} }
} }
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.idea.inspections
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.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.resolvedToArrayType import org.jetbrains.kotlin.idea.intentions.resolvedToArrayType
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
@@ -28,24 +27,23 @@ import org.jetbrains.kotlin.psi.createExpressionByPattern
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
class ReplaceArrayEqualityOpWithArraysEqualsInspection : AbstractApplicabilityBasedInspection<KtBinaryExpression>( class ReplaceArrayEqualityOpWithArraysEqualsInspection : AbstractApplicabilityBasedInspection<KtBinaryExpression>(
KtBinaryExpression::class.java KtBinaryExpression::class.java
) { ) {
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtBinaryExpression, project: Project, editor: Editor?) {
val expression = element as? KtBinaryExpression ?: return val right = element.right ?: return
val right = expression.right ?: return val left = element.left ?: return
val left = expression.left ?: return
val factory = KtPsiFactory(project) val factory = KtPsiFactory(project)
val template = buildString { val template = buildString {
if (expression.operationToken == KtTokens.EXCLEQ) append("!") if (element.operationToken == KtTokens.EXCLEQ) append("!")
append("$0.contentEquals($1)") append("$0.contentEquals($1)")
} }
expression.replace(factory.createExpressionByPattern(template, left, right)) element.replace(factory.createExpressionByPattern(template, left, right))
} }
override fun isApplicable(element: KtBinaryExpression): Boolean { override fun isApplicable(element: KtBinaryExpression): Boolean {
val operationToken = element.operationToken when (element.operationToken) {
when (operationToken) { KtTokens.EQEQ, KtTokens.EXCLEQ -> {
KtTokens.EQEQ, KtTokens.EXCLEQ -> {} }
else -> return false else -> return false
} }
val right = element.right val right = element.right
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInsight.actions.OptimizeImportsProcessor import com.intellij.codeInsight.actions.OptimizeImportsProcessor
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.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.core.ShortenReferences
@@ -39,21 +38,20 @@ class ReplaceAssertBooleanWithAssertEqualityInspection : AbstractApplicabilityBa
return (element.replaceableAssertion() != null) return (element.replaceableAssertion() != null)
} }
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtCallExpression, project: Project, editor: Editor?) {
val expression = element as? KtCallExpression ?: return val valueArguments = element.valueArguments
val valueArguments = expression.valueArguments
val condition = valueArguments.firstOrNull()?.getArgumentExpression() as? KtBinaryExpression ?: return val condition = valueArguments.firstOrNull()?.getArgumentExpression() as? KtBinaryExpression ?: return
val left = condition.left ?: return val left = condition.left ?: return
val right = condition.right ?: return val right = condition.right ?: return
val assertion = expression.replaceableAssertion() ?: return val assertion = element.replaceableAssertion() ?: return
val file = expression.containingKtFile val file = element.containingKtFile
val factory = KtPsiFactory(project) val factory = KtPsiFactory(project)
val replaced = if (valueArguments.size == 2) { val replaced = if (valueArguments.size == 2) {
val message = valueArguments[1].getArgumentExpression() ?: return val message = valueArguments[1].getArgumentExpression() ?: return
expression.replaced(factory.createExpressionByPattern("$assertion($0, $1, $2)", left, right, message)) element.replaced(factory.createExpressionByPattern("$assertion($0, $1, $2)", left, right, message))
} else { } else {
expression.replaced(factory.createExpressionByPattern("$assertion($0, $1)", left, right)) element.replaced(factory.createExpressionByPattern("$assertion($0, $1)", left, right))
} }
ShortenReferences.DEFAULT.process(replaced) ShortenReferences.DEFAULT.process(replaced)
OptimizeImportsProcessor(project, file).run() OptimizeImportsProcessor(project, file).run()
@@ -79,11 +77,11 @@ class ReplaceAssertBooleanWithAssertEqualityInspection : AbstractApplicabilityBa
return assertionMap[Pair(referencedName, operationToken)] return assertionMap[Pair(referencedName, operationToken)]
} }
private fun KtExpression.descriptor(context: BindingContext): CallableDescriptor? { private fun KtExpression.descriptor(context: BindingContext): CallableDescriptor? {
return getResolvedCall(context)?.resultingDescriptor return getResolvedCall(context)?.resultingDescriptor
} }
private fun KtExpression.type(context: BindingContext): KotlinType? { private fun KtExpression.type(context: BindingContext): KotlinType? {
return descriptor(context)?.returnType return descriptor(context)?.returnType
} }
@@ -18,15 +18,14 @@ package org.jetbrains.kotlin.idea.inspections
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.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.callExpression import org.jetbrains.kotlin.idea.intentions.callExpression
import org.jetbrains.kotlin.idea.util.calleeTextRangeInThis
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
@@ -63,26 +62,25 @@ class ReplacePutWithAssignmentInspection : AbstractApplicabilityBasedInspection<
return receiverClass.isSubclassOf(DefaultBuiltIns.Instance.mutableMap) return receiverClass.isSubclassOf(DefaultBuiltIns.Instance.mutableMap)
} }
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtDotQualifiedExpression, project: Project, editor: Editor?) {
val expression = element.getParentOfType<KtDotQualifiedExpression>(strict = false) ?: return val valueArguments = element.callExpression?.valueArguments ?: return
val valueArguments = expression.callExpression?.valueArguments ?: return
val firstArg = valueArguments[0]?.getArgumentExpression() ?: return val firstArg = valueArguments[0]?.getArgumentExpression() ?: return
val secondArg = valueArguments[1]?.getArgumentExpression() ?: return val secondArg = valueArguments[1]?.getArgumentExpression() ?: return
val label = if (secondArg is KtLambdaExpression) { val label = if (secondArg is KtLambdaExpression) {
val returnLabel = secondArg.findDescendantOfType<KtReturnExpression>()?.getLabelName() val returnLabel = secondArg.findDescendantOfType<KtReturnExpression>()?.getLabelName()
compatibleNames.firstOrNull { it == returnLabel }?.plus("@") ?: "" compatibleNames.firstOrNull { it == returnLabel }?.plus("@") ?: ""
} else "" } else ""
expression.replace( element.replace(
KtPsiFactory(expression).createExpressionByPattern( KtPsiFactory(element).createExpressionByPattern(
"$0[$1] = $label$2", "$0[$1] = $label$2",
expression.receiverExpression, element.receiverExpression,
firstArg, firstArg,
secondArg secondArg
) )
) )
} }
override fun inspectionTarget(element: KtDotQualifiedExpression) = element.callExpression?.calleeExpression ?: element override fun inspectionHighlightRangeInElement(element: KtDotQualifiedExpression) = element.calleeTextRangeInThis()
override fun inspectionText(element: KtDotQualifiedExpression): String = "map.put() should be converted to assignment" override fun inspectionText(element: KtDotQualifiedExpression): String = "map.put() should be converted to assignment"
@@ -7,13 +7,11 @@ package org.jetbrains.kotlin.idea.inspections
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.idea.intentions.isToString import org.jetbrains.kotlin.idea.intentions.isToString
import org.jetbrains.kotlin.psi.KtBlockStringTemplateEntry import org.jetbrains.kotlin.psi.KtBlockStringTemplateEntry
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtReferenceExpression import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
class ReplaceToStringWithStringTemplateInspection : AbstractApplicabilityBasedInspection<KtDotQualifiedExpression>( class ReplaceToStringWithStringTemplateInspection : AbstractApplicabilityBasedInspection<KtDotQualifiedExpression>(
KtDotQualifiedExpression::class.java KtDotQualifiedExpression::class.java
@@ -24,15 +22,12 @@ class ReplaceToStringWithStringTemplateInspection : AbstractApplicabilityBasedIn
return element.isToString() return element.isToString()
} }
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtDotQualifiedExpression, project: Project, editor: Editor?) {
val expression = element.getParentOfType<KtDotQualifiedExpression>(strict = false) ?: return val variable = element.receiverExpression.text
val variable = expression.receiverExpression.text
element.replace(KtPsiFactory(element).createExpression("\"$$variable\"")) element.replace(KtPsiFactory(element).createExpression("\"$$variable\""))
} }
override fun inspectionText(element: KtDotQualifiedExpression) = "Call of 'toString' could be replaced with string template" override fun inspectionText(element: KtDotQualifiedExpression) = "Call of 'toString' could be replaced with string template"
override fun inspectionTarget(element: KtDotQualifiedExpression) = element
override val defaultFixText = "Replace 'toString' with string template" override val defaultFixText = "Replace 'toString' with string template"
} }
@@ -19,7 +19,6 @@ 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 com.intellij.psi.tree.IElementType import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
@@ -115,8 +114,8 @@ class ReplaceWithOperatorAssignmentInspection : AbstractApplicabilityBasedInspec
operationToken == KtTokens.DIV || operationToken == KtTokens.DIV ||
operationToken == KtTokens.PERC operationToken == KtTokens.PERC
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtBinaryExpression, project: Project, editor: Editor?) {
(element as? KtBinaryExpression)?.replace(buildOperatorAssignment(element)) element.replace(buildOperatorAssignment(element))
} }
private fun buildOperatorAssignment(element: KtBinaryExpression): KtBinaryExpression { private fun buildOperatorAssignment(element: KtBinaryExpression): KtBinaryExpression {
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInsight.CodeInsightUtilCore import com.intellij.codeInsight.CodeInsightUtilCore
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.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.core.ShortenReferences
@@ -69,35 +68,34 @@ class SimplifyAssertNotNullInspection : AbstractApplicabilityBasedInspection<KtC
override fun fixText(element: KtCallExpression): String { override fun fixText(element: KtCallExpression): String {
return if (element.valueArguments.size == 1) { return if (element.valueArguments.size == 1) {
"Replace with '!!' operator" "Replace with '!!' operator"
} } else {
else {
"Replace with '?: error(...)'" "Replace with '?: error(...)'"
} }
} }
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtCallExpression, project: Project, editor: Editor?) {
val expression = element as? KtCallExpression ?: return val declaration = findVariableDeclaration(element) ?: return
val declaration = findVariableDeclaration(expression)!! val initializer = declaration.initializer ?: return
val initializer = declaration.initializer!! val message = extractMessage(element)
val message = extractMessage(expression)
val commentSaver = CommentSaver(expression) val commentSaver = CommentSaver(element)
if (message == null) { if (message == null) {
val newInitializer = KtPsiFactory(expression).createExpressionByPattern("$0!!", initializer) val newInitializer = KtPsiFactory(element).createExpressionByPattern("$0!!", initializer)
initializer.replace(newInitializer) initializer.replace(newInitializer)
} } else {
else { val newInitializer = KtPsiFactory(element).createExpressionByPattern("$0 ?: kotlin.error($1)", initializer, message)
val newInitializer = KtPsiFactory(expression).createExpressionByPattern("$0 ?: kotlin.error($1)", initializer, message)
val result = initializer.replace(newInitializer) val result = initializer.replace(newInitializer)
val qualifiedExpression = (result as KtBinaryExpression).right as KtDotQualifiedExpression val qualifiedExpression = (result as KtBinaryExpression).right as KtDotQualifiedExpression
ShortenReferences.DEFAULT.process(expression.containingKtFile, ShortenReferences.DEFAULT.process(
qualifiedExpression.startOffset, element.containingKtFile,
(qualifiedExpression.selectorExpression as KtCallExpression).calleeExpression!!.endOffset) qualifiedExpression.startOffset,
(qualifiedExpression.selectorExpression as KtCallExpression).calleeExpression!!.endOffset
)
} }
expression.delete() element.delete()
commentSaver.restore(declaration) commentSaver.restore(declaration)
@@ -120,8 +118,8 @@ class SimplifyAssertNotNullInspection : AbstractApplicabilityBasedInspection<KtC
val arguments = element.valueArguments val arguments = element.valueArguments
if (arguments.size != 2) return null if (arguments.size != 2) return null
return (arguments[1].getArgumentExpression() as? KtLambdaExpression) return (arguments[1].getArgumentExpression() as? KtLambdaExpression)
?.bodyExpression ?.bodyExpression
?.statements ?.statements
?.singleOrNull() ?.singleOrNull()
} }
} }
@@ -18,15 +18,13 @@ package org.jetbrains.kotlin.idea.inspections
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 com.intellij.psi.tree.IElementType import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.idea.util.textRangeIn
import org.jetbrains.kotlin.lexer.KtSingleValueToken import org.jetbrains.kotlin.lexer.KtSingleValueToken
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
class SimplifyNegatedBinaryExpressionInspection : AbstractApplicabilityBasedInspection<KtPrefixExpression>( class SimplifyNegatedBinaryExpressionInspection : AbstractApplicabilityBasedInspection<KtPrefixExpression>(KtPrefixExpression::class.java) {
KtPrefixExpression::class.java
) {
private fun IElementType.negate(): KtSingleValueToken? = when (this) { private fun IElementType.negate(): KtSingleValueToken? = when (this) {
KtTokens.IN_KEYWORD -> KtTokens.NOT_IN KtTokens.IN_KEYWORD -> KtTokens.NOT_IN
@@ -47,7 +45,7 @@ class SimplifyNegatedBinaryExpressionInspection : AbstractApplicabilityBasedInsp
else -> null else -> null
} }
override fun inspectionTarget(element: KtPrefixExpression) = element.operationReference override fun inspectionHighlightRangeInElement(element: KtPrefixExpression) = element.operationReference.textRangeIn(element)
override fun inspectionText(element: KtPrefixExpression) = "Negated operation should be simplified" override fun inspectionText(element: KtPrefixExpression) = "Negated operation should be simplified"
@@ -73,9 +71,8 @@ class SimplifyNegatedBinaryExpressionInspection : AbstractApplicabilityBasedInsp
return (expression.operationReference.getReferencedNameElementType() as? KtSingleValueToken)?.negate() != null return (expression.operationReference.getReferencedNameElementType() as? KtSingleValueToken)?.negate() != null
} }
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtPrefixExpression, project: Project, editor: Editor?) {
val prefixExpression = element.parent as? KtPrefixExpression ?: return val expression = KtPsiUtil.deparenthesize(element.baseExpression) ?: return
val expression = KtPsiUtil.deparenthesize(prefixExpression.baseExpression) ?: return
val operation = (expression as KtOperationExpression).operationReference.getReferencedNameElementType().negate()?.value ?: return val operation = (expression as KtOperationExpression).operationReference.getReferencedNameElementType().negate()?.value ?: return
val psiFactory = KtPsiFactory(expression) val psiFactory = KtPsiFactory(expression)
@@ -87,6 +84,6 @@ class SimplifyNegatedBinaryExpressionInspection : AbstractApplicabilityBasedInsp
else -> else ->
throw IllegalArgumentException() throw IllegalArgumentException()
} }
prefixExpression.replace(newExpression) element.replace(newExpression)
} }
} }
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.idea.inspections
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 com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.psi.search.searches.ReferencesSearch
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
@@ -27,9 +26,9 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator
import org.jetbrains.kotlin.idea.refactoring.inline.KotlinInlineValHandler import org.jetbrains.kotlin.idea.refactoring.inline.KotlinInlineValHandler
import org.jetbrains.kotlin.idea.util.nameIdentifierTextRangeInThis
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR import org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR
import org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET import org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET
@@ -40,7 +39,7 @@ class UnnecessaryVariableInspection : AbstractApplicabilityBasedInspection<KtPro
override fun isApplicable(element: KtProperty) = statusFor(element) != null override fun isApplicable(element: KtProperty) = statusFor(element) != null
override fun inspectionTarget(element: KtProperty) = element.nameIdentifier ?: element override fun inspectionHighlightRangeInElement(element: KtProperty) = element.nameIdentifierTextRangeInThis()
override fun inspectionText(element: KtProperty) = when (statusFor(element)) { override fun inspectionText(element: KtProperty) = when (statusFor(element)) {
Status.RETURN_ONLY -> Status.RETURN_ONLY ->
@@ -54,9 +53,8 @@ class UnnecessaryVariableInspection : AbstractApplicabilityBasedInspection<KtPro
override val startFixInWriteAction = false override val startFixInWriteAction = false
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtProperty, project: Project, editor: Editor?) {
val property = element.getParentOfType<KtProperty>(strict = false) ?: return KotlinInlineValHandler(withPrompt = false).inlineElement(project, editor, element)
KotlinInlineValHandler(withPrompt = false).inlineElement(project, editor, property)
} }
companion object { companion object {
@@ -72,7 +70,7 @@ class UnnecessaryVariableInspection : AbstractApplicabilityBasedInspection<KtPro
fun isExactCopy(): Boolean { fun isExactCopy(): Boolean {
if (!property.isVar && initializer is KtNameReferenceExpression && property.typeReference == null) { if (!property.isVar && initializer is KtNameReferenceExpression && property.typeReference == null) {
val initializerDescriptor = initializer.resolveToCall(BodyResolveMode.FULL)?.resultingDescriptor as? VariableDescriptor val initializerDescriptor = initializer.resolveToCall(BodyResolveMode.FULL)?.resultingDescriptor as? VariableDescriptor
?: return false ?: return false
if (initializerDescriptor.isVar) return false if (initializerDescriptor.isVar) return false
if (initializerDescriptor.containingDeclaration !is FunctionDescriptor) return false if (initializerDescriptor.containingDeclaration !is FunctionDescriptor) return false
@@ -10,8 +10,6 @@ import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel
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.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
@@ -21,7 +19,6 @@ import org.jetbrains.kotlin.idea.intentions.branchedTransformations.*
import org.jetbrains.kotlin.idea.util.CommentSaver import org.jetbrains.kotlin.idea.util.CommentSaver
import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.getType import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -42,14 +39,11 @@ class IfThenToElvisInspection(
else else
ProblemHighlightType.INFORMATION ProblemHighlightType.INFORMATION
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtIfExpression, project: Project, editor: Editor?) {
convert( convert(element, editor)
element as KtIfExpression,
editor
)
} }
override fun inspectionHighlightRangeInElement(element: KtIfExpression): TextRange? = element.textRange().shiftLeft(element.startOffset) override fun inspectionHighlightRangeInElement(element: KtIfExpression) = element.fromIfKeywordToRightParenthesisTextRangeInThis()
override fun createOptionsPanel(): JComponent? = MultipleCheckboxOptionsPanel(this).also { override fun createOptionsPanel(): JComponent? = MultipleCheckboxOptionsPanel(this).also {
it.addCheckbox("Report also on statement", "highlightStatement") it.addCheckbox("Report also on statement", "highlightStatement")
@@ -19,9 +19,7 @@ package org.jetbrains.kotlin.idea.inspections.branchedTransformations
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.openapi.util.TextRange
import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.inspections.AbstractApplicabilityBasedInspection import org.jetbrains.kotlin.idea.inspections.AbstractApplicabilityBasedInspection
@@ -40,7 +38,7 @@ class IfThenToSafeAccessInspection : AbstractApplicabilityBasedInspection<KtIfEx
override fun isApplicable(element: KtIfExpression): Boolean = isApplicableTo(element, expressionShouldBeStable = true) override fun isApplicable(element: KtIfExpression): Boolean = isApplicableTo(element, expressionShouldBeStable = true)
override fun inspectionHighlightRangeInElement(element: KtIfExpression): TextRange? = element.textRange().shiftLeft(element.startOffset) override fun inspectionHighlightRangeInElement(element: KtIfExpression) = element.fromIfKeywordToRightParenthesisTextRangeInThis()
override fun inspectionText(element: KtIfExpression) = "Foldable if-then" override fun inspectionText(element: KtIfExpression) = "Foldable if-then"
@@ -53,8 +51,8 @@ class IfThenToSafeAccessInspection : AbstractApplicabilityBasedInspection<KtIfEx
override val startFixInWriteAction = false override val startFixInWriteAction = false
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtIfExpression, project: Project, editor: Editor?) {
convert(element as KtIfExpression, editor) convert(element, editor)
} }
companion object { companion object {
@@ -18,18 +18,17 @@ package org.jetbrains.kotlin.idea.inspections.branchedTransformations
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.idea.inspections.AbstractApplicabilityBasedInspection import org.jetbrains.kotlin.idea.inspections.AbstractApplicabilityBasedInspection
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.getSubjectToIntroduce import org.jetbrains.kotlin.idea.intentions.branchedTransformations.getSubjectToIntroduce
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.introduceSubject import org.jetbrains.kotlin.idea.intentions.branchedTransformations.introduceSubject
import org.jetbrains.kotlin.idea.util.textRangeIn
import org.jetbrains.kotlin.psi.KtWhenExpression import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
class IntroduceWhenSubjectInspection : AbstractApplicabilityBasedInspection<KtWhenExpression>(KtWhenExpression::class.java) { class IntroduceWhenSubjectInspection : AbstractApplicabilityBasedInspection<KtWhenExpression>(KtWhenExpression::class.java) {
override fun isApplicable(element: KtWhenExpression) = element.getSubjectToIntroduce() != null override fun isApplicable(element: KtWhenExpression) = element.getSubjectToIntroduce() != null
override fun inspectionTarget(element: KtWhenExpression) = element.whenKeyword override fun inspectionHighlightRangeInElement(element: KtWhenExpression) = element.whenKeyword.textRangeIn(element)
override fun inspectionText(element: KtWhenExpression) = "'when' with subject should be used" override fun inspectionText(element: KtWhenExpression) = "'when' with subject should be used"
@@ -40,7 +39,7 @@ class IntroduceWhenSubjectInspection : AbstractApplicabilityBasedInspection<KtWh
return "Introduce '${subject.text}' as subject of 'when'" return "Introduce '${subject.text}' as subject of 'when'"
} }
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtWhenExpression, project: Project, editor: Editor?) {
element.getParentOfType<KtWhenExpression>(strict = true)?.introduceSubject() element.introduceSubject()
} }
} }
@@ -35,11 +35,12 @@ import org.jetbrains.kotlin.idea.intentions.isReceiverExpressionWithValue
import org.jetbrains.kotlin.idea.intentions.toResolvedCall import org.jetbrains.kotlin.idea.intentions.toResolvedCall
import org.jetbrains.kotlin.idea.project.languageVersionSettings import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.project.platform import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.idea.util.calleeTextRangeInThis
import org.jetbrains.kotlin.lexer.KtSingleValueToken import org.jetbrains.kotlin.lexer.KtSingleValueToken
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.platform.js.isJs
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getLastParentOfTypeInRow import org.jetbrains.kotlin.psi.psiUtil.getLastParentOfTypeInRow
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.calls.callUtil.getFirstArgumentExpression import org.jetbrains.kotlin.resolve.calls.callUtil.getFirstArgumentExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.getReceiverExpression import org.jetbrains.kotlin.resolve.calls.callUtil.getReceiverExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.getType import org.jetbrains.kotlin.resolve.calls.callUtil.getType
@@ -47,7 +48,6 @@ import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.calls.smartcasts.getKotlinTypeWithPossibleSmartCastToFP import org.jetbrains.kotlin.resolve.calls.smartcasts.getKotlinTypeWithPossibleSmartCastToFP
import org.jetbrains.kotlin.platform.js.isJs
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.types.expressions.OperatorConventions
@@ -82,7 +82,7 @@ class ReplaceCallWithBinaryOperatorInspection : AbstractApplicabilityBasedInspec
return element.isReceiverExpressionWithValue() return element.isReceiverExpressionWithValue()
} }
override fun inspectionTarget(element: KtDotQualifiedExpression) = element.callExpression?.calleeExpression ?: element override fun inspectionHighlightRangeInElement(element: KtDotQualifiedExpression) = element.calleeTextRangeInThis()
override fun inspectionHighlightType(element: KtDotQualifiedExpression): ProblemHighlightType { override fun inspectionHighlightType(element: KtDotQualifiedExpression): ProblemHighlightType {
val calleeExpression = element.callExpression?.calleeExpression as? KtSimpleNameExpression val calleeExpression = element.callExpression?.calleeExpression as? KtSimpleNameExpression
@@ -118,28 +118,27 @@ class ReplaceCallWithBinaryOperatorInspection : AbstractApplicabilityBasedInspec
return "Replace with '${operation.value}'" return "Replace with '${operation.value}'"
} }
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtDotQualifiedExpression, project: Project, editor: Editor?) {
val qualifiedExpression = element.getParentOfType<KtDotQualifiedExpression>(strict = false) ?: return val callExpression = element.callExpression ?: return
val callExpression = qualifiedExpression.callExpression ?: return
val operation = operation(callExpression.calleeExpression as? KtSimpleNameExpression ?: return) ?: return val operation = operation(callExpression.calleeExpression as? KtSimpleNameExpression ?: return) ?: return
val argument = callExpression.valueArguments.single().getArgumentExpression() ?: return val argument = callExpression.valueArguments.single().getArgumentExpression() ?: return
val receiver = qualifiedExpression.receiverExpression val receiver = element.receiverExpression
val factory = KtPsiFactory(qualifiedExpression) val factory = KtPsiFactory(element)
when (operation) { when (operation) {
KtTokens.EXCLEQ -> { KtTokens.EXCLEQ -> {
val prefixExpression = qualifiedExpression.getWrappingPrefixExpressionIfAny() ?: return val prefixExpression = element.getWrappingPrefixExpressionIfAny() ?: return
val newExpression = factory.createExpressionByPattern("$0 != $1", receiver, argument) val newExpression = factory.createExpressionByPattern("$0 != $1", receiver, argument)
prefixExpression.replace(newExpression) prefixExpression.replace(newExpression)
} }
in OperatorConventions.COMPARISON_OPERATIONS -> { in OperatorConventions.COMPARISON_OPERATIONS -> {
val binaryParent = qualifiedExpression.parent as? KtBinaryExpression ?: return val binaryParent = element.parent as? KtBinaryExpression ?: return
val newExpression = factory.createExpressionByPattern("$0 ${operation.value} $1", receiver, argument) val newExpression = factory.createExpressionByPattern("$0 ${operation.value} $1", receiver, argument)
binaryParent.replace(newExpression) binaryParent.replace(newExpression)
} }
else -> { else -> {
val newExpression = factory.createExpressionByPattern("$0 ${operation.value} $1", receiver, argument) val newExpression = factory.createExpressionByPattern("$0 ${operation.value} $1", receiver, argument)
qualifiedExpression.replace(newExpression) element.replace(newExpression)
} }
} }
} }
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.idea.inspections.AbstractApplicabilityBasedInspectio
import org.jetbrains.kotlin.idea.intentions.callExpression import org.jetbrains.kotlin.idea.intentions.callExpression
import org.jetbrains.kotlin.idea.intentions.isReceiverExpressionWithValue import org.jetbrains.kotlin.idea.intentions.isReceiverExpressionWithValue
import org.jetbrains.kotlin.idea.intentions.toResolvedCall import org.jetbrains.kotlin.idea.intentions.toResolvedCall
import org.jetbrains.kotlin.idea.util.calleeTextRangeInThis
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
@@ -39,7 +40,7 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.util.isValidOperator import org.jetbrains.kotlin.util.isValidOperator
class ReplaceGetOrSetInspection : AbstractApplicabilityBasedInspection<KtDotQualifiedExpression>( class ReplaceGetOrSetInspection : AbstractApplicabilityBasedInspection<KtDotQualifiedExpression>(
KtDotQualifiedExpression::class.java KtDotQualifiedExpression::class.java
) { ) {
private fun FunctionDescriptor.isExplicitOperator(): Boolean { private fun FunctionDescriptor.isExplicitOperator(): Boolean {
return if (overriddenDescriptors.isEmpty()) return if (overriddenDescriptors.isEmpty())
@@ -75,10 +76,9 @@ class ReplaceGetOrSetInspection : AbstractApplicabilityBasedInspection<KtDotQual
override fun inspectionText(element: KtDotQualifiedExpression) = "Should be replaced with indexing" override fun inspectionText(element: KtDotQualifiedExpression) = "Should be replaced with indexing"
override fun inspectionHighlightType(element: KtDotQualifiedExpression): ProblemHighlightType { override fun inspectionHighlightType(element: KtDotQualifiedExpression): ProblemHighlightType {
return if ((element.toResolvedCall(BodyResolveMode.PARTIAL)!!.resultingDescriptor as FunctionDescriptor).isExplicitOperator()) { return if ((element.toResolvedCall(BodyResolveMode.PARTIAL)?.resultingDescriptor as? FunctionDescriptor)?.isExplicitOperator() == true) {
ProblemHighlightType.GENERIC_ERROR_OR_WARNING ProblemHighlightType.GENERIC_ERROR_OR_WARNING
} } else {
else {
ProblemHighlightType.INFORMATION ProblemHighlightType.INFORMATION
} }
} }
@@ -92,16 +92,15 @@ class ReplaceGetOrSetInspection : AbstractApplicabilityBasedInspection<KtDotQual
return "Replace '${resolvedCall.resultingDescriptor.name.asString()}' call with indexing operator" return "Replace '${resolvedCall.resultingDescriptor.name.asString()}' call with indexing operator"
} }
override fun inspectionTarget(element: KtDotQualifiedExpression) = element.callExpression?.calleeExpression ?: element override fun inspectionHighlightRangeInElement(element: KtDotQualifiedExpression) = element.calleeTextRangeInThis()
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtDotQualifiedExpression, project: Project, editor: Editor?) {
val expression = element as? KtDotQualifiedExpression ?: element.parent.parent as? KtDotQualifiedExpression ?: return val isSet = element.toResolvedCall(BodyResolveMode.PARTIAL)?.resultingDescriptor?.name == OperatorNameConventions.SET
val isSet = expression.toResolvedCall(BodyResolveMode.PARTIAL)!!.resultingDescriptor.name == OperatorNameConventions.SET val allArguments = element.callExpression!!.valueArguments
val allArguments = expression.callExpression!!.valueArguments
assert(allArguments.isNotEmpty()) assert(allArguments.isNotEmpty())
val newExpression = KtPsiFactory(expression).buildExpression { val newExpression = KtPsiFactory(element).buildExpression {
appendExpression(expression.receiverExpression) appendExpression(element.receiverExpression)
appendFixedText("[") appendFixedText("[")
@@ -116,7 +115,7 @@ class ReplaceGetOrSetInspection : AbstractApplicabilityBasedInspection<KtDotQual
} }
} }
val newElement = expression.replace(newExpression) val newElement = element.replace(newExpression)
if (editor != null) { if (editor != null) {
moveCaret(editor, isSet, newElement) moveCaret(editor, isSet, newElement)
@@ -125,12 +124,11 @@ class ReplaceGetOrSetInspection : AbstractApplicabilityBasedInspection<KtDotQual
private fun moveCaret(editor: Editor, isSet: Boolean, newElement: PsiElement) { private fun moveCaret(editor: Editor, isSet: Boolean, newElement: PsiElement) {
val arrayAccessExpression = if (isSet) { val arrayAccessExpression = if (isSet) {
newElement.getChildOfType<KtArrayAccessExpression>()!! newElement.getChildOfType()
} } else {
else { newElement as? KtArrayAccessExpression
newElement as KtArrayAccessExpression } ?: return
}
editor.caretModel.moveToOffset(arrayAccessExpression.leftBracket!!.startOffset) arrayAccessExpression.leftBracket?.startOffset?.let { editor.caretModel.moveToOffset(it) }
} }
} }
@@ -5,22 +5,22 @@
package org.jetbrains.kotlin.idea.inspections.substring package org.jetbrains.kotlin.idea.inspections.substring
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.inspections.AbstractApplicabilityBasedInspection import org.jetbrains.kotlin.idea.inspections.AbstractApplicabilityBasedInspection
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.evaluatesTo import org.jetbrains.kotlin.idea.intentions.branchedTransformations.evaluatesTo
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStableSimpleExpression import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStableSimpleExpression
import org.jetbrains.kotlin.idea.intentions.callExpression import org.jetbrains.kotlin.idea.intentions.callExpression
import org.jetbrains.kotlin.idea.intentions.toResolvedCall import org.jetbrains.kotlin.idea.intentions.toResolvedCall
import org.jetbrains.kotlin.idea.util.calleeTextRangeInThis
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
abstract class ReplaceSubstringInspection : abstract class ReplaceSubstringInspection : AbstractApplicabilityBasedInspection<KtDotQualifiedExpression>(
AbstractApplicabilityBasedInspection<KtDotQualifiedExpression>(KtDotQualifiedExpression::class.java) { KtDotQualifiedExpression::class.java
) {
protected abstract fun isApplicableInner(element: KtDotQualifiedExpression): Boolean protected abstract fun isApplicableInner(element: KtDotQualifiedExpression): Boolean
protected open val isAlwaysStable: Boolean = false protected open val isAlwaysStable: Boolean = false
@@ -30,8 +30,7 @@ abstract class ReplaceSubstringInspection :
} else } else
false false
override fun inspectionHighlightRangeInElement(element: KtDotQualifiedExpression): TextRange? = override fun inspectionHighlightRangeInElement(element: KtDotQualifiedExpression) = element.calleeTextRangeInThis()
element.callExpression?.calleeExpression?.textRange?.shiftLeft(element.startOffset)
protected fun isIndexOfCall(expression: KtExpression?, expectedReceiver: KtExpression): Boolean { protected fun isIndexOfCall(expression: KtExpression?, expectedReceiver: KtExpression): Boolean {
return expression is KtDotQualifiedExpression return expression is KtDotQualifiedExpression
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.idea.inspections.substring
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.idea.intentions.branchedTransformations.evaluatesTo import org.jetbrains.kotlin.idea.intentions.branchedTransformations.evaluatesTo
import org.jetbrains.kotlin.idea.intentions.callExpression import org.jetbrains.kotlin.idea.intentions.callExpression
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
@@ -20,10 +19,9 @@ class ReplaceSubstringWithDropLastInspection : ReplaceSubstringInspection() {
override fun inspectionText(element: KtDotQualifiedExpression): String = "Replace 'substring' call with 'dropLast' call" override fun inspectionText(element: KtDotQualifiedExpression): String = "Replace 'substring' call with 'dropLast' call"
override val defaultFixText: String = "Replace 'substring' call with 'dropLast' call" override val defaultFixText: String = "Replace 'substring' call with 'dropLast' call"
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtDotQualifiedExpression, project: Project, editor: Editor?) {
if (element !is KtDotQualifiedExpression) return val argument = element.callExpression?.valueArguments?.elementAtOrNull(1)?.getArgumentExpression() ?: return
val argument = element.callExpression!!.valueArguments[1].getArgumentExpression()!! val rightExpression = (argument as? KtBinaryExpression)?.right ?: return
val rightExpression = (argument as KtBinaryExpression).right!!
element.replaceWith("$0.dropLast($1)", rightExpression) element.replaceWith("$0.dropLast($1)", rightExpression)
} }
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.idea.inspections.substring
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.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.callExpression import org.jetbrains.kotlin.idea.intentions.callExpression
import org.jetbrains.kotlin.psi.KtConstantExpression import org.jetbrains.kotlin.psi.KtConstantExpression
@@ -15,14 +14,12 @@ import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
class ReplaceSubstringWithIndexingOperationInspection : class ReplaceSubstringWithIndexingOperationInspection : ReplaceSubstringInspection() {
ReplaceSubstringInspection() {
override fun inspectionText(element: KtDotQualifiedExpression): String = "Replace 'substring' call with indexing operation call" override fun inspectionText(element: KtDotQualifiedExpression): String = "Replace 'substring' call with indexing operation call"
override val defaultFixText: String = "Replace 'substring' call with indexing operation call" override val defaultFixText: String = "Replace 'substring' call with indexing operation call"
override val isAlwaysStable: Boolean = true override val isAlwaysStable: Boolean = true
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtDotQualifiedExpression, project: Project, editor: Editor?) {
if (element !is KtDotQualifiedExpression) return
val expression = element.callExpression?.valueArguments?.firstOrNull()?.getArgumentExpression() ?: return val expression = element.callExpression?.valueArguments?.firstOrNull()?.getArgumentExpression() ?: return
element.replaceWith("$0[$1]", expression) element.replaceWith("$0[$1]", expression)
} }
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.idea.inspections.substring
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.idea.intentions.callExpression import org.jetbrains.kotlin.idea.intentions.callExpression
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
@@ -17,8 +16,7 @@ class ReplaceSubstringWithSubstringAfterInspection : ReplaceSubstringInspection(
override val defaultFixText: String = "Replace 'substring' call with 'substringAfter' call" override val defaultFixText: String = "Replace 'substring' call with 'substringAfter' call"
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtDotQualifiedExpression, project: Project, editor: Editor?) {
if (element !is KtDotQualifiedExpression) return
element.replaceWith( element.replaceWith(
"$0.substringAfter($1)", "$0.substringAfter($1)",
(element.getArgumentExpression(0) as KtDotQualifiedExpression).getArgumentExpression(0) (element.getArgumentExpression(0) as KtDotQualifiedExpression).getArgumentExpression(0)
@@ -36,8 +34,7 @@ class ReplaceSubstringWithSubstringBeforeInspection : ReplaceSubstringInspection
override val defaultFixText: String = "Replace 'substring' call with 'substringBefore' call" override val defaultFixText: String = "Replace 'substring' call with 'substringBefore' call"
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtDotQualifiedExpression, project: Project, editor: Editor?) {
if (element !is KtDotQualifiedExpression) return
element.replaceWith( element.replaceWith(
"$0.substringBefore($1)", "$0.substringBefore($1)",
(element.getArgumentExpression(1) as KtDotQualifiedExpression).getArgumentExpression(0) (element.getArgumentExpression(1) as KtDotQualifiedExpression).getArgumentExpression(0)
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.idea.inspections.substring
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.idea.intentions.callExpression import org.jetbrains.kotlin.idea.intentions.callExpression
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
@@ -16,9 +15,8 @@ class ReplaceSubstringWithTakeInspection : ReplaceSubstringInspection() {
override val defaultFixText: String = "Replace 'substring' call with 'take' call" override val defaultFixText: String = "Replace 'substring' call with 'take' call"
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { override fun applyTo(element: KtDotQualifiedExpression, project: Project, editor: Editor?) {
if (element !is KtDotQualifiedExpression) return val argument = element.callExpression?.valueArguments?.elementAtOrNull(1)?.getArgumentExpression() ?: return
val argument = element.callExpression!!.valueArguments[1].getArgumentExpression()!!
element.replaceWith("$0.take($1)", argument) element.replaceWith("$0.take($1)", argument)
} }
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinI
import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.resolve.frontendService import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.idea.util.textRangeIn
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -317,9 +318,9 @@ internal fun KtIfExpression.shouldBeTransformed(): Boolean = when (val condition
else -> false else -> false
} }
fun KtIfExpression.textRange(): TextRange { fun KtIfExpression.fromIfKeywordToRightParenthesisTextRangeInThis(): TextRange {
val rightOffset = rightParenthesis?.endOffset ?: return ifKeyword.textRange val rightOffset = rightParenthesis?.endOffset ?: return ifKeyword.textRangeIn(this)
return TextRange(ifKeyword.startOffset, rightOffset) return TextRange(ifKeyword.startOffset, rightOffset).shiftLeft(startOffset)
} }
private fun KtExpression.checkedExpression() = when (this) { private fun KtExpression.checkedExpression() = when (this) {
@@ -198,7 +198,7 @@ object J2KPostProcessingRegistrarImpl : J2KPostProcessingRegistrar {
if (!isApplicable(tElement)) return null if (!isApplicable(tElement)) return null
return { return {
if (isApplicable(tElement)) { // check availability of the inspection again because something could change if (isApplicable(tElement)) { // check availability of the inspection again because something could change
inspection.applyTo(inspection.inspectionTarget(tElement)) inspection.applyTo(tElement)
} }
} }
} }
@@ -5,10 +5,14 @@
package org.jetbrains.kotlin.idea.util package org.jetbrains.kotlin.idea.util
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.callExpression
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClass import org.jetbrains.kotlin.psi.psiUtil.containingClass
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -32,4 +36,10 @@ fun KtExpression.hasNoSideEffects(): Boolean = when (this) {
is KtStringTemplateExpression -> !hasInterpolation() is KtStringTemplateExpression -> !hasInterpolation()
is KtConstantExpression -> true is KtConstantExpression -> true
else -> ConstantExpressionEvaluator.getConstant(this, analyze(BodyResolveMode.PARTIAL)) != null else -> ConstantExpressionEvaluator.getConstant(this, analyze(BodyResolveMode.PARTIAL)) != null
} }
fun PsiElement.textRangeIn(other: PsiElement): TextRange = textRange.shiftLeft(other.startOffset)
fun KtDotQualifiedExpression.calleeTextRangeInThis(): TextRange? = callExpression?.calleeExpression?.textRangeIn(this)
fun KtNamedDeclaration.nameIdentifierTextRangeInThis(): TextRange? = nameIdentifier?.textRangeIn(this)
@@ -287,7 +287,7 @@ inline fun <reified TElement : PsiElement, TInspection : AbstractApplicabilityBa
if (!isApplicable(tElement)) return null if (!isApplicable(tElement)) return null
return { return {
if (isApplicable(tElement)) { // isApplicableTo availability of the inspection again because something could change if (isApplicable(tElement)) { // isApplicableTo availability of the inspection again because something could change
inspection.applyTo(inspection.inspectionTarget(tElement)) inspection.applyTo(tElement)
} }
} }
} }