New J2K: split diagnostic based processing from inspection ones

Analysing file file with diagnostics on each step of post-processings is rather expensive operation so the inspection-like post-processings which don't require diagnostic information was separated from ones that need it

Related to #KT-31848
This commit is contained in:
Ilya Kirillov
2019-06-11 11:56:23 +03:00
parent 2f8e83d6b9
commit 59f66d5882
7 changed files with 343 additions and 308 deletions
@@ -9,16 +9,9 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.editor.RangeMarker
import com.intellij.psi.PsiElement
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters2
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
import org.jetbrains.kotlin.idea.core.util.EDT
import org.jetbrains.kotlin.idea.formatter.commitAndUnblockDocument
@@ -29,10 +22,8 @@ import org.jetbrains.kotlin.idea.inspections.conventionNameCalls.ReplaceGetOrSet
import org.jetbrains.kotlin.idea.intentions.*
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnAsymmetricallyIntention
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnIntention
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isTrivialStatementBody
import org.jetbrains.kotlin.idea.quickfix.*
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.j2k.ConverterContext
@@ -40,15 +31,8 @@ import org.jetbrains.kotlin.j2k.PostProcessor
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.parentOfType
import org.jetbrains.kotlin.nj2k.postProcessing.processings.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.types.typeUtil.isSignedOrUnsignedNumberType
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
class NewJ2kPostProcessor : PostProcessor {
override fun insertImport(file: KtFile, fqName: FqName) {
@@ -90,6 +74,124 @@ class NewJ2kPostProcessor : PostProcessor {
}
}
private val errorsFixingDiagnosticBasedPostProcessingGroup =
DiagnosticBasedPostProcessingGroup(
diagnosticBasedProcessing(Errors.REDUNDANT_OPEN_IN_INTERFACE) { element: KtModifierListOwner, _ ->
element.removeModifier(KtTokens.OPEN_KEYWORD)
},
diagnosticBasedProcessing(Errors.PLATFORM_CLASS_MAPPED_TO_KOTLIN) { element: KtDotQualifiedExpression, _ ->
val parent = element.parent as? KtImportDirective ?: return@diagnosticBasedProcessing
parent.delete()
},
diagnosticBasedProcessing(
addExclExclFactoryNoImplicitReceiver(AddExclExclCallFix),
Errors.UNSAFE_CALL,
Errors.UNSAFE_INFIX_CALL,
Errors.UNSAFE_OPERATOR_CALL
),
diagnosticBasedProcessing(
addExclExclFactoryNoImplicitReceiver(MissingIteratorExclExclFixFactory),
Errors.ITERATOR_ON_NULLABLE
),
diagnosticBasedProcessing(
SmartCastImpossibleExclExclFixFactory,
Errors.SMARTCAST_IMPOSSIBLE
),
diagnosticBasedProcessing(
ReplacePrimitiveCastWithNumberConversionFix,
Errors.CAST_NEVER_SUCCEEDS
),
diagnosticBasedProcessing(
ChangeCallableReturnTypeFix.ReturnTypeMismatchOnOverrideFactory,
Errors.RETURN_TYPE_MISMATCH_ON_OVERRIDE
),
diagnosticBasedProcessing(
RemoveModifierFix.createRemoveProjectionFactory(true),
Errors.REDUNDANT_PROJECTION
),
diagnosticBasedProcessing(
AddModifierFix.createFactory(KtTokens.OVERRIDE_KEYWORD),
Errors.VIRTUAL_MEMBER_HIDDEN
),
diagnosticBasedProcessing(
RemoveModifierFix.createRemoveModifierFromListOwnerFactory(KtTokens.OPEN_KEYWORD),
Errors.NON_FINAL_MEMBER_IN_FINAL_CLASS, Errors.NON_FINAL_MEMBER_IN_OBJECT
),
diagnosticBasedProcessing(
MakeVisibleFactory,
Errors.INVISIBLE_MEMBER
),
fixValToVarDiagnosticBasedProcessing,
fixTypeMismatchDiagnosticBasedProcessing
)
private val inspectionLikePostProcessingGroup =
InspectionLikeProcessingGroup(
RemoveRedundantVisibilityModifierProcessing(),
RemoveRedundantModalityModifierProcessing(),
RemoveRedundantConstructorKeywordProcessing(),
RemoveExplicitOpenInInterfaceProcessing(),
generalInspectionBasedProcessing(ExplicitThisInspection()),
RemoveExplicitTypeArgumentsProcessing(),
RemoveRedundantOverrideVisibilityProcessing(),
inspectionBasedProcessing(MoveLambdaOutsideParenthesesInspection()),
generalInspectionBasedProcessing(RedundantCompanionReferenceInspection()),
ConvertToStringTemplateProcessing(),
UsePropertyAccessSyntaxProcessing(),
UninitializedVariableReferenceFromInitializerToThisReferenceProcessing(),
UnresolvedVariableReferenceFromInitializerToThisReferenceProcessing(),
RemoveRedundantSamAdaptersProcessing(),
RemoveRedundantCastToNullableProcessing(),
inspectionBasedProcessing(ReplacePutWithAssignmentInspection()),
UseExpressionBodyProcessing(),
inspectionBasedProcessing(UnnecessaryVariableInspection()),
RemoveExplicitPropertyTypeWithInspectionProcessing(),
generalInspectionBasedProcessing(RedundantUnitReturnTypeInspection()),
JavaObjectEqualsToEqOperatorProcessing(),
RemoveExplicitPropertyTypeProcessing(),
RemoveRedundantNullabilityProcessing(),
generalInspectionBasedProcessing(CanBeValInspection(ignoreNotUsedVals = false)),
inspectionBasedProcessing(FoldInitializerAndIfToElvisInspection()),
generalInspectionBasedProcessing(RedundantSemicolonInspection()),
intentionBasedProcessing(RemoveEmptyClassBodyIntention()),
intentionBasedProcessing(RemoveRedundantCallsOfConversionMethodsIntention()),
inspectionBasedProcessing(JavaMapForEachInspection()),
intentionBasedProcessing(FoldIfToReturnIntention()) { it.then.isTrivialStatementBody() && it.`else`.isTrivialStatementBody() },
intentionBasedProcessing(FoldIfToReturnAsymmetricallyIntention()) {
it.then.isTrivialStatementBody() && (KtPsiUtil.skipTrailingWhitespacesAndComments(
it
) as KtReturnExpression).returnedExpression.isTrivialStatementBody()
},
inspectionBasedProcessing(IfThenToSafeAccessInspection()),
inspectionBasedProcessing(IfThenToElvisInspection(highlightStatement = true)),
inspectionBasedProcessing(SimplifyNegatedBinaryExpressionInspection()),
inspectionBasedProcessing(ReplaceGetOrSetInspection()),
inspectionBasedProcessing(AddOperatorModifierInspection()),
intentionBasedProcessing(ObjectLiteralToLambdaIntention()),
intentionBasedProcessing(AnonymousFunctionToLambdaIntention()),
intentionBasedProcessing(RemoveUnnecessaryParenthesesIntention()),
intentionBasedProcessing(DestructureIntention()),
inspectionBasedProcessing(SimplifyAssertNotNullInspection()),
intentionBasedProcessing(RemoveRedundantCallsOfConversionMethodsIntention()),
generalInspectionBasedProcessing(LiftReturnOrAssignmentInspection(skipLongExpressions = false)),
intentionBasedProcessing(RemoveEmptyPrimaryConstructorIntention()),
generalInspectionBasedProcessing(MayBeConstantInspection()),
RemoveForExpressionLoopParameterTypeProcessing()
)
private val cleaningUpDiagnosticBasedPostProcessingGroup =
DiagnosticBasedPostProcessingGroup(
removeUselessCastDiagnosticBasedProcessing,
removeInnecessaryNotNullAssertionDiagnosticBasedProcessing,
fixValToVarDiagnosticBasedProcessing
)
private val processings: List<NamedPostProcessingGroup> = listOf(
NamedPostProcessingGroup(
"Inferring declarations nullability",
@@ -119,188 +221,9 @@ private val processings: List<NamedPostProcessingGroup> = listOf(
NamedPostProcessingGroup(
"Cleaning up Kotlin code",
listOf(
InspectionLikeProcessingGroup(
RemoveRedundantVisibilityModifierProcessing(),
RemoveRedundantModalityModifierProcessing(),
RemoveRedundantConstructorKeywordProcessing(),
diagnosticBasedProcessing(Errors.REDUNDANT_OPEN_IN_INTERFACE) { element: KtDeclaration, _ ->
element.removeModifier(KtTokens.OPEN_KEYWORD)
},
RemoveExplicitOpenInInterfaceProcessing(),
generalInspectionBasedProcessing(ExplicitThisInspection()),
RemoveExplicitTypeArgumentsProcessing(),
RemoveRedundantOverrideVisibilityProcessing(),
inspectionBasedProcessing(MoveLambdaOutsideParenthesesInspection()),
generalInspectionBasedProcessing(RedundantCompanionReferenceInspection()),
FixObjectStringConcatenationProcessing(),
ConvertToStringTemplateProcessing(),
UsePropertyAccessSyntaxProcessing(),
UninitializedVariableReferenceFromInitializerToThisReferenceProcessing(),
UnresolvedVariableReferenceFromInitializerToThisReferenceProcessing(),
RemoveRedundantSamAdaptersProcessing(),
RemoveRedundantCastToNullableProcessing(),
inspectionBasedProcessing(ReplacePutWithAssignmentInspection()),
UseExpressionBodyProcessing(),
inspectionBasedProcessing(UnnecessaryVariableInspection()),
RemoveExplicitPropertyTypeWithInspectionProcessing(),
generalInspectionBasedProcessing(RedundantUnitReturnTypeInspection()),
JavaObjectEqualsToEqOperatorProcessing(),
RemoveExplicitPropertyTypeProcessing(),
RemoveRedundantNullabilityProcessing(),
generalInspectionBasedProcessing(CanBeValInspection(ignoreNotUsedVals = false)),
inspectionBasedProcessing(FoldInitializerAndIfToElvisInspection()),
generalInspectionBasedProcessing(RedundantSemicolonInspection()),
intentionBasedProcessing(RemoveEmptyClassBodyIntention()),
intentionBasedProcessing(
RemoveRedundantCallsOfConversionMethodsIntention()
),
inspectionBasedProcessing(JavaMapForEachInspection()),
intentionBasedProcessing(FoldIfToReturnIntention()) { it.then.isTrivialStatementBody() && it.`else`.isTrivialStatementBody() },
intentionBasedProcessing(FoldIfToReturnAsymmetricallyIntention()) {
it.then.isTrivialStatementBody() && (KtPsiUtil.skipTrailingWhitespacesAndComments(
it
) as KtReturnExpression).returnedExpression.isTrivialStatementBody()
},
inspectionBasedProcessing(IfThenToSafeAccessInspection()),
inspectionBasedProcessing(IfThenToSafeAccessInspection()),
inspectionBasedProcessing(IfThenToElvisInspection(true)),
inspectionBasedProcessing(SimplifyNegatedBinaryExpressionInspection()),
inspectionBasedProcessing(ReplaceGetOrSetInspection()),
inspectionBasedProcessing(AddOperatorModifierInspection()),
intentionBasedProcessing(ObjectLiteralToLambdaIntention()),
intentionBasedProcessing(AnonymousFunctionToLambdaIntention()),
intentionBasedProcessing(RemoveUnnecessaryParenthesesIntention()),
intentionBasedProcessing(DestructureIntention()),
inspectionBasedProcessing(SimplifyAssertNotNullInspection()),
intentionBasedProcessing(
RemoveRedundantCallsOfConversionMethodsIntention()
),
generalInspectionBasedProcessing(LiftReturnOrAssignmentInspection(skipLongExpressions = false)),
generalInspectionBasedProcessing(MayBeConstantInspection()),
intentionBasedProcessing(RemoveEmptyPrimaryConstructorIntention()),
diagnosticBasedProcessing(Errors.PLATFORM_CLASS_MAPPED_TO_KOTLIN) { element: KtDotQualifiedExpression, _ ->
val parent = element.parent as? KtImportDirective ?: return@diagnosticBasedProcessing
parent.delete()
},
diagnosticBasedProcessing(
Errors.UNSAFE_CALL,
Errors.UNSAFE_INFIX_CALL,
Errors.UNSAFE_OPERATOR_CALL
) { element: PsiElement, diagnostic ->
val action =
AddExclExclCallFix.createActions(diagnostic).singleOrNull()
?: return@diagnosticBasedProcessing
action.invoke(element.project, null, element.containingFile)
},
diagnosticBasedProcessingWithFixFactory(
MissingIteratorExclExclFixFactory,
Errors.ITERATOR_ON_NULLABLE
),
diagnosticBasedProcessingWithFixFactory(
SmartCastImpossibleExclExclFixFactory,
Errors.SMARTCAST_IMPOSSIBLE
),
diagnosticBasedProcessing(Errors.TYPE_MISMATCH) { element: PsiElement, diagnostic ->
@Suppress("UNCHECKED_CAST")
val diagnosticWithParameters =
diagnostic as? DiagnosticWithParameters2<KtExpression, KotlinType, KotlinType>
?: return@diagnosticBasedProcessing
val expectedType = diagnosticWithParameters.a
val realType = diagnosticWithParameters.b
when {
realType.makeNotNullable().isSubtypeOf(expectedType.makeNotNullable())
&& realType.isNullable()
&& !expectedType.isNullable()
-> {
val factory = KtPsiFactory(element)
element.replace(factory.createExpressionByPattern("($0)!!", element.text))
}
element is KtExpression
&& realType.isSignedOrUnsignedNumberType()
&& expectedType.isSignedOrUnsignedNumberType() -> {
val fix = NumberConversionFix(element, expectedType, disableIfAvailable = null)
fix.invoke(element.project, null, element.containingFile)
}
}
},
diagnosticBasedProcessingWithFixFactory(
ReplacePrimitiveCastWithNumberConversionFix,
Errors.CAST_NEVER_SUCCEEDS
),
diagnosticBasedProcessingWithFixFactory(
ChangeCallableReturnTypeFix.ReturnTypeMismatchOnOverrideFactory,
Errors.RETURN_TYPE_MISMATCH_ON_OVERRIDE
),
diagnosticBasedProcessing<KtBinaryExpressionWithTypeRHS>(Errors.USELESS_CAST) { element, _ ->
if (element.left.isNullExpression()) return@diagnosticBasedProcessing
val expression = RemoveUselessCastFix.invoke(element)
val variable = expression.parent as? KtProperty
if (variable != null && expression == variable.initializer && variable.isLocal) {
val ref = ReferencesSearch.search(variable, LocalSearchScope(variable.containingFile)).findAll().singleOrNull()
if (ref != null && ref.element is KtSimpleNameExpression) {
ref.element.replace(expression)
variable.delete()
}
}
},
diagnosticBasedProcessingWithFixFactory(
RemoveModifierFix.createRemoveProjectionFactory(true),
Errors.REDUNDANT_PROJECTION
),
diagnosticBasedProcessingWithFixFactory(
AddModifierFix.createFactory(KtTokens.OVERRIDE_KEYWORD),
Errors.VIRTUAL_MEMBER_HIDDEN
),
diagnosticBasedProcessingWithFixFactory(
RemoveModifierFix.createRemoveModifierFromListOwnerFactory(KtTokens.OPEN_KEYWORD),
Errors.NON_FINAL_MEMBER_IN_FINAL_CLASS, Errors.NON_FINAL_MEMBER_IN_OBJECT
),
diagnosticBasedProcessingWithFixFactory(
MakeVisibleFactory,
Errors.INVISIBLE_MEMBER
),
diagnosticBasedProcessingFactory(
Errors.VAL_REASSIGNMENT, Errors.CAPTURED_VAL_INITIALIZATION, Errors.CAPTURED_MEMBER_VAL_INITIALIZATION
) { element: KtSimpleNameExpression, _: Diagnostic ->
val property = element.mainReference.resolve() as? KtProperty
if (property == null) {
null
} else {
val action = {
if (!property.isVar) {
property.valOrVarKeyword.replace(KtPsiFactory(element.project).createVarKeyword())
}
}
action
}
},
diagnosticBasedProcessing<KtSimpleNameExpression>(Errors.UNNECESSARY_NOT_NULL_ASSERTION) { element, _ ->
val exclExclExpr = element.parent as KtUnaryExpression
val baseExpression = exclExclExpr.baseExpression!!
val context = baseExpression.analyze(BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS)
if (context.diagnostics.forElement(element).any { it.factory == Errors.UNNECESSARY_NOT_NULL_ASSERTION }) {
exclExclExpr.replace(baseExpression)
}
},
diagnosticBasedProcessing<LeafPsiElement>(
Errors.WRONG_MODIFIER_TARGET
) { element, _ ->
if (element.elementType == KtTokens.OPEN_KEYWORD) {
element.parentOfType<KtModifierListOwner>()?.removeModifier(KtTokens.OPEN_KEYWORD)
}
},
RemoveForExpressionLoopParameterTypeProcessing()
)
errorsFixingDiagnosticBasedPostProcessingGroup,
inspectionLikePostProcessingGroup,
cleaningUpDiagnosticBasedPostProcessingGroup
)
),
NamedPostProcessingGroup(
@@ -0,0 +1,100 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.nj2k.postProcessing
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.editor.RangeMarker
import com.intellij.psi.PsiElement
import kotlinx.coroutines.withContext
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.util.EDT
import org.jetbrains.kotlin.idea.core.util.range
import org.jetbrains.kotlin.idea.quickfix.AddExclExclCallFix
import org.jetbrains.kotlin.idea.quickfix.KotlinIntentionActionsFactory
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class DiagnosticBasedPostProcessingGroup(diagnosticBasedProcessings: List<DiagnosticBasedProcessing>) : ProcessingGroup {
constructor(vararg diagnosticBasedProcessings: DiagnosticBasedProcessing) : this(diagnosticBasedProcessings.toList())
private val diagnosticToFix =
diagnosticBasedProcessings.asSequence().flatMap { processing ->
processing.diagnosticFactories.asSequence().map { it to processing::fix }
}.groupBy { it.first }.mapValues { (_, list) ->
list.map { it.second }
}
override suspend fun runProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
withContext(EDT) {
CommandProcessor.getInstance().runUndoTransparentAction {
val diagnostics = runWriteAction { analyzeFileRange(file, rangeMarker) }
for (diagnostic in diagnostics.all()) {
diagnosticToFix[diagnostic.factory]?.forEach { fix ->
runWriteAction { fix(diagnostic) }
}
}
}
}
}
private fun analyzeFileRange(file: KtFile, rangeMarker: RangeMarker?): Diagnostics {
val elements =
if (rangeMarker == null) listOf(file)
else file.elementsInRange(rangeMarker.range!!).filterIsInstance<KtElement>()
return if (elements.isNotEmpty())
file.getResolutionFacade().analyzeWithAllCompilerChecks(elements).bindingContext.diagnostics
else Diagnostics.EMPTY
}
}
interface DiagnosticBasedProcessing {
val diagnosticFactories: List<DiagnosticFactory<*>>
fun fix(diagnostic: Diagnostic)
}
inline fun <reified T : PsiElement> diagnosticBasedProcessing(
vararg diagnosticFactory: DiagnosticFactory<*>,
crossinline fix: (T, Diagnostic) -> Unit
) =
object : DiagnosticBasedProcessing {
override val diagnosticFactories = diagnosticFactory.toList()
override fun fix(diagnostic: Diagnostic) {
val element = diagnostic.psiElement as? T
if (element != null) {
fix(element, diagnostic)
}
}
}
fun diagnosticBasedProcessing(fixFactory: KotlinIntentionActionsFactory, vararg diagnosticFactory: DiagnosticFactory<*>) =
object : DiagnosticBasedProcessing {
override val diagnosticFactories = diagnosticFactory.toList()
override fun fix(diagnostic: Diagnostic) {
val fix = fixFactory.createActions(diagnostic).singleOrNull()
fix?.invoke(diagnostic.psiElement.project, null, diagnostic.psiFile)
}
}
fun addExclExclFactoryNoImplicitReceiver(initialFactory: KotlinSingleIntentionActionFactory) =
object : KotlinSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): IntentionAction? =
initialFactory.createActions(diagnostic).singleOrNull()
?.safeAs<AddExclExclCallFix>()
?.let {
AddExclExclCallFix(diagnostic.psiElement, checkImplicitReceivers = false)
}
}
@@ -88,8 +88,6 @@ class InspectionLikeProcessingGroup(val inspectionLikeProcessings: List<Inspecti
context: NewJ2kConverterContext,
rangeMarker: RangeMarker?
): List<ActionData> {
val diagnostics = analyzeFileRange(file, rangeMarker)
val availableActions = ArrayList<ActionData>()
file.accept(object : PsiRecursiveElementVisitor() {
@@ -101,7 +99,7 @@ class InspectionLikeProcessingGroup(val inspectionLikeProcessings: List<Inspecti
if (rangeResult == RangeFilterResult.PROCESS) {
inspectionLikeProcessings.forEach { processing ->
val action = processing.createAction(element, diagnostics, context.converter.settings)
val action = processing.createAction(element, context.converter.settings)
if (action != null) {
availableActions.add(
ActionData(
@@ -119,17 +117,6 @@ class InspectionLikeProcessingGroup(val inspectionLikeProcessings: List<Inspecti
return availableActions
}
private fun analyzeFileRange(file: KtFile, rangeMarker: RangeMarker?): Diagnostics {
val elements = if (rangeMarker == null)
listOf(file)
else
file.elementsInRange(rangeMarker.range!!).filterIsInstance<KtElement>()
return if (elements.isNotEmpty())
file.getResolutionFacade().analyzeWithAllCompilerChecks(elements).bindingContext.diagnostics
else
Diagnostics.EMPTY
}
private fun rangeFilter(element: PsiElement, rangeMarker: RangeMarker?): RangeFilterResult {
if (rangeMarker == null) return RangeFilterResult.PROCESS
@@ -145,7 +132,7 @@ class InspectionLikeProcessingGroup(val inspectionLikeProcessings: List<Inspecti
}
interface InspectionLikeProcessing {
fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)?
fun createAction(element: PsiElement, settings: ConverterSettings?): (() -> Unit)?
val writeActionNeeded: Boolean
}
@@ -154,7 +141,7 @@ abstract class ApplicabilityBasedInspectionLikeProcessing<E : PsiElement>(privat
protected abstract fun isApplicableTo(element: E, settings: ConverterSettings?): Boolean
protected abstract fun apply(element: E)
final override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
final override fun createAction(element: PsiElement, settings: ConverterSettings?): (() -> Unit)? {
if (!element::class.isSubclassOf(classTag)) return null
@Suppress("UNCHECKED_CAST")
if (!isApplicableTo(element as E, settings)) return null
@@ -176,7 +163,7 @@ inline fun <reified TElement : PsiElement, TIntention : SelfTargetingRangeIntent
// Intention can either need or not need write apply
override val writeActionNeeded = intention.startInWriteAction()
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
override fun createAction(element: PsiElement, settings: ConverterSettings?): (() -> Unit)? {
if (!TElement::class.java.isInstance(element)) return null
val tElement = element as TElement
if (intention.applicabilityRange(tElement) == null) return null
@@ -238,7 +225,7 @@ fun <TInspection : AbstractKotlinInspection> generalInspectionBasedProcessing(
}
}
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
override fun createAction(element: PsiElement, settings: ConverterSettings?): (() -> Unit)? {
val holder = ProblemsHolder(InspectionManager.getInstance(element.project), element.containingFile, false)
val visitor = inspection.buildVisitor(
holder,
@@ -271,7 +258,7 @@ inline fun <reified TElement : PsiElement, TInspection : AbstractApplicabilityBa
return acceptInformationLevel || inspection.inspectionHighlightType(element) != ProblemHighlightType.INFORMATION
}
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
override fun createAction(element: PsiElement, settings: ConverterSettings?): (() -> Unit)? {
if (!TElement::class.java.isInstance(element)) return null
val tElement = element as TElement
if (!isApplicable(tElement)) return null
@@ -282,41 +269,3 @@ inline fun <reified TElement : PsiElement, TInspection : AbstractApplicabilityBa
}
}
}
fun diagnosticBasedProcessingWithFixFactory(
fixFactory: KotlinIntentionActionsFactory,
vararg diagnosticFactory: DiagnosticFactory<*>
): InspectionLikeProcessing =
diagnosticBasedProcessing(*diagnosticFactory) { element: PsiElement, diagnostic: Diagnostic ->
fixFactory.createActions(diagnostic).singleOrNull()
?.invoke(element.project, null, element.containingFile)
}
inline fun <reified TElement : PsiElement> diagnosticBasedProcessing(
vararg diagnosticFactory: DiagnosticFactory<*>,
crossinline fix: (TElement, Diagnostic) -> Unit
) = object : InspectionLikeProcessing {
override val writeActionNeeded = true
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
if (!TElement::class.java.isInstance(element)) return null
val diagnostic = diagnostics.forElement(element).firstOrNull { it.factory in diagnosticFactory } ?: return null
return {
fix(element as TElement, diagnostic)
}
}
}
inline fun <reified TElement : PsiElement> diagnosticBasedProcessingFactory(
vararg diagnosticFactory: DiagnosticFactory<*>,
crossinline fixFactory: (TElement, Diagnostic) -> (() -> Unit)?
) = object : InspectionLikeProcessing {
override val writeActionNeeded = true
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
if (!TElement::class.java.isInstance(element)) return null
val diagnostic = diagnostics.forElement(element).firstOrNull { it.factory in diagnosticFactory } ?: return null
return fixFactory(element as TElement, diagnostic)
}
}
@@ -0,0 +1,85 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.nj2k.postProcessing.processings
import com.intellij.psi.PsiElement
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters2
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
import org.jetbrains.kotlin.idea.quickfix.NumberConversionFix
import org.jetbrains.kotlin.idea.quickfix.RemoveUselessCastFix
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.nj2k.postProcessing.diagnosticBasedProcessing
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.types.typeUtil.isSignedOrUnsignedNumberType
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
val fixValToVarDiagnosticBasedProcessing =
diagnosticBasedProcessing(
Errors.VAL_REASSIGNMENT, Errors.CAPTURED_VAL_INITIALIZATION, Errors.CAPTURED_MEMBER_VAL_INITIALIZATION
) { element: KtSimpleNameExpression, _ ->
val property = element.mainReference.resolve() as? KtProperty ?: return@diagnosticBasedProcessing
if (!property.isVar) {
property.valOrVarKeyword.replace(KtPsiFactory(element.project).createVarKeyword())
}
}
val fixTypeMismatchDiagnosticBasedProcessing =
diagnosticBasedProcessing(Errors.TYPE_MISMATCH) { element: PsiElement, diagnostic ->
@Suppress("UNCHECKED_CAST")
val diagnosticWithParameters =
diagnostic as? DiagnosticWithParameters2<KtExpression, KotlinType, KotlinType>
?: return@diagnosticBasedProcessing
val expectedType = diagnosticWithParameters.a
val realType = diagnosticWithParameters.b
when {
realType.makeNotNullable().isSubtypeOf(expectedType.makeNotNullable())
&& realType.isNullable()
&& !expectedType.isNullable()
-> {
val factory = KtPsiFactory(element)
element.replace(factory.createExpressionByPattern("($0)!!", element.text))
}
element is KtExpression
&& realType.isSignedOrUnsignedNumberType()
&& expectedType.isSignedOrUnsignedNumberType() -> {
val fix = NumberConversionFix(element, expectedType, disableIfAvailable = null)
fix.invoke(element.project, null, element.containingFile)
}
}
}
val removeUselessCastDiagnosticBasedProcessing =
diagnosticBasedProcessing<KtBinaryExpressionWithTypeRHS>(Errors.USELESS_CAST) { element, _ ->
if (element.left.isNullExpression()) return@diagnosticBasedProcessing
val expression = RemoveUselessCastFix.invoke(element)
val variable = expression.parent as? KtProperty
if (variable != null && expression == variable.initializer && variable.isLocal) {
val ref = ReferencesSearch.search(variable, LocalSearchScope(variable.containingFile)).findAll().singleOrNull()
if (ref != null && ref.element is KtSimpleNameExpression) {
ref.element.replace(expression)
variable.delete()
}
}
}
val removeInnecessaryNotNullAssertionDiagnosticBasedProcessing =
diagnosticBasedProcessing<KtSimpleNameExpression>(Errors.UNNECESSARY_NOT_NULL_ASSERTION) { element, _ ->
val exclExclExpr = element.parent as KtUnaryExpression
val baseExpression = exclExclExpr.baseExpression!!
val context = baseExpression.analyze(BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS)
if (context.diagnostics.forElement(element).any { it.factory == Errors.UNNECESSARY_NOT_NULL_ASSERTION }) {
exclExclExpr.replace(baseExpression)
}
}
@@ -7,11 +7,9 @@ package org.jetbrains.kotlin.nj2k.postProcessing.processings
import com.intellij.psi.PsiElement
import com.intellij.psi.search.searches.ReferencesSearch
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
@@ -19,8 +17,13 @@ import org.jetbrains.kotlin.idea.core.implicitModality
import org.jetbrains.kotlin.idea.core.implicitVisibility
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.core.setVisibility
import org.jetbrains.kotlin.idea.inspections.*
import org.jetbrains.kotlin.idea.intentions.*
import org.jetbrains.kotlin.idea.inspections.RedundantExplicitTypeInspection
import org.jetbrains.kotlin.idea.inspections.RedundantSamConstructorInspection
import org.jetbrains.kotlin.idea.inspections.UseExpressionBodyInspection
import org.jetbrains.kotlin.idea.intentions.ConvertToStringTemplateIntention
import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArgumentsIntention
import org.jetbrains.kotlin.idea.intentions.UsePropertyAccessSyntaxIntention
import org.jetbrains.kotlin.idea.intentions.addUseSiteTarget
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.references.mainReference
@@ -30,13 +33,14 @@ import org.jetbrains.kotlin.j2k.ConverterSettings
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.nj2k.parentOfType
import org.jetbrains.kotlin.nj2k.parentsOfType
import org.jetbrains.kotlin.nj2k.postProcessing.*
import org.jetbrains.kotlin.nj2k.postProcessing.ApplicabilityBasedInspectionLikeProcessing
import org.jetbrains.kotlin.nj2k.postProcessing.InspectionLikeProcessing
import org.jetbrains.kotlin.nj2k.postProcessing.generalInspectionBasedProcessing
import org.jetbrains.kotlin.nj2k.postProcessing.resolve
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.utils.addToStdlib.cast
@@ -63,7 +67,7 @@ class RemoveExplicitPropertyTypeProcessing : ApplicabilityBasedInspectionLikePro
}
}
class RemoveRedundantNullabilityProcessing: ApplicabilityBasedInspectionLikeProcessing<KtProperty>(KtProperty::class) {
class RemoveRedundantNullabilityProcessing : ApplicabilityBasedInspectionLikeProcessing<KtProperty>(KtProperty::class) {
override fun isApplicableTo(element: KtProperty, settings: ConverterSettings?): Boolean {
if (!element.isLocal) return false
val typeReference = element.typeReference
@@ -101,7 +105,7 @@ class RemoveExplicitTypeArgumentsProcessing : ApplicabilityBasedInspectionLikePr
class RemoveRedundantOverrideVisibilityProcessing : InspectionLikeProcessing {
override val writeActionNeeded = true
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
override fun createAction(element: PsiElement, settings: ConverterSettings?): (() -> Unit)? {
if (element !is KtCallableDeclaration || !element.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return null
val modifier = element.visibilityModifierType() ?: return null
return { element.setVisibility(modifier) }
@@ -113,7 +117,7 @@ class ConvertToStringTemplateProcessing : InspectionLikeProcessing {
val intention = ConvertToStringTemplateIntention()
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
override fun createAction(element: PsiElement, settings: ConverterSettings?): (() -> Unit)? {
if (element is KtBinaryExpression && intention.isApplicableTo(element) && ConvertToStringTemplateIntention.shouldSuggestToConvert(
element
)
@@ -130,7 +134,7 @@ class UsePropertyAccessSyntaxProcessing : InspectionLikeProcessing {
val intention = UsePropertyAccessSyntaxIntention()
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
override fun createAction(element: PsiElement, settings: ConverterSettings?): (() -> Unit)? {
if (element !is KtCallExpression) return null
val propertyName = intention.detectPropertyNameToUse(element) ?: return null
return { intention.applyTo(element, propertyName, reformat = true) }
@@ -140,7 +144,7 @@ class UsePropertyAccessSyntaxProcessing : InspectionLikeProcessing {
class RemoveRedundantSamAdaptersProcessing : InspectionLikeProcessing {
override val writeActionNeeded = true
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
override fun createAction(element: PsiElement, settings: ConverterSettings?): (() -> Unit)? {
if (element !is KtCallExpression) return null
val expressions = RedundantSamConstructorInspection.samConstructorCallsToBeConverted(element)
@@ -156,7 +160,7 @@ class RemoveRedundantSamAdaptersProcessing : InspectionLikeProcessing {
class UseExpressionBodyProcessing : InspectionLikeProcessing {
override val writeActionNeeded = true
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
override fun createAction(element: PsiElement, settings: ConverterSettings?): (() -> Unit)? {
if (element !is KtPropertyAccessor) return null
val inspection = UseExpressionBodyInspection(convertEmptyToUnit = false)
@@ -173,7 +177,7 @@ class UseExpressionBodyProcessing : InspectionLikeProcessing {
class RemoveRedundantCastToNullableProcessing : InspectionLikeProcessing {
override val writeActionNeeded = true
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
override fun createAction(element: PsiElement, settings: ConverterSettings?): (() -> Unit)? {
if (element !is KtBinaryExpressionWithTypeRHS) return null
val context = element.analyze()
@@ -191,42 +195,12 @@ class RemoveRedundantCastToNullableProcessing : InspectionLikeProcessing {
}
}
class FixObjectStringConcatenationProcessing : InspectionLikeProcessing {
override val writeActionNeeded = true
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
if (element !is KtBinaryExpression ||
element.operationToken != KtTokens.PLUS ||
diagnostics.forElement(element.operationReference).none {
it.factory == Errors.UNRESOLVED_REFERENCE_WRONG_RECEIVER
|| it.factory == Errors.NONE_APPLICABLE
}
)
return null
val bindingContext = element.analyze()
val rightType = element.right?.getType(bindingContext) ?: return null
if (KotlinBuiltIns.isString(rightType)) {
return {
val factory = KtPsiFactory(element)
element.left!!.replace(factory.buildExpression {
appendFixedText("(")
appendExpression(element.left)
appendFixedText(").toString()")
})
}
}
return null
}
}
class UninitializedVariableReferenceFromInitializerToThisReferenceProcessing :
InspectionLikeProcessing {
override val writeActionNeeded = true
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
if (element !is KtSimpleNameExpression || diagnostics.forElement(element).none { it.factory == Errors.UNINITIALIZED_VARIABLE }) return null
override fun createAction(element: PsiElement, settings: ConverterSettings?): (() -> Unit)? {
if (element !is KtSimpleNameExpression || element.mainReference.resolve() == null) return null
val resolved = element.mainReference.resolve() ?: return null
if (resolved.isAncestor(element, strict = true)) {
@@ -246,8 +220,8 @@ class UnresolvedVariableReferenceFromInitializerToThisReferenceProcessing :
InspectionLikeProcessing {
override val writeActionNeeded = true
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
if (element !is KtSimpleNameExpression || diagnostics.forElement(element).none { it.factory == Errors.UNRESOLVED_REFERENCE }) return null
override fun createAction(element: PsiElement, settings: ConverterSettings?): (() -> Unit)? {
if (element !is KtSimpleNameExpression || element.mainReference.resolve() != null) return null
val anonymousObject = element.getParentOfType<KtClassOrObject>(true) ?: return null
@@ -381,10 +355,10 @@ class RemoveExplicitPropertyTypeWithInspectionProcessing :
private val processing =
generalInspectionBasedProcessing(RedundantExplicitTypeInspection())
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
override fun createAction(element: PsiElement, settings: ConverterSettings?): (() -> Unit)? {
if (settings?.specifyLocalVariableTypeByDefault == true) return null
return processing.createAction(element, diagnostics, settings)
return processing.createAction(element, settings)
}
}
+4 -1
View File
@@ -1,4 +1,7 @@
internal class Test {
val int: Int
get() = 10
get() {
val b: Byte = 10
return b.toInt()
}
}
@@ -1,8 +1,9 @@
internal class C {
fun foo(o: Any?) {
if (o is String) {
val l = o.length
val substring = o.substring(l - 2)
val s = o
val l = s.length
val substring = s.substring(l - 2)
}
}
}