IntentionBasedInspection: Removing synchronizing on intention instances

Recreate intention instance in `buildVisitor`
Lock caused contention for UpSource
This commit is contained in:
Pavel V. Talanov
2016-08-18 15:42:55 +03:00
parent 742d7db953
commit a683c2b68d
32 changed files with 239 additions and 229 deletions
@@ -31,34 +31,34 @@ import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.PsiFile
import com.intellij.util.SmartList
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
import kotlin.reflect.KClass
abstract class IntentionBasedInspection<TElement : PsiElement>(
val intentions: List<IntentionBasedInspection.IntentionData<TElement>>,
protected open val problemText: String?,
protected val elementType: Class<TElement>
val intentionInfos: List<IntentionBasedInspection.IntentionData<TElement>>,
protected open val problemText: String?
) : AbstractKotlinInspection() {
constructor(
intention: SelfTargetingRangeIntention<TElement>,
intention: KClass<out SelfTargetingRangeIntention<TElement>>,
problemText: String? = null
) : this(listOf(IntentionData(intention)), problemText, intention.elementType)
) : this(listOf(IntentionData(intention)), problemText)
constructor(
intention: SelfTargetingRangeIntention<TElement>,
intention: KClass<out SelfTargetingRangeIntention<TElement>>,
additionalChecker: (TElement, IntentionBasedInspection<TElement>) -> Boolean,
problemText: String? = null
) : this(listOf(IntentionData(intention, additionalChecker)), problemText, intention.elementType)
) : this(listOf(IntentionData(intention, additionalChecker)), problemText)
constructor(
intention: SelfTargetingRangeIntention<TElement>,
intention: KClass<out SelfTargetingRangeIntention<TElement>>,
additionalChecker: (TElement) -> Boolean,
problemText: String? = null
) : this(listOf(IntentionData(intention, { element, inspection -> additionalChecker(element) } )), problemText, intention.elementType)
) : this(listOf(IntentionData(intention, { element, inspection -> additionalChecker(element) } )), problemText)
data class IntentionData<TElement : PsiElement>(
val intention: SelfTargetingRangeIntention<TElement>,
val intention: KClass<out SelfTargetingRangeIntention<TElement>>,
val additionalChecker: (TElement, IntentionBasedInspection<TElement>) -> Boolean = { element, inspection -> true }
)
@@ -67,6 +67,13 @@ abstract class IntentionBasedInspection<TElement : PsiElement>(
open fun inspectionRange(element: TElement): TextRange? = null
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
val intentionsAndCheckers = intentionInfos.map {
it.intention.constructors.single().call() to it.additionalChecker
}
val elementType = intentionsAndCheckers.map { it.first.elementType }.distinct().singleOrNull()
?: error("$intentionInfos should have the same elementType")
return object : PsiElementVisitor() {
override fun visitElement(element: PsiElement) {
if (!elementType.isInstance(element) || element.textLength == 0) return
@@ -77,21 +84,19 @@ abstract class IntentionBasedInspection<TElement : PsiElement>(
var problemRange: TextRange? = null
var fixes: SmartList<LocalQuickFix>? = null
for ((intention, additionalChecker) in intentions) {
synchronized(intention) {
val range = intention.applicabilityRange(targetElement)?.let { range ->
val elementRange = targetElement.textRange
assert(range in elementRange) { "Wrong applicabilityRange() result for $intention - should be within element's range" }
range.shiftRight(-elementRange.startOffset)
}
for ((intention, additionalChecker) in intentionsAndCheckers) {
val range = intention.applicabilityRange(targetElement)?.let { range ->
val elementRange = targetElement.textRange
assert(range in elementRange) { "Wrong applicabilityRange() result for $intention - should be within element's range" }
range.shiftRight(-elementRange.startOffset)
}
if (range != null && additionalChecker(targetElement, this@IntentionBasedInspection)) {
problemRange = problemRange?.union(range) ?: range
if (fixes == null) {
fixes = SmartList<LocalQuickFix>()
}
fixes!!.add(createQuickFix(intention, additionalChecker, targetElement))
if (range != null && additionalChecker(targetElement, this@IntentionBasedInspection)) {
problemRange = problemRange?.union(range) ?: range
if (fixes == null) {
fixes = SmartList<LocalQuickFix>()
}
fixes!!.add(createQuickFix(intention, additionalChecker, targetElement))
}
}
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.psiUtil.containsInside
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import java.util.*
import kotlin.reflect.KClass
abstract class SelfTargetingIntention<TElement : PsiElement>(
val elementType: Class<TElement>,
@@ -88,14 +89,14 @@ abstract class SelfTargetingIntention<TElement : PsiElement>(
}
protected fun isIntentionBaseInspectionEnabled(project: Project, target: TElement): Boolean {
val inspection = findInspection(javaClass) ?: return false
val inspection = findInspection(this.javaClass.kotlin) ?: return false
val key = HighlightDisplayKey.find(inspection.shortName)
if (!InspectionProjectProfileManager.getInstance(project).getInspectionProfile(target).isToolEnabled(key)) {
return false
}
return inspection.intentions.single { it.intention.javaClass == javaClass }.additionalChecker(target, inspection)
return inspection.intentionInfos.single { it.intention == this.javaClass.kotlin }.additionalChecker(target, inspection)
}
final override fun invoke(project: Project, editor: Editor, file: PsiFile): Unit {
@@ -109,9 +110,9 @@ abstract class SelfTargetingIntention<TElement : PsiElement>(
override fun toString(): String = getText()
companion object {
private val intentionBasedInspections = HashMap<Class<out SelfTargetingIntention<*>>, IntentionBasedInspection<*>?>()
private val intentionBasedInspections = HashMap<KClass<out SelfTargetingIntention<*>>, IntentionBasedInspection<*>?>()
fun <TElement : PsiElement> findInspection(intentionClass: Class<out SelfTargetingIntention<TElement>>): IntentionBasedInspection<TElement>? {
fun <TElement : PsiElement> findInspection(intentionClass: KClass<out SelfTargetingIntention<TElement>>): IntentionBasedInspection<TElement>? {
if (intentionBasedInspections.containsKey(intentionClass)) {
@Suppress("UNCHECKED_CAST")
return intentionBasedInspections[intentionClass] as IntentionBasedInspection<TElement>?
@@ -119,7 +120,7 @@ abstract class SelfTargetingIntention<TElement : PsiElement>(
for (extension in Extensions.getExtensions(LocalInspectionEP.LOCAL_INSPECTION)) {
val inspection = extension.instance as? IntentionBasedInspection<*> ?: continue
if (inspection.intentions.any { it.intention.javaClass == intentionClass }) {
if (inspection.intentionInfos.any { it.intention == intentionClass }) {
intentionBasedInspections[intentionClass] = inspection
@Suppress("UNCHECKED_CAST")
return inspection as IntentionBasedInspection<TElement>
@@ -33,14 +33,13 @@ import org.jetbrains.kotlin.types.isNullabilityFlexible
import javax.swing.JComponent
class HasPlatformTypeInspection(
val intention: SpecifyTypeExplicitlyIntention = SpecifyTypeExplicitlyIntention(),
@JvmField var publicAPIOnly: Boolean = true,
@JvmField var reportPlatformArguments: Boolean = false
) : IntentionBasedInspection<KtCallableDeclaration>(
intention,
SpecifyTypeExplicitlyIntention::class,
{ element, inspection ->
with(inspection as HasPlatformTypeInspection) {
intention.dangerousFlexibleTypeOrNull(element, this.publicAPIOnly, this.reportPlatformArguments) != null
SpecifyTypeExplicitlyIntention.dangerousFlexibleTypeOrNull(element, this.publicAPIOnly, this.reportPlatformArguments) != null
}
}
) {
@@ -50,7 +49,7 @@ class HasPlatformTypeInspection(
override val problemText = "Declaration has platform type. Make the type explicit to prevent subtle bugs."
override fun additionalFixes(element: KtCallableDeclaration): List<LocalQuickFix>? {
val type = intention.dangerousFlexibleTypeOrNull(element, publicAPIOnly, reportPlatformArguments) ?: return null
val type = SpecifyTypeExplicitlyIntention.dangerousFlexibleTypeOrNull(element, publicAPIOnly, reportPlatformArguments) ?: return null
if (type.isNullabilityFlexible()) {
val expression = element.node.findChildByType(KtTokens.EQ)?.psi?.getNextSiblingIgnoringWhitespaceAndComments()
@@ -32,11 +32,9 @@ import org.jetbrains.kotlin.types.isDynamic
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.typeUtil.isUnit
class ConvertLambdaToReferenceInspection(
val intention: ConvertLambdaToReferenceIntention = ConvertLambdaToReferenceIntention()
) : IntentionBasedInspection<KtLambdaExpression>(
intention,
{ it -> intention.shouldSuggestToConvert(it) }
class ConvertLambdaToReferenceInspection() : IntentionBasedInspection<KtLambdaExpression>(
ConvertLambdaToReferenceIntention::class,
{ it -> ConvertLambdaToReferenceIntention.shouldSuggestToConvert(it) }
)
class ConvertLambdaToReferenceIntention : SelfTargetingOffsetIndependentIntention<KtLambdaExpression>(
@@ -79,7 +77,7 @@ class ConvertLambdaToReferenceIntention : SelfTargetingOffsetIndependentIntentio
if (calleeDescriptor.typeParameters.isNotEmpty()) return false
// No references to Java synthetic properties
if (calleeDescriptor is SyntheticJavaPropertyDescriptor) return false
val descriptorHasReceiver = with (calleeDescriptor) {
val descriptorHasReceiver = with(calleeDescriptor) {
// No references to both member / extension
if (dispatchReceiverParameter != null && extensionReceiverParameter != null) return false
dispatchReceiverParameter != null || extensionReceiverParameter != null
@@ -144,34 +142,6 @@ class ConvertLambdaToReferenceIntention : SelfTargetingOffsetIndependentIntentio
}
}
internal fun shouldSuggestToConvert(element: KtLambdaExpression): Boolean {
val body = element.bodyExpression ?: return false
val referenceName = buildReferenceText(body.statements.singleOrNull() ?: return false) ?: return false
return referenceName.length < element.text.length
}
private fun KtCallExpression.getCallReferencedName() = (calleeExpression as? KtNameReferenceExpression)?.getReferencedName()
private fun buildReferenceText(expression: KtExpression): String? {
return when (expression) {
is KtCallExpression -> "::${expression.getCallReferencedName()}"
is KtDotQualifiedExpression -> {
val selector = expression.selectorExpression
val selectorReferenceName = when (selector) {
is KtCallExpression -> selector.getCallReferencedName() ?: return null
is KtNameReferenceExpression -> selector.getReferencedName()
else -> return null
}
val receiver = expression.receiverExpression as? KtNameReferenceExpression ?: return null
val context = receiver.analyze()
val receiverDescriptor = context[REFERENCE_TARGET, receiver] as? ParameterDescriptor ?: return null
val receiverType = receiverDescriptor.type
"$receiverType::$selectorReferenceName"
}
else -> null
}
}
override fun applyTo(element: KtLambdaExpression, editor: Editor?) {
val body = element.bodyExpression ?: return
val referenceName = buildReferenceText(body.statements.singleOrNull() ?: return) ?: return
@@ -221,4 +191,34 @@ class ConvertLambdaToReferenceIntention : SelfTargetingOffsetIndependentIntentio
}
}
}
companion object {
internal fun shouldSuggestToConvert(element: KtLambdaExpression): Boolean {
val body = element.bodyExpression ?: return false
val referenceName = buildReferenceText(body.statements.singleOrNull() ?: return false) ?: return false
return referenceName.length < element.text.length
}
private fun buildReferenceText(expression: KtExpression): String? {
return when (expression) {
is KtCallExpression -> "::${expression.getCallReferencedName()}"
is KtDotQualifiedExpression -> {
val selector = expression.selectorExpression
val selectorReferenceName = when (selector) {
is KtCallExpression -> selector.getCallReferencedName() ?: return null
is KtNameReferenceExpression -> selector.getReferencedName()
else -> return null
}
val receiver = expression.receiverExpression as? KtNameReferenceExpression ?: return null
val context = receiver.analyze()
val receiverDescriptor = context[REFERENCE_TARGET, receiver] as? ParameterDescriptor ?: return null
val receiverType = receiverDescriptor.type
"$receiverType::$selectorReferenceName"
}
else -> null
}
}
private fun KtCallExpression.getCallReferencedName() = (calleeExpression as? KtNameReferenceExpression)?.getReferencedName()
}
}
@@ -29,8 +29,8 @@ import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluat
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class ConvertToStringTemplateInspection : IntentionBasedInspection<KtBinaryExpression>(
ConvertToStringTemplateIntention(),
{ it -> ConvertToStringTemplateIntention().shouldSuggestToConvert(it) }
ConvertToStringTemplateIntention::class,
{ it -> ConvertToStringTemplateIntention.shouldSuggestToConvert(it) }
)
class ConvertToStringTemplateIntention : SelfTargetingOffsetIndependentIntention<KtBinaryExpression>(KtBinaryExpression::class.java, "Convert concatenation to template") {
@@ -47,101 +47,103 @@ class ConvertToStringTemplateIntention : SelfTargetingOffsetIndependentIntention
element.replaced(buildReplacement(element))
}
fun shouldSuggestToConvert(expression: KtBinaryExpression): Boolean {
val entries = buildReplacement(expression).entries
return entries.none { it is KtBlockStringTemplateEntry }
&& !entries.all { it is KtLiteralStringTemplateEntry || it is KtEscapeStringTemplateEntry }
&& entries.count { it is KtLiteralStringTemplateEntry } > 1
&& !expression.textContains('\n')
}
private fun isApplicableToNoParentCheck(expression: KtBinaryExpression): Boolean {
if (expression.operationToken != KtTokens.PLUS) return false
val expressionType = expression.analyze(BodyResolveMode.PARTIAL).getType(expression)
if (!KotlinBuiltIns.isString(expressionType)) return false
return isSuitable(expression)
}
private fun isSuitable(expression: KtExpression): Boolean {
if (expression is KtBinaryExpression && expression.operationToken == KtTokens.PLUS) {
return isSuitable(expression.left ?: return false) && isSuitable(expression.right ?: return false)
companion object {
fun shouldSuggestToConvert(expression: KtBinaryExpression): Boolean {
val entries = buildReplacement(expression).entries
return entries.none { it is KtBlockStringTemplateEntry }
&& !entries.all { it is KtLiteralStringTemplateEntry || it is KtEscapeStringTemplateEntry }
&& entries.count { it is KtLiteralStringTemplateEntry } > 1
&& !expression.textContains('\n')
}
if (PsiUtilCore.hasErrorElementChild(expression)) return false
if (expression.textContains('\n')) return false
return true
}
private fun buildReplacement(expression: KtBinaryExpression): KtStringTemplateExpression {
val rightText = buildText(expression.right, false)
return fold(expression.left, rightText, KtPsiFactory(expression))
}
private fun fold(left: KtExpression?, right: String, factory: KtPsiFactory): KtStringTemplateExpression {
val forceBraces = !right.isEmpty() && right.first() != '$' && right.first().isJavaIdentifierPart()
if (left is KtBinaryExpression && isApplicableToNoParentCheck(left)) {
val leftRight = buildText(left.right, forceBraces)
return fold(left.left, leftRight + right, factory)
private fun buildReplacement(expression: KtBinaryExpression): KtStringTemplateExpression {
val rightText = buildText(expression.right, false)
return fold(expression.left, rightText, KtPsiFactory(expression))
}
else {
val leftText = buildText(left, forceBraces)
return factory.createExpression("\"$leftText$right\"") as KtStringTemplateExpression
}
}
private fun buildText(expr: KtExpression?, forceBraces: Boolean): String {
if (expr == null) return ""
val expression = KtPsiUtil.safeDeparenthesize(expr)
val expressionText = expression.text
when (expression) {
is KtConstantExpression -> {
val bindingContext = expression.analyze(BodyResolveMode.PARTIAL)
val type = bindingContext.getType(expression)!!
private fun fold(left: KtExpression?, right: String, factory: KtPsiFactory): KtStringTemplateExpression {
val forceBraces = !right.isEmpty() && right.first() != '$' && right.first().isJavaIdentifierPart()
if (KotlinBuiltIns.isChar(type)) {
val value = expressionText.removePrefix("'").removeSuffix("'")
return when (value) { // escape double quote and unescape single one
"\"" -> "\\\""
"\\'" -> "'"
else -> value
}
}
val constant = ConstantExpressionEvaluator.getConstant(expression, bindingContext)
val stringValue = constant?.getValue(type).toString()
if (stringValue == expressionText) {
return StringUtil.escapeStringCharacters(stringValue)
}
if (left is KtBinaryExpression && isApplicableToNoParentCheck(left)) {
val leftRight = buildText(left.right, forceBraces)
return fold(left.left, leftRight + right, factory)
}
else {
val leftText = buildText(left, forceBraces)
return factory.createExpression("\"$leftText$right\"") as KtStringTemplateExpression
}
}
is KtStringTemplateExpression -> {
val base = if (expressionText.startsWith("\"\"\"") && expressionText.endsWith("\"\"\"")) {
val unquoted = expressionText.substring(3, expressionText.length - 3)
StringUtil.escapeStringCharacters(unquoted)
}
else {
StringUtil.unquoteString(expressionText)
}
private fun buildText(expr: KtExpression?, forceBraces: Boolean): String {
if (expr == null) return ""
val expression = KtPsiUtil.safeDeparenthesize(expr)
val expressionText = expression.text
when (expression) {
is KtConstantExpression -> {
val bindingContext = expression.analyze(BodyResolveMode.PARTIAL)
val type = bindingContext.getType(expression)!!
if (forceBraces) {
if (base.endsWith('$')) {
return base.dropLast(1) + "\\$"
}
else {
val lastPart = expression.children.lastOrNull()
if (lastPart is KtSimpleNameStringTemplateEntry) {
return base.dropLast(lastPart.textLength) + "\${" + lastPart.text.drop(1) + "}"
if (KotlinBuiltIns.isChar(type)) {
val value = expressionText.removePrefix("'").removeSuffix("'")
return when (value) { // escape double quote and unescape single one
"\"" -> "\\\""
"\\'" -> "'"
else -> value
}
}
val constant = ConstantExpressionEvaluator.getConstant(expression, bindingContext)
val stringValue = constant?.getValue(type).toString()
if (stringValue == expressionText) {
return StringUtil.escapeStringCharacters(stringValue)
}
}
return base
is KtStringTemplateExpression -> {
val base = if (expressionText.startsWith("\"\"\"") && expressionText.endsWith("\"\"\"")) {
val unquoted = expressionText.substring(3, expressionText.length - 3)
StringUtil.escapeStringCharacters(unquoted)
}
else {
StringUtil.unquoteString(expressionText)
}
if (forceBraces) {
if (base.endsWith('$')) {
return base.dropLast(1) + "\\$"
}
else {
val lastPart = expression.children.lastOrNull()
if (lastPart is KtSimpleNameStringTemplateEntry) {
return base.dropLast(lastPart.textLength) + "\${" + lastPart.text.drop(1) + "}"
}
}
}
return base
}
is KtNameReferenceExpression ->
return "$" + (if (forceBraces) "{$expressionText}" else expressionText)
}
is KtNameReferenceExpression ->
return "$" + (if (forceBraces) "{$expressionText}" else expressionText)
return "\${$expressionText}"
}
return "\${$expressionText}"
private fun isApplicableToNoParentCheck(expression: KtBinaryExpression): Boolean {
if (expression.operationToken != KtTokens.PLUS) return false
val expressionType = expression.analyze(BodyResolveMode.PARTIAL).getType(expression)
if (!KotlinBuiltIns.isString(expressionType)) return false
return isSuitable(expression)
}
private fun isSuitable(expression: KtExpression): Boolean {
if (expression is KtBinaryExpression && expression.operationToken == KtTokens.PLUS) {
return isSuitable(expression.left ?: return false) && isSuitable(expression.right ?: return false)
}
if (PsiUtilCore.hasErrorElementChild(expression)) return false
if (expression.textContains('\n')) return false
return true
}
}
}
@@ -25,12 +25,12 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.core.moveCaret
import org.jetbrains.kotlin.idea.core.unblockDocument
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
import org.jetbrains.kotlin.resolve.BindingContext
@@ -42,7 +42,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.types.typeUtil.isUnit
import java.util.*
class DeprecatedCallableAddReplaceWithInspection : IntentionBasedInspection<KtCallableDeclaration>(DeprecatedCallableAddReplaceWithIntention())
class DeprecatedCallableAddReplaceWithInspection : IntentionBasedInspection<KtCallableDeclaration>(DeprecatedCallableAddReplaceWithIntention::class)
class DeprecatedCallableAddReplaceWithIntention : SelfTargetingRangeIntention<KtCallableDeclaration>(
KtCallableDeclaration::class.java, "Add 'replaceWith' argument to specify replacement pattern", "Add 'replaceWith' argument to 'Deprecated' annotation"
@@ -35,7 +35,7 @@ import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
class IfNullToElvisInspection : IntentionBasedInspection<KtIfExpression>(IfNullToElvisIntention())
class IfNullToElvisInspection : IntentionBasedInspection<KtIfExpression>(IfNullToElvisIntention::class)
class IfNullToElvisIntention : SelfTargetingRangeIntention<KtIfExpression>(KtIfExpression::class.java, "Replace 'if' with elvis operator"){
override fun applicabilityRange(element: KtIfExpression): TextRange? {
@@ -24,13 +24,13 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.core.moveFunctionLiteralOutsideParentheses
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
import org.jetbrains.kotlin.idea.inspections.RedundantSamConstructorInspection
import org.jetbrains.kotlin.idea.util.CommentSaver
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils
import org.jetbrains.kotlin.psi.*
@@ -43,7 +43,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType
class ObjectLiteralToLambdaInspection : IntentionBasedInspection<KtObjectLiteralExpression>(ObjectLiteralToLambdaIntention())
class ObjectLiteralToLambdaInspection : IntentionBasedInspection<KtObjectLiteralExpression>(ObjectLiteralToLambdaIntention::class)
class ObjectLiteralToLambdaIntention : SelfTargetingRangeIntention<KtObjectLiteralExpression>(
KtObjectLiteralExpression::class.java,
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.psi.KtAnnotatedExpression
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtPsiFactory
class RemoveAtFromAnnotationArgumentInspection : IntentionBasedInspection<KtAnnotatedExpression>(RemoveAtFromAnnotationArgumentIntention())
class RemoveAtFromAnnotationArgumentInspection : IntentionBasedInspection<KtAnnotatedExpression>(RemoveAtFromAnnotationArgumentIntention::class)
class RemoveAtFromAnnotationArgumentIntention : SelfTargetingOffsetIndependentIntention<KtAnnotatedExpression>(
KtAnnotatedExpression::class.java,
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtStringTemplateEntryWithExpression
import org.jetbrains.kotlin.psi.psiUtil.canPlaceAfterSimpleNameEntry
class RemoveCurlyBracesFromTemplateInspection : IntentionBasedInspection<KtBlockStringTemplateEntry>(RemoveCurlyBracesFromTemplateIntention())
class RemoveCurlyBracesFromTemplateInspection : IntentionBasedInspection<KtBlockStringTemplateEntry>(RemoveCurlyBracesFromTemplateIntention::class)
class RemoveCurlyBracesFromTemplateIntention : SelfTargetingOffsetIndependentIntention<KtBlockStringTemplateEntry>(KtBlockStringTemplateEntry::class.java, "Remove curly braces") {
override fun isApplicableTo(element: KtBlockStringTemplateEntry): Boolean {
@@ -23,8 +23,8 @@ import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.psi.KtSuperExpression
@@ -39,7 +39,7 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.TypeUtils
class RemoveExplicitSuperQualifierInspection : IntentionBasedInspection<KtSuperExpression>(RemoveExplicitSuperQualifierIntention()), CleanupLocalInspectionTool {
class RemoveExplicitSuperQualifierInspection : IntentionBasedInspection<KtSuperExpression>(RemoveExplicitSuperQualifierIntention::class), CleanupLocalInspectionTool {
override val problemHighlightType: ProblemHighlightType
get() = ProblemHighlightType.LIKE_UNUSED_SYMBOL
}
@@ -18,9 +18,9 @@ package org.jetbrains.kotlin.idea.intentions
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.openapi.editor.Editor
import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
@@ -34,7 +34,7 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
class RemoveExplicitTypeArgumentsInspection : IntentionBasedInspection<KtTypeArgumentList>(RemoveExplicitTypeArgumentsIntention()) {
class RemoveExplicitTypeArgumentsInspection : IntentionBasedInspection<KtTypeArgumentList>(RemoveExplicitTypeArgumentsIntention::class) {
override val problemHighlightType: ProblemHighlightType
get() = ProblemHighlightType.LIKE_UNUSED_SYMBOL
}
@@ -25,11 +25,10 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.getStartOffsetIn
import org.jetbrains.kotlin.psi.psiUtil.startOffset
class RemoveSetterParameterTypeInspection(
val intention: RemoveExplicitTypeIntention = RemoveExplicitTypeIntention()
) : IntentionBasedInspection<KtCallableDeclaration>(
intention,
{ it -> intention.isSetterParameter(it) }
class RemoveSetterParameterTypeInspection()
: IntentionBasedInspection<KtCallableDeclaration>(
RemoveExplicitTypeIntention::class,
{ it -> RemoveExplicitTypeIntention.isSetterParameter(it) }
) {
override val problemHighlightType = ProblemHighlightType.LIKE_UNUSED_SYMBOL
@@ -44,11 +43,6 @@ class RemoveExplicitTypeIntention : SelfTargetingRangeIntention<KtCallableDeclar
"Remove explicit type specification"
) {
private val KtParameter.isSetterParameter: Boolean get() = (parent?.parent as? KtPropertyAccessor)?.isSetter ?: false
fun isSetterParameter(element: KtCallableDeclaration) =
element is KtParameter && element.isSetterParameter
override fun applicabilityRange(element: KtCallableDeclaration): TextRange? {
if (element.containingFile is KtCodeFragment) return null
if (element.typeReference == null) return null
@@ -66,4 +60,11 @@ class RemoveExplicitTypeIntention : SelfTargetingRangeIntention<KtCallableDeclar
override fun applyTo(element: KtCallableDeclaration, editor: Editor?) {
element.typeReference = null
}
companion object {
fun isSetterParameter(element: KtCallableDeclaration) =
element is KtParameter && element.isSetterParameter
private val KtParameter.isSetterParameter: Boolean get() = (parent?.parent as? KtPropertyAccessor)?.isSetter ?: false
}
}
@@ -32,8 +32,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class RemoveForLoopIndicesInspection : IntentionBasedInspection<KtForExpression>(
RemoveForLoopIndicesIntention(),
problemText = "Index is not used in the loop body"
RemoveForLoopIndicesIntention::class,
"Index is not used in the loop body"
) {
override val problemHighlightType: ProblemHighlightType
get() = ProblemHighlightType.LIKE_UNUSED_SYMBOL
@@ -30,7 +30,7 @@ private fun KtStringTemplateExpression.singleExpressionOrNull() =
children.singleOrNull()?.children?.firstOrNull() as? KtExpression
class RemoveSingleExpressionStringTemplateInspection : IntentionBasedInspection<KtStringTemplateExpression>(
RemoveSingleExpressionStringTemplateIntention(),
RemoveSingleExpressionStringTemplateIntention::class,
additionalChecker = {
templateExpression ->
templateExpression.singleExpressionOrNull()?.let {
@@ -23,12 +23,15 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
import org.jetbrains.kotlin.idea.conversion.copy.range
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
class RemoveUnnecessaryLateinitInspection : IntentionBasedInspection<KtProperty>(RemoveUnnecessaryLateinitIntention()) {
class RemoveUnnecessaryLateinitInspection : IntentionBasedInspection<KtProperty>(RemoveUnnecessaryLateinitIntention::class) {
override val problemHighlightType = ProblemHighlightType.LIKE_UNUSED_SYMBOL
override val problemText = "Unnecessary lateinit"
@@ -35,7 +35,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class ReplaceWithOperatorAssignmentInspection : IntentionBasedInspection<KtBinaryExpression>(ReplaceWithOperatorAssignmentIntention())
class ReplaceWithOperatorAssignmentInspection : IntentionBasedInspection<KtBinaryExpression>(ReplaceWithOperatorAssignmentIntention::class)
class ReplaceWithOperatorAssignmentIntention : SelfTargetingOffsetIndependentIntention<KtBinaryExpression>(KtBinaryExpression::class.java, "Replace with operator-assignment") {
override fun isApplicableTo(element: KtBinaryExpression): Boolean {
@@ -20,12 +20,12 @@ import com.intellij.codeInsight.CodeInsightUtilCore
import com.intellij.openapi.editor.Editor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.core.moveCaret
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.expressionComparedToNull
import org.jetbrains.kotlin.idea.core.moveCaret
import org.jetbrains.kotlin.idea.util.CommentSaver
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset
@@ -36,7 +36,7 @@ import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
class SimplifyAssertNotNullInspection : IntentionBasedInspection<KtCallExpression>(SimplifyAssertNotNullIntention())
class SimplifyAssertNotNullInspection : IntentionBasedInspection<KtCallExpression>(SimplifyAssertNotNullIntention::class)
class SimplifyAssertNotNullIntention : SelfTargetingOffsetIndependentIntention<KtCallExpression>(
KtCallExpression::class.java,
@@ -26,9 +26,8 @@ import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
class SimplifyBooleanWithConstantsInspection : IntentionBasedInspection<KtBinaryExpression>(SimplifyBooleanWithConstantsIntention())
class SimplifyBooleanWithConstantsInspection : IntentionBasedInspection<KtBinaryExpression>(SimplifyBooleanWithConstantsIntention::class)
class SimplifyBooleanWithConstantsIntention : SelfTargetingOffsetIndependentIntention<KtBinaryExpression>(KtBinaryExpression::class.java, "Simplify boolean expression") {
@@ -37,9 +37,8 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import java.util.*
import kotlin.collections.forEach
class SimplifyForInspection : IntentionBasedInspection<KtForExpression>(SimplifyForIntention())
class SimplifyForInspection : IntentionBasedInspection<KtForExpression>(SimplifyForIntention::class)
class SimplifyForIntention : SelfTargetingRangeIntention<KtForExpression>(
KtForExpression::class.java,
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.lexer.KtSingleValueToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
class SimplifyNegatedBinaryExpressionInspection : IntentionBasedInspection<KtPrefixExpression>(SimplifyNegatedBinaryExpressionIntention())
class SimplifyNegatedBinaryExpressionInspection : IntentionBasedInspection<KtPrefixExpression>(SimplifyNegatedBinaryExpressionIntention::class)
class SimplifyNegatedBinaryExpressionIntention : SelfTargetingRangeIntention<KtPrefixExpression>(KtPrefixExpression::class.java, "Simplify negated binary expression") {
@@ -51,29 +51,6 @@ class SpecifyTypeExplicitlyIntention :
SelfTargetingRangeIntention<KtCallableDeclaration>(KtCallableDeclaration::class.java, "Specify type explicitly"),
LowPriorityAction {
fun dangerousFlexibleTypeOrNull(
declaration: KtCallableDeclaration, publicAPIOnly: Boolean, reportPlatformArguments: Boolean
): KotlinType? {
when (declaration) {
is KtFunction -> if (declaration.isLocal || declaration.hasDeclaredReturnType()) return null
is KtProperty -> if (declaration.isLocal || declaration.typeReference != null) return null
else -> return null
}
if (declaration.containingClassOrObject?.isLocal() ?: false) return null
val callable = declaration.resolveToDescriptorIfAny() as? CallableDescriptor ?: return null
if (publicAPIOnly && !callable.visibility.isPublicAPI) return null
val type = callable.returnType ?: return null
if (reportPlatformArguments) {
if (!type.isFlexibleRecursive()) return null
}
else {
if (!type.isFlexible()) return null
}
return type
}
override fun applicabilityRange(element: KtCallableDeclaration): TextRange? {
if (element.containingFile is KtCodeFragment) return null
if (element is KtFunctionLiteral) return null // TODO: should KtFunctionLiteral be KtCallableDeclaration at all?
@@ -108,6 +85,29 @@ class SpecifyTypeExplicitlyIntention :
private val PropertyDescriptor.setterType: KotlinType?
get() = setter?.valueParameters?.firstOrNull()?.type?.let { if (it.isError) null else it }
fun dangerousFlexibleTypeOrNull(
declaration: KtCallableDeclaration, publicAPIOnly: Boolean, reportPlatformArguments: Boolean
): KotlinType? {
when (declaration) {
is KtFunction -> if (declaration.isLocal || declaration.hasDeclaredReturnType()) return null
is KtProperty -> if (declaration.isLocal || declaration.typeReference != null) return null
else -> return null
}
if (declaration.containingClassOrObject?.isLocal() ?: false) return null
val callable = declaration.resolveToDescriptorIfAny() as? CallableDescriptor ?: return null
if (publicAPIOnly && !callable.visibility.isPublicAPI) return null
val type = callable.returnType ?: return null
if (reportPlatformArguments) {
if (!type.isFlexibleRecursive()) return null
}
else {
if (!type.isFlexible()) return null
}
return type
}
fun getTypeForDeclaration(declaration: KtCallableDeclaration): KotlinType {
val descriptor = declaration.analyze()[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration]
val type = (descriptor as? CallableDescriptor)?.returnType
@@ -55,7 +55,7 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isUnit
class UsePropertyAccessSyntaxInspection : IntentionBasedInspection<KtCallExpression>(UsePropertyAccessSyntaxIntention()), CleanupLocalInspectionTool
class UsePropertyAccessSyntaxInspection : IntentionBasedInspection<KtCallExpression>(UsePropertyAccessSyntaxIntention::class), CleanupLocalInspectionTool
class UsePropertyAccessSyntaxIntention : SelfTargetingOffsetIndependentIntention<KtCallExpression>(KtCallExpression::class.java, "Use property access syntax") {
override fun isApplicableTo(element: KtCallExpression): Boolean {
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.idea.intentions.branchedTransformations.*
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
class IfThenToElvisInspection : IntentionBasedInspection<KtIfExpression>(IfThenToElvisIntention())
class IfThenToElvisInspection : IntentionBasedInspection<KtIfExpression>(IfThenToElvisIntention::class)
class IfThenToElvisIntention : SelfTargetingOffsetIndependentIntention<KtIfExpression>(KtIfExpression::class.java, "Replace 'if' expression with elvis expression") {
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.idea.intentions.branchedTransformations.*
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
class IfThenToSafeAccessInspection : IntentionBasedInspection<KtIfExpression>(IfThenToSafeAccessIntention())
class IfThenToSafeAccessInspection : IntentionBasedInspection<KtIfExpression>(IfThenToSafeAccessIntention::class)
class IfThenToSafeAccessIntention : SelfTargetingOffsetIndependentIntention<KtIfExpression>(KtIfExpression::class.java, "Replace 'if' expression with safe access expression") {
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.idea.intentions.branchedTransformations.getSubjectTo
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.introduceSubject
import org.jetbrains.kotlin.psi.KtWhenExpression
class IntroduceWhenSubjectInspection : IntentionBasedInspection<KtWhenExpression>(IntroduceWhenSubjectIntention())
class IntroduceWhenSubjectInspection : IntentionBasedInspection<KtWhenExpression>(IntroduceWhenSubjectIntention::class)
class IntroduceWhenSubjectIntention : SelfTargetingRangeIntention<KtWhenExpression>(KtWhenExpression::class.java, "Introduce argument to 'when'") {
override fun applicabilityRange(element: KtWhenExpression): TextRange? {
@@ -19,25 +19,25 @@ package org.jetbrains.kotlin.idea.intentions.conventionNameCalls
import com.intellij.codeInsight.intention.HighPriorityAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.intentions.*
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
import org.jetbrains.kotlin.idea.intentions.callExpression
import org.jetbrains.kotlin.idea.intentions.isReceiverExpressionWithValue
import org.jetbrains.kotlin.idea.intentions.toResolvedCall
import org.jetbrains.kotlin.lexer.KtSingleValueToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getLastParentOfTypeInRow
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.util.OperatorNameConventions
class ReplaceCallWithComparisonInspection(
val intention: ReplaceCallWithBinaryOperatorIntention = ReplaceCallWithBinaryOperatorIntention()
) : IntentionBasedInspection<KtDotQualifiedExpression>(
intention,
class ReplaceCallWithComparisonInspection() : IntentionBasedInspection<KtDotQualifiedExpression>(
ReplaceCallWithBinaryOperatorIntention::class,
{ qualifiedExpression ->
val calleeExpression = qualifiedExpression.callExpression?.calleeExpression as? KtSimpleNameExpression
val identifier = calleeExpression?.getReferencedNameAsName()
@@ -42,7 +42,7 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.util.isValidOperator
class ReplaceGetOrSetInspection : IntentionBasedInspection<KtDotQualifiedExpression>(
ReplaceGetOrSetIntention(), ReplaceGetOrSetInspection.additionalChecker
ReplaceGetOrSetIntention::class, ReplaceGetOrSetInspection.additionalChecker
) {
companion object {
@@ -28,9 +28,9 @@ import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.psiUtil.startOffset
class LoopToCallChainInspection : IntentionBasedInspection<KtForExpression>(
listOf(IntentionData(LoopToCallChainIntention()), IntentionData(LoopToLazyCallChainIntention())),
problemText = "Loop can be replaced with stdlib operations",
elementType = KtForExpression::class.java)
listOf(IntentionData(LoopToCallChainIntention::class), IntentionData(LoopToLazyCallChainIntention::class)),
problemText = "Loop can be replaced with stdlib operations"
)
class LoopToCallChainIntention : AbstractLoopToCallChainIntention(lazy = false, text = "Replace with stdlib operations")
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.psi.KtForExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.createExpressionByPattern
class UseWithIndexInspection : IntentionBasedInspection<KtForExpression>(UseWithIndexIntention())
class UseWithIndexInspection : IntentionBasedInspection<KtForExpression>(UseWithIndexIntention::class)
class UseWithIndexIntention : SelfTargetingRangeIntention<KtForExpression>(
KtForExpression::class.java,
@@ -190,7 +190,7 @@ object J2KPostProcessingRegistrar {
private val intention = ConvertToStringTemplateIntention()
override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? {
if (element is KtBinaryExpression && intention.isApplicableTo(element) && intention.shouldSuggestToConvert(element)) {
if (element is KtBinaryExpression && intention.isApplicableTo(element) && ConvertToStringTemplateIntention.shouldSuggestToConvert(element)) {
return { intention.applyTo(element, null) }
}
else {
@@ -20,7 +20,8 @@ import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
import org.jetbrains.kotlin.psi.KtPackageDirective
class PackageDirectoryMismatchInspection: IntentionBasedInspection<KtPackageDirective>(
listOf(IntentionBasedInspection.IntentionData(MoveFileToPackageMatchingDirectoryIntention()), IntentionBasedInspection.IntentionData(ChangePackageToMatchDirectoryIntention())),
"Package directive doesn't match file location",
KtPackageDirective::class.java
listOf(
IntentionBasedInspection.IntentionData(MoveFileToPackageMatchingDirectoryIntention::class),
IntentionBasedInspection.IntentionData(ChangePackageToMatchDirectoryIntention::class)),
"Package directive doesn't match file location"
)