From 59f66d5882273df8bd888296423ad150aa89d81e Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Tue, 11 Jun 2019 11:56:23 +0300 Subject: [PATCH] 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 --- .../nj2k/postProcessing/J2kPostProcessor.kt | 319 +++++++----------- .../diagnosticBasedPostProcessing.kt | 100 ++++++ .../inspectionLikePostProcessings.kt | 63 +--- .../processings/diagnosticBasedProcessings.kt | 85 +++++ .../processings/inspeсtionLikeProcessings.kt | 74 ++-- nj2k/testData/newJ2k/issues/kt-794.kt | 5 +- .../RedundantTypeCastAndInline.kt | 5 +- 7 files changed, 343 insertions(+), 308 deletions(-) create mode 100644 nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/diagnosticBasedPostProcessing.kt create mode 100644 nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/diagnosticBasedProcessings.kt diff --git a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/J2kPostProcessor.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/J2kPostProcessor.kt index 42e62bfe650..be8dcc15c62 100644 --- a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/J2kPostProcessor.kt +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/J2kPostProcessor.kt @@ -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 = listOf( NamedPostProcessingGroup( "Inferring declarations nullability", @@ -119,188 +221,9 @@ private val processings: List = 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 - ?: 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(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(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( - Errors.WRONG_MODIFIER_TARGET - ) { element, _ -> - if (element.elementType == KtTokens.OPEN_KEYWORD) { - element.parentOfType()?.removeModifier(KtTokens.OPEN_KEYWORD) - } - }, - RemoveForExpressionLoopParameterTypeProcessing() - ) + errorsFixingDiagnosticBasedPostProcessingGroup, + inspectionLikePostProcessingGroup, + cleaningUpDiagnosticBasedPostProcessingGroup ) ), NamedPostProcessingGroup( diff --git a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/diagnosticBasedPostProcessing.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/diagnosticBasedPostProcessing.kt new file mode 100644 index 00000000000..025a900a8d8 --- /dev/null +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/diagnosticBasedPostProcessing.kt @@ -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) : 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() + + return if (elements.isNotEmpty()) + file.getResolutionFacade().analyzeWithAllCompilerChecks(elements).bindingContext.diagnostics + else Diagnostics.EMPTY + } +} + +interface DiagnosticBasedProcessing { + val diagnosticFactories: List> + fun fix(diagnostic: Diagnostic) +} + +inline fun 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() + ?.let { + AddExclExclCallFix(diagnostic.psiElement, checkImplicitReceivers = false) + } + } + diff --git a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/inspectionLikePostProcessings.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/inspectionLikePostProcessings.kt index e2c04d22058..ddb767f3f29 100644 --- a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/inspectionLikePostProcessings.kt +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/inspectionLikePostProcessings.kt @@ -88,8 +88,6 @@ class InspectionLikeProcessingGroup(val inspectionLikeProcessings: List { - val diagnostics = analyzeFileRange(file, rangeMarker) - val availableActions = ArrayList() file.accept(object : PsiRecursiveElementVisitor() { @@ -101,7 +99,7 @@ class InspectionLikeProcessingGroup(val inspectionLikeProcessings: List - 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() - - 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 Unit)? + fun createAction(element: PsiElement, settings: ConverterSettings?): (() -> Unit)? val writeActionNeeded: Boolean } @@ -154,7 +141,7 @@ abstract class ApplicabilityBasedInspectionLikeProcessing(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 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 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 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 -): InspectionLikeProcessing = - diagnosticBasedProcessing(*diagnosticFactory) { element: PsiElement, diagnostic: Diagnostic -> - fixFactory.createActions(diagnostic).singleOrNull() - ?.invoke(element.project, null, element.containingFile) - } - - -inline fun 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 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) - } -} diff --git a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/diagnosticBasedProcessings.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/diagnosticBasedProcessings.kt new file mode 100644 index 00000000000..bcbc9414b5b --- /dev/null +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/diagnosticBasedProcessings.kt @@ -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 + ?: 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(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(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) + } + } \ No newline at end of file diff --git a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/inspeсtionLikeProcessings.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/inspeсtionLikeProcessings.kt index 9cd9bb07d37..ecd9dc9b833 100644 --- a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/inspeсtionLikeProcessings.kt +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/inspeсtionLikeProcessings.kt @@ -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::class) { +class RemoveRedundantNullabilityProcessing : ApplicabilityBasedInspectionLikeProcessing(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(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) } } diff --git a/nj2k/testData/newJ2k/issues/kt-794.kt b/nj2k/testData/newJ2k/issues/kt-794.kt index 08212a3ec06..92b6418c974 100644 --- a/nj2k/testData/newJ2k/issues/kt-794.kt +++ b/nj2k/testData/newJ2k/issues/kt-794.kt @@ -1,4 +1,7 @@ internal class Test { val int: Int - get() = 10 + get() { + val b: Byte = 10 + return b.toInt() + } } \ No newline at end of file diff --git a/nj2k/testData/newJ2k/postProcessing/RedundantTypeCastAndInline.kt b/nj2k/testData/newJ2k/postProcessing/RedundantTypeCastAndInline.kt index 2f82242497e..92016986ed1 100644 --- a/nj2k/testData/newJ2k/postProcessing/RedundantTypeCastAndInline.kt +++ b/nj2k/testData/newJ2k/postProcessing/RedundantTypeCastAndInline.kt @@ -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) } } } \ No newline at end of file