diff --git a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/J2kPostProcessor.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/J2kPostProcessor.kt index 434dfbf482c..e69de29bb2d 100644 --- a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/J2kPostProcessor.kt +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/J2kPostProcessor.kt @@ -1,230 +0,0 @@ -/* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.nj2k - -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.openapi.util.TextRange -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiRecursiveElementVisitor -import com.intellij.psi.codeStyle.CodeStyleManager -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext -import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade -import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference -import org.jetbrains.kotlin.idea.core.ShortenReferences -import org.jetbrains.kotlin.idea.core.util.EDT -import org.jetbrains.kotlin.idea.core.util.range -import org.jetbrains.kotlin.idea.formatter.commitAndUnblockDocument -import org.jetbrains.kotlin.idea.util.ImportInsertHelper -import org.jetbrains.kotlin.idea.util.application.runReadAction -import org.jetbrains.kotlin.idea.util.application.runWriteAction -import org.jetbrains.kotlin.j2k.ConverterContext -import org.jetbrains.kotlin.j2k.PostProcessor -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.nj2k.nullabilityAnalysis.AnalysisScope -import org.jetbrains.kotlin.nj2k.nullabilityAnalysis.NullabilityAnalysisFacade -import org.jetbrains.kotlin.nj2k.nullabilityAnalysis.nullabilityByUndefinedNullabilityComment -import org.jetbrains.kotlin.nj2k.nullabilityAnalysis.prepareTypeElementByMakingAllTypesNullableConsideringNullabilityComment -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 java.util.* - -class NewJ2kPostProcessor(private val formatCode: Boolean) : PostProcessor { - override fun insertImport(file: KtFile, fqName: FqName) { - ApplicationManager.getApplication().invokeAndWait { - runWriteAction { - val descriptors = file.resolveImportReference(fqName) - descriptors.firstOrNull()?.let { ImportInsertHelper.getInstance(file.project).importDescriptor(file, it) } - } - } - } - - private enum class RangeFilterResult { - SKIP, - GO_INSIDE, - PROCESS - } - - private suspend fun List.runProcessings( - file: KtFile, - converterContext: NewJ2kConverterContext, - rangeMarker: RangeMarker? - ): Boolean { - var modificationStamp: Long? = file.modificationStamp - val elementToActions = runReadAction { - collectAvailableActions(this, file, converterContext, rangeMarker) - } - withContext(EDT) { - CommandProcessor.getInstance().runUndoTransparentAction { - for ((element, action, _, writeActionNeeded) in elementToActions) { - if (element.isValid) { - if (writeActionNeeded) { - runWriteAction { - runAction(action, element) - } - } else { - runAction(action, element) - } - } else { - modificationStamp = null - } - } - } - } - return modificationStamp != file.modificationStamp && elementToActions.isNotEmpty() - } - - private inline fun runAction(action: () -> Unit, element: PsiElement) { - try { - action() - } catch (e: IllegalStateException) { - element.containingFile.commitAndUnblockDocument() - action() - } - } - - - private suspend fun Processing.runProcessings(file: KtFile, converterContext: NewJ2kConverterContext, rangeMarker: RangeMarker?) { - when (this) { - is SingleOneTimeProcessing -> listOf(processing).runProcessings(file, converterContext, rangeMarker) - is RepeatableProcessingGroup -> - do { - val needContinue = processings.runProcessings(file, converterContext, rangeMarker) - } while (needContinue) - is OneTimeProcessingGroup -> - processings.forEach { it.runProcessings(file, converterContext, rangeMarker) } - } - } - - - override fun doAdditionalProcessing(file: KtFile, converterContext: ConverterContext?, rangeMarker: RangeMarker?) { - runBlocking(EDT.ModalityStateElement(ModalityState.defaultModalityState())) { - withContext(EDT) { - NullabilityAnalysisFacade( - converterContext as NewJ2kConverterContext, - getTypeElementNullability = ::nullabilityByUndefinedNullabilityComment, - prepareTypeElement = ::prepareTypeElementByMakingAllTypesNullableConsideringNullabilityComment, - debugPrint = false - ).fixNullability(AnalysisScope(file, rangeMarker)) - } - withContext(EDT) { - CommandProcessor.getInstance().runUndoTransparentAction { - runWriteAction { - if (rangeMarker != null) { - ShortenReferences.DEFAULT.process(file, rangeMarker.startOffset, rangeMarker.endOffset) - } else { - ShortenReferences.DEFAULT.process(file) - } - - } - } - } - NewJ2KPostProcessingRegistrar.mainProcessings.runProcessings(file, converterContext as NewJ2kConverterContext, rangeMarker) - withContext(EDT) { - CommandProcessor.getInstance().runUndoTransparentAction { - runWriteAction { - file.commitAndUnblockDocument() - } - } - } - withContext(EDT) { - runWriteAction { - CommandProcessor.getInstance().runUndoTransparentAction { - val codeStyleManager = CodeStyleManager.getInstance(file.project) - if (rangeMarker != null) { - if (rangeMarker.isValid) { - codeStyleManager.reformatRange(file, rangeMarker.startOffset, rangeMarker.endOffset) - } - } else { - codeStyleManager.reformat(file) - } - Unit - } - } - } - } - } - - - private data class ActionData(val element: PsiElement, val action: () -> Unit, val priority: Int, val writeActionNeeded: Boolean) - - private fun collectAvailableActions( - processings: Collection, - file: KtFile, - context: NewJ2kConverterContext, - rangeMarker: RangeMarker? - ): List { - val diagnostics = analyzeFileRange(file, rangeMarker) - - val availableActions = ArrayList() - - file.accept(object : PsiRecursiveElementVisitor() { - override fun visitElement(element: PsiElement) { - val rangeResult = rangeFilter(element, rangeMarker) - if (rangeResult == RangeFilterResult.SKIP) return - - super.visitElement(element) - - if (rangeResult == RangeFilterResult.PROCESS) { - processings.forEach { processing -> - val action = processing.createAction(element, diagnostics, context.converter.settings) - if (action != null) { - availableActions.add( - ActionData( - element, action, - NewJ2KPostProcessingRegistrar.priority(processing), - processing.writeActionNeeded - ) - ) - } - } - } - } - }) - availableActions.sortBy { it.priority } - return availableActions - } - - 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 - } - - private fun rangeFilter(element: PsiElement, rangeMarker: RangeMarker?): RangeFilterResult { - if (rangeMarker == null) return RangeFilterResult.PROCESS - if (!rangeMarker.isValid) return RangeFilterResult.SKIP - val range = TextRange(rangeMarker.startOffset, rangeMarker.endOffset) - val elementRange = element.textRange - return when { - range.contains(elementRange) -> RangeFilterResult.PROCESS - range.intersects(elementRange) -> RangeFilterResult.GO_INSIDE - else -> RangeFilterResult.SKIP - } - } -} diff --git a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/NewJ2kConveterExtension.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/NewJ2kConveterExtension.kt index 323d3dd52b6..84a5ad300da 100644 --- a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/NewJ2kConveterExtension.kt +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/NewJ2kConveterExtension.kt @@ -2,6 +2,7 @@ package org.jetbrains.kotlin.nj2k import com.intellij.openapi.project.Project import org.jetbrains.kotlin.j2k.* +import org.jetbrains.kotlin.nj2k.postProcessing.NewJ2kPostProcessor class NewJ2kConverterExtension : J2kConverterExtension() { override val isNewJ2k = true @@ -14,5 +15,5 @@ class NewJ2kConverterExtension : J2kConverterExtension() { NewJavaToKotlinConverter(project, settings, services) override fun createPostProcessor(formatCode: Boolean): PostProcessor = - NewJ2kPostProcessor(formatCode) + NewJ2kPostProcessor() } \ No newline at end of file diff --git a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/NewJ2kPostProcessings.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/NewJ2kPostProcessings.kt deleted file mode 100644 index 13938888052..00000000000 --- a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/NewJ2kPostProcessings.kt +++ /dev/null @@ -1,924 +0,0 @@ -/* - * 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 - -import com.intellij.codeInspection.* -import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.project.Project -import com.intellij.psi.PsiComment -import com.intellij.psi.PsiElement -import com.intellij.psi.search.LocalSearchScope -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.diagnostics.Diagnostic -import org.jetbrains.kotlin.diagnostics.DiagnosticFactory -import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters2 -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 -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.formatter.commitAndUnblockDocument -import org.jetbrains.kotlin.idea.inspections.* -import org.jetbrains.kotlin.idea.inspections.branchedTransformations.IfThenToSafeAccessInspection -import org.jetbrains.kotlin.idea.inspections.conventionNameCalls.ReplaceGetOrSetInspection -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.intentions.IfThenToElvisIntention -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.refactoring.fqName.getKotlinFqName -import org.jetbrains.kotlin.idea.references.KtSimpleNameReference -import org.jetbrains.kotlin.idea.references.mainReference -import org.jetbrains.kotlin.idea.references.readWriteAccess -import org.jetbrains.kotlin.idea.util.getResolutionScope -import org.jetbrains.kotlin.j2k.ConverterSettings -import org.jetbrains.kotlin.lexer.KtModifierKeywordToken -import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.nj2k.postProcessing.* -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.resolve.lazy.BodyResolveMode -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.isNullable -import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf -import org.jetbrains.kotlin.types.typeUtil.makeNotNullable -import org.jetbrains.kotlin.utils.addToStdlib.safeAs -import org.jetbrains.kotlin.utils.mapToIndex -import java.util.* -import kotlin.reflect.KClass -import kotlin.reflect.full.isSubclassOf - -interface NewJ2kPostProcessing { - fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? = - createAction(element, diagnostics) - - fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? = - createAction(element, diagnostics, null) - - val writeActionNeeded: Boolean -} - -interface Processing -data class SingleOneTimeProcessing(val processing: NewJ2kPostProcessing) : Processing -data class RepeatableProcessingGroup(val processings: List) : Processing { - constructor(vararg processings: NewJ2kPostProcessing) : this(processings.toList()) -} - -data class OneTimeProcessingGroup(val processings: List) : Processing { - constructor(vararg processings: Processing) : this(processings.toList()) -} - -private abstract class CheckableProcessing(private val classTag: KClass) : NewJ2kPostProcessing { - protected open fun check(element: E): Boolean = - check(element, null) - - protected open fun check(element: E, settings: ConverterSettings?): Boolean = - check(element) - - protected abstract fun action(element: E) - - override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? { - if (!element::class.isSubclassOf(classTag)) return null - @Suppress("UNCHECKED_CAST") - if (!check(element as E, settings)) return null - return { - if (element.isValid && check(element, settings)) { - action(element) - } - } - } - - override val writeActionNeeded: Boolean = true -} - -object NewJ2KPostProcessingRegistrar { - - private fun Processing.processings(): Sequence = - when (this) { - is SingleOneTimeProcessing -> sequenceOf(processing) - is RepeatableProcessingGroup -> processings.asSequence() - is OneTimeProcessingGroup -> processings.asSequence().flatMap { it.processings() } - else -> sequenceOf() - } - - - private val processingsToPriorityMap = HashMap() - - fun priority(processing: NewJ2kPostProcessing): Int = processingsToPriorityMap[processing]!! - - val mainProcessings = OneTimeProcessingGroup( - SingleOneTimeProcessing(VarToVal()), - SingleOneTimeProcessing(ConvertGettersAndSetters()), - SingleOneTimeProcessing(MoveGetterAndSetterAnnotationsToProperty()), - SingleOneTimeProcessing(registerGeneralInspectionBasedProcessing(RedundantGetterInspection())), - SingleOneTimeProcessing(registerGeneralInspectionBasedProcessing(RedundantSetterInspection())), - SingleOneTimeProcessing(ConvertDataClass()), - RepeatableProcessingGroup( - RemoveRedundantVisibilityModifierProcessing(), - RemoveRedundantModalityModifierProcessing(), - RemoveRedundantConstructorKeywordProcessing(), - registerDiagnosticBasedProcessing(Errors.REDUNDANT_OPEN_IN_INTERFACE) { element: KtDeclaration, _ -> - element.removeModifier(KtTokens.OPEN_KEYWORD) - }, - object : NewJ2kPostProcessing { - override val writeActionNeeded: Boolean = true - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (element !is KtClass) return null - - fun check(klass: KtClass): Boolean { - return klass.isValid - && klass.isInterface() - && klass.hasModifier(KtTokens.OPEN_KEYWORD) - } - - if (!check(element)) return null - return { - if (check(element)) { - element.removeModifier(KtTokens.OPEN_KEYWORD) - } - } - } - }, - - registerGeneralInspectionBasedProcessing(ExplicitThisInspection()), - RemoveExplicitTypeArgumentsProcessing(), - RemoveRedundantOverrideVisibilityProcessing(), - registerInspectionBasedProcessing(MoveLambdaOutsideParenthesesInspection()), - registerGeneralInspectionBasedProcessing(RedundantCompanionReferenceInspection()), - FixObjectStringConcatenationProcessing(), - ConvertToStringTemplateProcessing(), - UsePropertyAccessSyntaxProcessing(), - UninitializedVariableReferenceFromInitializerToThisReferenceProcessing(), - UnresolvedVariableReferenceFromInitializerToThisReferenceProcessing(), - RemoveRedundantSamAdaptersProcessing(), - RemoveRedundantCastToNullableProcessing(), - registerInspectionBasedProcessing(ReplacePutWithAssignmentInspection()), - UseExpressionBodyProcessing(), - registerInspectionBasedProcessing(UnnecessaryVariableInspection()), - - object : NewJ2kPostProcessing { - override val writeActionNeeded: Boolean = true - private val processing = registerGeneralInspectionBasedProcessing(RedundantExplicitTypeInspection()) - - override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? { - if (settings?.specifyLocalVariableTypeByDefault == true) return null - - return processing.createAction(element, diagnostics) - } - } - , - registerGeneralInspectionBasedProcessing(RedundantUnitReturnTypeInspection()), - JavaObjectEqualsToEqOperator(), - RemoveExplicitPropertyType(), - RemoveRedundantNullability(), - - registerGeneralInspectionBasedProcessing(CanBeValInspection(ignoreNotUsedVals = false)), - - registerIntentionBasedProcessing(FoldInitializerAndIfToElvisIntention()), - registerGeneralInspectionBasedProcessing(RedundantSemicolonInspection()), - registerIntentionBasedProcessing(RemoveEmptyClassBodyIntention()), - registerIntentionBasedProcessing(RemoveRedundantCallsOfConversionMethodsIntention()), - registerInspectionBasedProcessing(JavaMapForEachInspection()), - - registerIntentionBasedProcessing(FoldIfToReturnIntention()) { it.then.isTrivialStatementBody() && it.`else`.isTrivialStatementBody() }, - registerIntentionBasedProcessing(FoldIfToReturnAsymmetricallyIntention()) { - it.then.isTrivialStatementBody() && (KtPsiUtil.skipTrailingWhitespacesAndComments( - it - ) as KtReturnExpression).returnedExpression.isTrivialStatementBody() - }, - - - registerInspectionBasedProcessing(IfThenToSafeAccessInspection()), - registerInspectionBasedProcessing(IfThenToSafeAccessInspection()), - registerIntentionBasedProcessing(IfThenToElvisIntention()), - registerInspectionBasedProcessing(SimplifyNegatedBinaryExpressionInspection()), - registerInspectionBasedProcessing(ReplaceGetOrSetInspection()), - registerIntentionBasedProcessing(AddOperatorModifierIntention()), - registerIntentionBasedProcessing(ObjectLiteralToLambdaIntention()), - registerIntentionBasedProcessing(AnonymousFunctionToLambdaIntention()), - registerIntentionBasedProcessing(RemoveUnnecessaryParenthesesIntention()), - registerIntentionBasedProcessing(DestructureIntention()), - registerInspectionBasedProcessing(SimplifyAssertNotNullInspection()), - registerIntentionBasedProcessing(RemoveRedundantCallsOfConversionMethodsIntention()), - registerGeneralInspectionBasedProcessing(LiftReturnOrAssignmentInspection()), - registerGeneralInspectionBasedProcessing(MayBeConstantInspection()), - registerIntentionBasedProcessing(RemoveEmptyPrimaryConstructorIntention()), - registerDiagnosticBasedProcessing(Errors.PLATFORM_CLASS_MAPPED_TO_KOTLIN) { element: KtDotQualifiedExpression, _ -> - val parent = element.parent as? KtImportDirective ?: return@registerDiagnosticBasedProcessing - parent.delete() - }, - - registerDiagnosticBasedProcessing( - Errors.UNSAFE_CALL, - Errors.UNSAFE_INFIX_CALL, - Errors.UNSAFE_OPERATOR_CALL - ) { element: PsiElement, diagnostic -> - val action = - AddExclExclCallFix.createActions(diagnostic).singleOrNull() - ?: return@registerDiagnosticBasedProcessing - action.invoke(element.project, null, element.containingFile) - }, - - registerDiagnosticBasedProcessingWithFixFactory(MissingIteratorExclExclFixFactory, Errors.ITERATOR_ON_NULLABLE), - registerDiagnosticBasedProcessingWithFixFactory(SmartCastImpossibleExclExclFixFactory, Errors.SMARTCAST_IMPOSSIBLE), - registerDiagnosticBasedProcessing(Errors.TYPE_MISMATCH) { element: PsiElement, diagnostic -> - @Suppress("UNCHECKED_CAST") val diagnosticWithParameters = - diagnostic as? DiagnosticWithParameters2 - ?: return@registerDiagnosticBasedProcessing - val expectedType = diagnosticWithParameters.a - val realType = diagnosticWithParameters.b - if (realType.makeNotNullable().isSubtypeOf(expectedType.makeNotNullable()) - && realType.isNullable() - && !expectedType.isNullable() - ) { - val factory = KtPsiFactory(element) - element.replace(factory.createExpressionByPattern("($0)!!", element.text)) - } - }, - - registerDiagnosticBasedProcessingWithFixFactory( - ReplacePrimitiveCastWithNumberConversionFix.Factory, - Errors.CAST_NEVER_SUCCEEDS - ), - registerDiagnosticBasedProcessingWithFixFactory( - ChangeCallableReturnTypeFix.ReturnTypeMismatchOnOverrideFactory, - Errors.RETURN_TYPE_MISMATCH_ON_OVERRIDE - ), - - RemoveRedundantTypeQualifierProcessing(), - RemoveRedundantExpressionQualifierProcessing(), - - registerDiagnosticBasedProcessing(Errors.USELESS_CAST) { element, _ -> - if (element.left.isNullExpression()) return@registerDiagnosticBasedProcessing - 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() - } - } - }, - - registerDiagnosticBasedProcessingWithFixFactory( - RemoveModifierFix.createRemoveProjectionFactory(true), - Errors.REDUNDANT_PROJECTION - ), - registerDiagnosticBasedProcessingWithFixFactory( - AddModifierFix.createFactory(KtTokens.OVERRIDE_KEYWORD), - Errors.VIRTUAL_MEMBER_HIDDEN - ), - registerDiagnosticBasedProcessingWithFixFactory( - RemoveModifierFix.createRemoveModifierFromListOwnerFactory(KtTokens.OPEN_KEYWORD), - Errors.NON_FINAL_MEMBER_IN_FINAL_CLASS, Errors.NON_FINAL_MEMBER_IN_OBJECT - ), - registerDiagnosticBasedProcessingWithFixFactory( - MakeVisibleFactory, - Errors.INVISIBLE_MEMBER - ), - - registerDiagnosticBasedProcessingFactory( - 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 - } - }, - - registerDiagnosticBasedProcessing(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) - } - }, - RemoveForExpressionLoopParameterTypeProcessing() - ) - ) - - val processings: Collection = - mainProcessings.processings().toList() - - - init { - processingsToPriorityMap.putAll(processings.mapToIndex()) - } - - - private inline fun > registerIntentionBasedProcessing( - intention: TIntention, - noinline additionalChecker: (TElement) -> Boolean = { true } - ) = object : NewJ2kPostProcessing { - // Intention can either need or not need write action - override val writeActionNeeded = intention.startInWriteAction() - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (!TElement::class.java.isInstance(element)) return null - val tElement = element as TElement - if (intention.applicabilityRange(tElement) == null) return null - if (!additionalChecker(tElement)) return null - return { - if (intention.applicabilityRange(tElement) != null) { // check availability of the intention again because something could change - intention.applyTo(element, null) - } - } - } - } - - - private fun registerGeneralInspectionBasedProcessing( - inspection: TInspection, - acceptInformationLevel: Boolean = false - ) = (object : NewJ2kPostProcessing { - override val writeActionNeeded = false - - fun QuickFix.applyFixSmart(project: Project, descriptor: D) { - if (descriptor is ProblemDescriptor) { - if (this is IntentionWrapper) { - @Suppress("NOT_YET_SUPPORTED_IN_INLINE") - fun applySelfTargetingIntention(action: SelfTargetingIntention) { - val target = action.getTarget(descriptor.psiElement.startOffset, descriptor.psiElement.containingFile) ?: return - if (!action.isApplicableTo(target, descriptor.psiElement.startOffset)) return - action.applyTo(target, null) - } - - @Suppress("NOT_YET_SUPPORTED_IN_INLINE") - fun applyQuickFixActionBase(action: QuickFixActionBase) { - if (!action.isAvailable(project, null, descriptor.psiElement.containingFile)) return - action.invoke(project, null, descriptor.psiElement.containingFile) - } - - - @Suppress("NOT_YET_SUPPORTED_IN_INLINE") - fun applyIntention() { - @Suppress("UNCHECKED_CAST") - when (val action = this.action) { - is SelfTargetingIntention<*> -> applySelfTargetingIntention(action as SelfTargetingIntention) - is QuickFixActionBase<*> -> applyQuickFixActionBase(action) - } - } - - - if (this.startInWriteAction()) { - ApplicationManager.getApplication().runWriteAction(::applyIntention) - } else { - applyIntention() - } - - } - } - - ApplicationManager.getApplication().runWriteAction { - this.applyFix(project, descriptor) - } - } - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - val holder = ProblemsHolder(InspectionManager.getInstance(element.project), element.containingFile, false) - val visitor = inspection.buildVisitor( - holder, - false, - LocalInspectionToolSession(element.containingFile, 0, element.containingFile.endOffset) - ) - element.accept(visitor) - if (!holder.hasResults()) return null - return { - holder.results.clear() - element.accept(visitor) - if (holder.hasResults()) { - holder.results - .filter { acceptInformationLevel || it.highlightType != ProblemHighlightType.INFORMATION } - .forEach { it.fixes?.firstOrNull()?.applyFixSmart(element.project, it) } - } - } - } - }) - - - private inline fun - > registerInspectionBasedProcessing( - - inspection: TInspection, - acceptInformationLevel: Boolean = false - ) = object : NewJ2kPostProcessing { - // Inspection can either need or not need write action - override val writeActionNeeded = inspection.startFixInWriteAction - - private fun isApplicable(element: TElement): Boolean { - if (!inspection.isApplicable(element)) return false - return acceptInformationLevel || inspection.inspectionHighlightType(element) != ProblemHighlightType.INFORMATION - } - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (!TElement::class.java.isInstance(element)) return null - val tElement = element as TElement - if (!isApplicable(tElement)) return null - return { - if (isApplicable(tElement)) { // check availability of the inspection again because something could change - inspection.applyTo(inspection.inspectionTarget(tElement)) - } - } - } - } - - private fun registerDiagnosticBasedProcessingWithFixFactory( - fixFactory: KotlinIntentionActionsFactory, - vararg diagnosticFactory: DiagnosticFactory<*> - ): NewJ2kPostProcessing = registerDiagnosticBasedProcessing(*diagnosticFactory) { element: PsiElement, diagnostic: Diagnostic -> - fixFactory.createActions(diagnostic).singleOrNull() - ?.invoke(element.project, null, element.containingFile) - } - - - private inline fun registerDiagnosticBasedProcessing( - vararg diagnosticFactory: DiagnosticFactory<*>, - crossinline fix: (TElement, Diagnostic) -> Unit - ) = object : NewJ2kPostProcessing { - override val writeActionNeeded = true - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> 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) - } - } - } - - private inline fun registerDiagnosticBasedProcessingFactory( - vararg diagnosticFactory: DiagnosticFactory<*>, - crossinline fixFactory: (TElement, Diagnostic) -> (() -> Unit)? - ) = object : NewJ2kPostProcessing { - override val writeActionNeeded = true - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> 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) - } - } - - - private class RemoveExplicitPropertyType : NewJ2kPostProcessing { - override val writeActionNeeded = true - - override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? { - if (element !is KtProperty) return null - val needFieldTypes = settings?.specifyFieldTypeByDefault == true - val needLocalVariablesTypes = settings?.specifyLocalVariableTypeByDefault == true - - fun check(element: KtProperty): Boolean { - if (needLocalVariablesTypes && element.isLocal) return false - if (needFieldTypes && element.isMember) return false - val initializer = element.initializer ?: return false - val withoutExpectedType = - initializer.analyzeInContext(initializer.getResolutionScope()).getType(initializer) ?: return false - val descriptor = element.resolveToDescriptorIfAny() as? CallableDescriptor ?: return false - return if (element.isVar) withoutExpectedType == descriptor.returnType - else withoutExpectedType.makeNotNullable() == descriptor.returnType?.makeNotNullable() - } - - if (!check(element)) { - return null - } else { - return { - if (element.isValid && check(element)) { - element.typeReference = null - } - } - } - } - } - - - private class RemoveRedundantNullability : NewJ2kPostProcessing { - override val writeActionNeeded: Boolean = true - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (element !is KtProperty) return null - - fun check(element: KtProperty): Boolean { - if (!element.isLocal) return false - val typeReference = element.typeReference - if (typeReference == null || typeReference.typeElement !is KtNullableType) return false - val initializerType = element.initializer?.let { - it.analyzeInContext(element.getResolutionScope()).getType(it) - } - if (initializerType?.isNullable() == true) return false - - return ReferencesSearch.search(element, element.useScope).findAll().mapNotNull { ref -> - val parent = (ref.element.parent as? KtExpression)?.asAssignment() - parent?.takeIf { it.left == ref.element } - }.all { - val right = it.right - val withoutExpectedType = right?.analyzeInContext(element.getResolutionScope()) - withoutExpectedType?.getType(right)?.isNullable() == false - } - } - - if (!check(element)) { - return null - } else { - return { - val typeElement = element.typeReference?.typeElement - if (element.isValid && check(element) && typeElement != null && typeElement is KtNullableType) { - typeElement.replace(typeElement.innerType!!) - } - } - } - } - } - - private class RemoveExplicitTypeArgumentsProcessing : NewJ2kPostProcessing { - override val writeActionNeeded = true - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (element !is KtTypeArgumentList || !RemoveExplicitTypeArgumentsIntention.isApplicableTo( - element, - approximateFlexible = true - ) - ) return null - - return { - if (RemoveExplicitTypeArgumentsIntention.isApplicableTo(element, approximateFlexible = true)) { - element.delete() - } - } - } - } - - private class RemoveRedundantOverrideVisibilityProcessing : NewJ2kPostProcessing { - override val writeActionNeeded = true - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (element !is KtCallableDeclaration || !element.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return null - val modifier = element.visibilityModifierType() ?: return null - return { element.setVisibility(modifier) } - } - } - - private class ConvertToStringTemplateProcessing : NewJ2kPostProcessing { - override val writeActionNeeded = true - - private val intention = ConvertToStringTemplateIntention() - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (element is KtBinaryExpression && intention.isApplicableTo(element) && ConvertToStringTemplateIntention.shouldSuggestToConvert( - element - ) - ) { - return { intention.applyTo(element, null) } - } else { - return null - } - } - } - - private class UsePropertyAccessSyntaxProcessing : NewJ2kPostProcessing { - override val writeActionNeeded = true - - private val intention = UsePropertyAccessSyntaxIntention() - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (element !is KtCallExpression) return null - val propertyName = intention.detectPropertyNameToUse(element) ?: return null - return { intention.applyTo(element, propertyName, reformat = true) } - } - } - - private class RemoveRedundantSamAdaptersProcessing : NewJ2kPostProcessing { - override val writeActionNeeded = true - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (element !is KtCallExpression) return null - - val expressions = RedundantSamConstructorInspection.samConstructorCallsToBeConverted(element) - if (expressions.isEmpty()) return null - - return { - RedundantSamConstructorInspection.samConstructorCallsToBeConverted(element) - .forEach { RedundantSamConstructorInspection.replaceSamConstructorCall(it) } - } - } - } - - private class UseExpressionBodyProcessing : NewJ2kPostProcessing { - override val writeActionNeeded = true - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (element !is KtPropertyAccessor) return null - - val inspection = UseExpressionBodyInspection(convertEmptyToUnit = false) - if (!inspection.isActiveFor(element)) return null - - return { - if (inspection.isActiveFor(element)) { - inspection.simplify(element, false) - } - } - } - } - - private class RemoveRedundantCastToNullableProcessing : NewJ2kPostProcessing { - override val writeActionNeeded = true - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (element !is KtBinaryExpressionWithTypeRHS) return null - - val context = element.analyze() - val leftType = context.getType(element.left) ?: return null - val rightType = context.get(BindingContext.TYPE, element.right) ?: return null - - if (!leftType.isMarkedNullable && rightType.isMarkedNullable) { - return { - val type = element.right?.typeElement as? KtNullableType - type?.replace(type.innerType!!) - } - } - - return null - } - } - - private class FixObjectStringConcatenationProcessing : NewJ2kPostProcessing { - override val writeActionNeeded = true - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> 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 - } - } - - private class UninitializedVariableReferenceFromInitializerToThisReferenceProcessing : NewJ2kPostProcessing { - override val writeActionNeeded = true - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (element !is KtSimpleNameExpression || diagnostics.forElement(element).none { it.factory == Errors.UNINITIALIZED_VARIABLE }) return null - - val resolved = element.mainReference.resolve() ?: return null - if (resolved.isAncestor(element, strict = true)) { - if (resolved is KtVariableDeclaration && resolved.hasInitializer()) { - val anonymousObject = element.getParentOfType(true) ?: return null - if (resolved.initializer!!.getChildOfType() == anonymousObject) { - return { element.replaced(KtPsiFactory(element).createThisExpression()) } - } - } - } - - return null - } - } - - private class UnresolvedVariableReferenceFromInitializerToThisReferenceProcessing : NewJ2kPostProcessing { - override val writeActionNeeded = true - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (element !is KtSimpleNameExpression || diagnostics.forElement(element).none { it.factory == Errors.UNRESOLVED_REFERENCE }) return null - - val anonymousObject = element.getParentOfType(true) ?: return null - - val variable = anonymousObject.getParentOfType(true) ?: return null - - if (variable.nameAsName == element.getReferencedNameAsName() && - variable.initializer?.getChildOfType() == anonymousObject - ) { - return { element.replaced(KtPsiFactory(element).createThisExpression()) } - } - - return null - } - } - - private class VarToVal : NewJ2kPostProcessing { - override val writeActionNeeded = true - - private fun KtProperty.hasWriteUsages(): Boolean = - ReferencesSearch.search(this, useScope).any { usage -> - (usage as? KtSimpleNameReference)?.element?.let { - it.readWriteAccess(useResolveForReadWrite = true).isWrite - && it.parentOfType() == null//TODO properly check - } == true - } - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (element !is KtProperty) return null - - fun check(element: KtProperty): Boolean { - if (!element.isVar) return false - if (!element.isPrivate()) return false - val descriptor = element.resolveToDescriptorIfAny() ?: return false - if (descriptor.overriddenDescriptors.any { it.safeAs()?.isVar == true }) return false - return !element.hasWriteUsages() - } - - if (!check(element)) { - return null - } else { - return { - if (element.isValid && check(element)) { - val factory = KtPsiFactory(element) - element.valOrVarKeyword.replace(factory.createValKeyword()) - println() - } - } - } - } - } - - private class JavaObjectEqualsToEqOperator : NewJ2kPostProcessing { - companion object { - private val CALL_FQ_NAME = FqName("java.util.Objects.equals") - } - - private fun check(callExpression: KtCallExpression): Boolean { - if (callExpression.valueArguments.size != 2) return false - if (callExpression.valueArguments.any { it.getArgumentExpression() == null }) return false - val target = callExpression.calleeExpression - .safeAs() - ?.resolve() - ?: return false - return target.getKotlinFqName() == CALL_FQ_NAME - } - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (element !is KtCallExpression) return null - if (!check(element)) return null - return { - if (element.isValid() && check(element)) { - val factory = KtPsiFactory(element) - element.getQualifiedExpressionForSelectorOrThis().replace( - factory.createExpressionByPattern( - "($0 == $1)", - element.valueArguments[0].getArgumentExpression()!!, - element.valueArguments[1].getArgumentExpression()!! - ) - ) - } - } - } - - override val writeActionNeeded: Boolean = true - } - - private class RemoveForExpressionLoopParameterTypeProcessing : CheckableProcessing(KtForExpression::class) { - override fun check(element: KtForExpression, settings: ConverterSettings?): Boolean = - element.loopParameter?.typeReference?.typeElement != null - && settings?.specifyLocalVariableTypeByDefault != true - - override fun action(element: KtForExpression) { - element.loopParameter?.typeReference = null - } - - } - - private class RemoveRedundantExpressionQualifierProcessing : NewJ2kPostProcessing { - private fun check(qualifiedExpression: KtQualifiedExpression): Boolean { - val qualifier = (qualifiedExpression.receiverExpression as? KtNameReferenceExpression) - ?.referenceExpression() - ?.resolve()?.safeAs()?.parentClassForCompanionOrThis() ?: return false - return qualifier.parentClassForCompanionOrThis() in qualifiedExpression.parentsOfType() - } - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (element !is KtQualifiedExpression) return null - if (!check(element)) return null - return { - if (element.isValid() && check(element)) { - element.replace(element.selectorExpression!!) - } - } - } - - override val writeActionNeeded: Boolean = true - } - - private class RemoveRedundantTypeQualifierProcessing : NewJ2kPostProcessing { - private fun check(reference: KtUserType): Boolean { - val qualifierClass = reference.qualifier - ?.referenceExpression - ?.resolve() as? KtClassOrObject ?: return false - val topLevelClass = reference.topLevelContainingClassOrObject() ?: return false - return topLevelClass.isAncestor(qualifierClass) - } - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (element !is KtUserType) return null - if (!check(element)) return null - return { - if (element.isValid && check(element)) { - element.deleteQualifier() - } - } - } - - override val writeActionNeeded: Boolean = true - } - - private class RemoveRedundantConstructorKeywordProcessing : CheckableProcessing(KtPrimaryConstructor::class) { - override fun check(element: KtPrimaryConstructor): Boolean = - element.containingClassOrObject is KtClass - && element.getConstructorKeyword() != null - && element.annotationEntries.isEmpty() - && element.visibilityModifier() == null - - - override fun action(element: KtPrimaryConstructor) { - element.removeRedundantConstructorKeywordAndSpace() - } - } - - private class RemoveRedundantModalityModifierProcessing : CheckableProcessing(KtDeclaration::class) { - override fun check(element: KtDeclaration): Boolean { - val modalityModifier = element.modalityModifier() ?: return false - val modalityModifierType = modalityModifier.node.elementType - val implicitModality = element.implicitModality() - - return modalityModifierType == implicitModality - } - - override fun action(element: KtDeclaration) { - element.removeModifierSmart(element.modalityModifierType()!!) - } - } - - //hack until KT-30804 is fixed - private fun KtModifierListOwner.removeModifierSmart(modifierToken: KtModifierKeywordToken) { - val modifier = modifierList?.getModifier(modifierToken) - val comment = modifier?.getPrevSiblingIgnoringWhitespace() as? PsiComment - comment?.also { - it.parent.addAfter(KtPsiFactory(this).createNewLine(), it) - } - val newElement = copy() as KtModifierListOwner - newElement.removeModifier(modifierToken) - replace(newElement) - containingFile.commitAndUnblockDocument() - } - - private class RemoveRedundantVisibilityModifierProcessing : CheckableProcessing(KtDeclaration::class) { - override fun check(element: KtDeclaration): Boolean { - val visibilityModifier = element.visibilityModifier() ?: return false - val implicitVisibility = element.implicitVisibility() - return when { - visibilityModifier.node.elementType == implicitVisibility -> - true - element.hasModifier(KtTokens.INTERNAL_KEYWORD) && element.containingClassOrObject?.isLocal == true -> - true - else -> false - } - } - - override fun action(element: KtDeclaration) { - element.removeModifierSmart(element.visibilityModifierType()!!) - } - } - -} \ No newline at end of file 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 new file mode 100644 index 00000000000..535c959892a --- /dev/null +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/J2kPostProcessor.kt @@ -0,0 +1,245 @@ +/* + * 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.openapi.application.ApplicationManager +import com.intellij.openapi.application.ModalityState +import com.intellij.openapi.editor.RangeMarker +import com.intellij.psi.PsiElement +import com.intellij.psi.search.LocalSearchScope +import com.intellij.psi.search.searches.ReferencesSearch +import kotlinx.coroutines.runBlocking +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.inspections.* +import org.jetbrains.kotlin.idea.inspections.branchedTransformations.IfThenToSafeAccessInspection +import org.jetbrains.kotlin.idea.inspections.conventionNameCalls.ReplaceGetOrSetInspection +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.intentions.IfThenToElvisIntention +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 +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.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.isSubtypeOf +import org.jetbrains.kotlin.types.typeUtil.makeNotNullable + +class NewJ2kPostProcessor : PostProcessor { + override fun insertImport(file: KtFile, fqName: FqName) { + ApplicationManager.getApplication().invokeAndWait { + runWriteAction { + val descriptors = file.resolveImportReference(fqName) + descriptors.firstOrNull()?.let { ImportInsertHelper.getInstance(file.project).importDescriptor(file, it) } + } + } + } + + override fun doAdditionalProcessing(file: KtFile, converterContext: ConverterContext?, rangeMarker: RangeMarker?) { + runBlocking(EDT.ModalityStateElement(ModalityState.defaultModalityState())) { + for (processing in processings) { + processing.runProcessing(file, rangeMarker, converterContext as NewJ2kConverterContext) + } + } + } +} + +private val processings: List = listOf( + nullabilityProcessing, + formatCodeProcessing, + shortenReferencesProcessing, + InspectionLikeProcessingGroup(VarToValProcessing()), + ConvertGettersAndSettersToPropertyProcessing(), + InspectionLikeProcessingGroup(MoveGetterAndSetterAnnotationsToPropertyProcessing()), + InspectionLikeProcessingGroup( + generalInspectionBasedProcessing(RedundantGetterInspection()), + generalInspectionBasedProcessing(RedundantSetterInspection()) + ), + ConvertToDataClassProcessing(), + 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)), + intentionBasedProcessing(FoldInitializerAndIfToElvisIntention()), + 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()), + intentionBasedProcessing(IfThenToElvisIntention()), + inspectionBasedProcessing(SimplifyNegatedBinaryExpressionInspection()), + inspectionBasedProcessing(ReplaceGetOrSetInspection()), + intentionBasedProcessing(AddOperatorModifierIntention()), + intentionBasedProcessing(ObjectLiteralToLambdaIntention()), + intentionBasedProcessing(AnonymousFunctionToLambdaIntention()), + intentionBasedProcessing(RemoveUnnecessaryParenthesesIntention()), + intentionBasedProcessing(DestructureIntention()), + inspectionBasedProcessing(SimplifyAssertNotNullInspection()), + intentionBasedProcessing( + RemoveRedundantCallsOfConversionMethodsIntention() + ), + generalInspectionBasedProcessing(LiftReturnOrAssignmentInspection()), + 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 + if (realType.makeNotNullable().isSubtypeOf(expectedType.makeNotNullable()) + && realType.isNullable() + && !expectedType.isNullable() + ) { + val factory = KtPsiFactory(element) + element.replace(factory.createExpressionByPattern("($0)!!", element.text)) + } + }, + + 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) + } + }, + RemoveForExpressionLoopParameterTypeProcessing() + ), + formatCodeProcessing +) \ No newline at end of file diff --git a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/MoveGetterAndSetterAnnotationsToProperty.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/MoveGetterAndSetterAnnotationsToProperty.kt deleted file mode 100644 index 3b8d862297c..00000000000 --- a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/MoveGetterAndSetterAnnotationsToProperty.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.psi.PsiElement -import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget -import org.jetbrains.kotlin.idea.intentions.addUseSiteTarget -import org.jetbrains.kotlin.nj2k.NewJ2kPostProcessing -import org.jetbrains.kotlin.psi.KtProperty -import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics - -class MoveGetterAndSetterAnnotationsToProperty : NewJ2kPostProcessing { - override val writeActionNeeded: Boolean = true - - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (element !is KtProperty) return null - if (element.accessors.isEmpty()) return null - return { - for (accessor in element.accessors.sortedBy { it.isGetter }) { - for (entry in accessor.annotationEntries) { - element.addAnnotationEntry(entry).also { - it.addUseSiteTarget( - if (accessor.isGetter) AnnotationUseSiteTarget.PROPERTY_GETTER - else AnnotationUseSiteTarget.PROPERTY_SETTER, - element.project - ) - } - } - accessor.annotationEntries.forEach { it.delete() } - } - } - } -} \ No newline at end of file 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 new file mode 100644 index 00000000000..47f641c0c4e --- /dev/null +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/inspectionLikePostProcessings.kt @@ -0,0 +1,327 @@ +/* + * 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.codeInspection.* +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.command.CommandProcessor +import com.intellij.openapi.editor.RangeMarker +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiRecursiveElementVisitor +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.range +import org.jetbrains.kotlin.idea.core.util.EDT +import org.jetbrains.kotlin.idea.formatter.commitAndUnblockDocument +import org.jetbrains.kotlin.idea.inspections.AbstractApplicabilityBasedInspection +import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection +import org.jetbrains.kotlin.idea.intentions.SelfTargetingIntention +import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention +import org.jetbrains.kotlin.idea.quickfix.KotlinIntentionActionsFactory +import org.jetbrains.kotlin.idea.quickfix.QuickFixActionBase +import org.jetbrains.kotlin.idea.util.application.runReadAction +import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.jetbrains.kotlin.j2k.ConverterSettings +import org.jetbrains.kotlin.nj2k.* +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.psiUtil.elementsInRange +import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics +import org.jetbrains.kotlin.utils.mapToIndex +import java.util.ArrayList +import kotlin.reflect.KClass +import kotlin.reflect.full.isSubclassOf + + +class InspectionLikeProcessingGroup(val inspectionLikeProcessings: List) : ProcessingGroup { + constructor(vararg inspectionLikeProcessings: InspectionLikeProcessing) : this(inspectionLikeProcessings.toList()) + + private val processingsToPriorityMap = inspectionLikeProcessings.mapToIndex() + fun priority(processing: InspectionLikeProcessing): Int = processingsToPriorityMap.getValue(processing) + + override suspend fun runProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) { + do { + var modificationStamp: Long? = file.modificationStamp + val elementToActions = runReadAction { + collectAvailableActions(file, converterContext, rangeMarker) + } + withContext(EDT) { + CommandProcessor.getInstance().runUndoTransparentAction { + for ((element, action, _, writeActionNeeded) in elementToActions) { + if (element.isValid) { + if (writeActionNeeded) { + runWriteAction { + runAction(action, element) + } + } else { + runAction(action, element) + } + } else { + modificationStamp = null + } + } + } + } + } while (modificationStamp != file.modificationStamp && elementToActions.isNotEmpty()) + } + + + private enum class RangeFilterResult { + SKIP, + GO_INSIDE, + PROCESS + } + + private inline fun runAction(action: () -> Unit, element: PsiElement) { + val file = element.containingFile //element can be deleted after action performed + action() + runWriteAction { file.commitAndUnblockDocument() } + } + + private data class ActionData(val element: PsiElement, val action: () -> Unit, val priority: Int, val writeActionNeeded: Boolean) + + private fun collectAvailableActions( + file: KtFile, + context: NewJ2kConverterContext, + rangeMarker: RangeMarker? + ): List { + val diagnostics = analyzeFileRange(file, rangeMarker) + + val availableActions = ArrayList() + + file.accept(object : PsiRecursiveElementVisitor() { + override fun visitElement(element: PsiElement) { + val rangeResult = rangeFilter(element, rangeMarker) + if (rangeResult == RangeFilterResult.SKIP) return + + super.visitElement(element) + + if (rangeResult == RangeFilterResult.PROCESS) { + inspectionLikeProcessings.forEach { processing -> + val action = processing.createAction(element, diagnostics, context.converter.settings) + if (action != null) { + availableActions.add( + ActionData( + element, action, + priority(processing), + processing.writeActionNeeded + ) + ) + } + } + } + } + }) + availableActions.sortBy { it.priority } + return availableActions + } + + 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 + } + + private fun rangeFilter(element: PsiElement, rangeMarker: RangeMarker?): RangeFilterResult { + if (rangeMarker == null) return RangeFilterResult.PROCESS + if (!rangeMarker.isValid) return RangeFilterResult.SKIP + val range = TextRange(rangeMarker.startOffset, rangeMarker.endOffset) + val elementRange = element.textRange + return when { + range.contains(elementRange) -> RangeFilterResult.PROCESS + range.intersects(elementRange) -> RangeFilterResult.GO_INSIDE + else -> RangeFilterResult.SKIP + } + } +} + +interface InspectionLikeProcessing { + fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? + + val writeActionNeeded: Boolean +} + +abstract class ApplicabilityBasedInspectionLikeProcessing(private val classTag: KClass) : InspectionLikeProcessing { + 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)? { + if (!element::class.isSubclassOf(classTag)) return null + @Suppress("UNCHECKED_CAST") + if (!isApplicableTo(element as E, settings)) return null + return { + if (element.isValid && isApplicableTo(element, settings)) { + apply(element) + } + } + } + + final override val writeActionNeeded: Boolean = true +} + + +inline fun > intentionBasedProcessing( + intention: TIntention, + noinline additionalChecker: (TElement) -> Boolean = { true } +) = object : InspectionLikeProcessing { + // Intention can either need or not need write apply + override val writeActionNeeded = intention.startInWriteAction() + + override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? { + if (!TElement::class.java.isInstance(element)) return null + val tElement = element as TElement + if (intention.applicabilityRange(tElement) == null) return null + if (!additionalChecker(tElement)) return null + return { + if (intention.applicabilityRange(tElement) != null) { // isApplicableTo availability of the intention again because something could change + intention.applyTo(element, null) + } + } + } +} + + +fun generalInspectionBasedProcessing( + inspection: TInspection, + acceptInformationLevel: Boolean = false +) = (object : InspectionLikeProcessing { + override val writeActionNeeded = false + + fun QuickFix.applyFixSmart(element: PsiElement, descriptor: D) { + if (!element.isValid) return + if (descriptor is ProblemDescriptor) { + if (this is IntentionWrapper) { + @Suppress("NOT_YET_SUPPORTED_IN_INLINE") + fun applySelfTargetingIntention(action: SelfTargetingIntention) { + val target = action.getTarget(descriptor.psiElement.startOffset, descriptor.psiElement.containingFile) ?: return + if (!action.isApplicableTo(target, descriptor.psiElement.startOffset)) return + action.applyTo(target, null) + } + + @Suppress("NOT_YET_SUPPORTED_IN_INLINE") + fun applyQuickFixActionBase(action: QuickFixActionBase) { + if (!action.isAvailable(element.project, null, descriptor.psiElement.containingFile)) return + action.invoke(element.project, null, descriptor.psiElement.containingFile) + } + + + @Suppress("NOT_YET_SUPPORTED_IN_INLINE") + fun applyIntention() { + @Suppress("UNCHECKED_CAST") + when (val action = this.action) { + is SelfTargetingIntention<*> -> applySelfTargetingIntention(action as SelfTargetingIntention) + is QuickFixActionBase<*> -> applyQuickFixActionBase(action) + } + } + + + if (this.startInWriteAction()) { + ApplicationManager.getApplication().runWriteAction(::applyIntention) + } else { + applyIntention() + } + + } + } + + ApplicationManager.getApplication().runWriteAction { + this.applyFix(element.project, descriptor) + } + } + + override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? { + val holder = ProblemsHolder(InspectionManager.getInstance(element.project), element.containingFile, false) + val visitor = inspection.buildVisitor( + holder, + false, + LocalInspectionToolSession(element.containingFile, 0, element.containingFile.endOffset) + ) + element.accept(visitor) + if (!holder.hasResults()) return null + return { + holder.results.clear() + element.accept(visitor) + if (holder.hasResults()) { + holder.results + .filter { acceptInformationLevel || it.highlightType != ProblemHighlightType.INFORMATION } + .forEach { it.fixes?.firstOrNull()?.applyFixSmart(element, it) } + } + } + } +}) + + +inline fun > + inspectionBasedProcessing(inspection: TInspection, acceptInformationLevel: Boolean = false) = + object : InspectionLikeProcessing { + // Inspection can either need or not need write apply + override val writeActionNeeded = inspection.startFixInWriteAction + + fun isApplicable(element: TElement): Boolean { + if (!inspection.isApplicable(element)) return false + return acceptInformationLevel || inspection.inspectionHighlightType(element) != ProblemHighlightType.INFORMATION + } + + override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? { + if (!TElement::class.java.isInstance(element)) return null + val tElement = element as TElement + if (!isApplicable(tElement)) return null + return { + if (isApplicable(tElement)) { // isApplicableTo availability of the inspection again because something could change + inspection.applyTo(inspection.inspectionTarget(tElement)) + } + } + } + } + +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 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/postProcessings.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/postProcessings.kt new file mode 100644 index 00000000000..ca12037f525 --- /dev/null +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/postProcessings.kt @@ -0,0 +1,57 @@ +/* + * 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.openapi.command.CommandProcessor +import com.intellij.openapi.editor.RangeMarker +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiElement +import kotlinx.coroutines.withContext +import org.jetbrains.kotlin.idea.core.util.EDT +import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.psiUtil.elementsInRange + + +interface GeneralPostProcessing { + suspend fun runProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) +} + +abstract class SimplePostProcessing : GeneralPostProcessing { + final override suspend fun runProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) { + withContext(EDT) { + CommandProcessor.getInstance().runUndoTransparentAction { + runWriteAction { + applySimpleProcessing(file, rangeMarker, converterContext) + } + } + } + } + + abstract fun applySimpleProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) +} + +abstract class ElementsBasedPostProcessing : SimplePostProcessing() { + final override fun applySimpleProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) { + val elements = + rangeMarker?.let { marker -> + file.elementsInRange(TextRange(marker.startOffset, marker.endOffset)) + } ?: listOf(file) + runProcessing(elements, converterContext) + } + + abstract fun runProcessing(elements: List, converterContext: NewJ2kConverterContext) +} + +interface ProcessingGroup : GeneralPostProcessing + +fun postProcessing(action: (file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) -> Unit): GeneralPostProcessing = + object : SimplePostProcessing() { + override fun applySimpleProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) { + action(file, rangeMarker, converterContext) + } + } diff --git a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/ConvertDataClass.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/ConvertToDataClassProcessing.kt similarity index 51% rename from nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/ConvertDataClass.kt rename to nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/ConvertToDataClassProcessing.kt index af865ea08f5..c640e5c611f 100644 --- a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/ConvertDataClass.kt +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/ConvertToDataClassProcessing.kt @@ -3,26 +3,29 @@ * 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 +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.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.idea.core.setVisibility import org.jetbrains.kotlin.idea.intentions.addUseSiteTarget import org.jetbrains.kotlin.idea.references.KtSimpleNameReference import org.jetbrains.kotlin.idea.util.CommentSaver -import org.jetbrains.kotlin.nj2k.NewJ2kPostProcessing +import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext +import org.jetbrains.kotlin.nj2k.escaped +import org.jetbrains.kotlin.nj2k.postProcessing.ElementsBasedPostProcessing +import org.jetbrains.kotlin.nj2k.postProcessing.descendantsOfType +import org.jetbrains.kotlin.nj2k.postProcessing.type +import org.jetbrains.kotlin.nj2k.postProcessing.unpackedReferenceToProperty import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.addRemoveModifier.setModifierList import org.jetbrains.kotlin.psi.psiUtil.asAssignment import org.jetbrains.kotlin.psi.psiUtil.containingClass -import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics +import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierTypeOrDefault import org.jetbrains.kotlin.types.typeUtil.makeNotNullable -class ConvertDataClass : NewJ2kPostProcessing { - override val writeActionNeeded: Boolean = true - +class ConvertToDataClassProcessing : ElementsBasedPostProcessing() { private fun KtCallableDeclaration.rename(newName: String) { val factory = KtPsiFactory(this) val escapedName = newName.escaped() @@ -49,6 +52,7 @@ class ConvertDataClass : NewJ2kPostProcessing { ?.unpackedReferenceToProperty() ?.takeIf { it.containingClass() == klass } ?: return@mapNotNull null if (property.getter != null || property.setter != null) return@mapNotNull null + if (property.initializer != null) return@mapNotNull null val constructorParameter = ((statement.right as? KtReferenceExpression) ?.references @@ -62,41 +66,51 @@ class ConvertDataClass : NewJ2kPostProcessing { if (constructorParameterType.makeNotNullable() != propertyType.makeNotNullable()) return@mapNotNull null - DataClassInfo(constructorParameter, property, statement) + DataClassInfo( + constructorParameter, + property, + statement + ) }.toList() - override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? { - if (element !is KtClass) return null - return { - val factory = KtPsiFactory(element) - for ((constructorParameter, property, statement) in collectPropertiesData(element)) { - constructorParameter.addBefore(property.valOrVarKeyword, constructorParameter.nameIdentifier!!) - constructorParameter.addAfter(factory.createWhiteSpace(), constructorParameter.valOrVarKeyword!!) - constructorParameter.rename(property.name!!) - val propertyCommentSaver = CommentSaver(property, saveLineBreaks = true) - if (property.modifierList != null) { - constructorParameter.setModifierList(property.modifierList!!) - constructorParameter.annotationEntries.forEach { it.delete() } - for (entry in constructorParameter.annotationEntries) { - entry.addUseSiteTarget(AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER, element.project) - } - } + override fun runProcessing(elements: List, converterContext: NewJ2kConverterContext) { + for (klass in elements.descendantsOfType()) { + convertClass(klass) + } + } - for (annotationEntry in property.annotationEntries) { - constructorParameter.addAnnotationEntry(annotationEntry).also { entry -> - if (entry.useSiteTarget == null) { - entry.addUseSiteTarget(AnnotationUseSiteTarget.FIELD, element.project) - } + private fun convertClass(klass: KtClass) { + val factory = KtPsiFactory(klass) + for ((constructorParameter, property, statement) in collectPropertiesData(klass)) { + constructorParameter.addBefore(property.valOrVarKeyword, constructorParameter.nameIdentifier!!) + constructorParameter.addAfter(factory.createWhiteSpace(), constructorParameter.valOrVarKeyword!!) + constructorParameter.rename(property.name!!) + val propertyCommentSaver = CommentSaver(property, saveLineBreaks = true) + + constructorParameter.setVisibility(property.visibilityModifierTypeOrDefault()) + for (annotationEntry in constructorParameter.annotationEntries) { + if (annotationEntry.useSiteTarget == null) { + annotationEntry.addUseSiteTarget(AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER, klass.project) + } + } + + for (annotationEntry in property.annotationEntries) { + constructorParameter.addAnnotationEntry(annotationEntry).also { entry -> + if (entry.useSiteTarget == null) { + entry.addUseSiteTarget(AnnotationUseSiteTarget.FIELD, klass.project) } } - property.delete() - statement.delete() - propertyCommentSaver.restore(constructorParameter, forceAdjustIndent = false) } - for (initBlock in element.getAnonymousInitializers()) { - if ((initBlock.body as KtBlockExpression).statements.isEmpty()) { - initBlock.delete() - } + property.delete() + statement.delete() + propertyCommentSaver.restore(constructorParameter, forceAdjustIndent = false) + } + + for (initBlock in klass.getAnonymousInitializers()) { + if ((initBlock.body as KtBlockExpression).statements.isEmpty()) { + val commentSaver = CommentSaver(initBlock) + initBlock.delete() + klass.primaryConstructor?.let { commentSaver.restore(it) } } } } 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 new file mode 100644 index 00000000000..f474062f631 --- /dev/null +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/inspeҁtionLikeProcessings.kt @@ -0,0 +1,420 @@ +/* + * 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.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 +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.refactoring.fqName.getKotlinFqName +import org.jetbrains.kotlin.idea.references.KtSimpleNameReference +import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.idea.references.readWriteAccess +import org.jetbrains.kotlin.idea.util.getResolutionScope +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.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 +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + + +class RemoveExplicitPropertyTypeProcessing : ApplicabilityBasedInspectionLikeProcessing(KtProperty::class) { + override fun isApplicableTo(element: KtProperty, settings: ConverterSettings?): Boolean { + val needFieldTypes = settings?.specifyFieldTypeByDefault == true + val needLocalVariablesTypes = settings?.specifyLocalVariableTypeByDefault == true + + if (needLocalVariablesTypes && element.isLocal) return false + if (needFieldTypes && element.isMember) return false + val initializer = element.initializer ?: return false + val withoutExpectedType = + initializer.analyzeInContext(initializer.getResolutionScope()).getType(initializer) ?: return false + val descriptor = element.resolveToDescriptorIfAny() as? CallableDescriptor ?: return false + return if (element.isVar) withoutExpectedType == descriptor.returnType + else withoutExpectedType.makeNotNullable() == descriptor.returnType?.makeNotNullable() + } + + override fun apply(element: KtProperty) { + element.typeReference = null + } +} + +class RemoveRedundantNullabilityProcessing: ApplicabilityBasedInspectionLikeProcessing(KtProperty::class) { + override fun isApplicableTo(element: KtProperty, settings: ConverterSettings?): Boolean { + if (!element.isLocal) return false + val typeReference = element.typeReference + if (typeReference == null || typeReference.typeElement !is KtNullableType) return false + val initializerType = element.initializer?.let { + it.analyzeInContext(element.getResolutionScope()).getType(it) + } + if (initializerType?.isNullable() == true) return false + + return ReferencesSearch.search(element, element.useScope).findAll().mapNotNull { ref -> + val parent = (ref.element.parent as? KtExpression)?.asAssignment() + parent?.takeIf { it.left == ref.element } + }.all { + val right = it.right + val withoutExpectedType = right?.analyzeInContext(element.getResolutionScope()) + withoutExpectedType?.getType(right)?.isNullable() == false + } + } + + override fun apply(element: KtProperty) { + val typeElement = element.typeReference?.typeElement + typeElement?.replace(typeElement.cast().innerType!!) + } +} + +class RemoveExplicitTypeArgumentsProcessing : ApplicabilityBasedInspectionLikeProcessing(KtTypeArgumentList::class) { + override fun isApplicableTo(element: KtTypeArgumentList, settings: ConverterSettings?): Boolean = + RemoveExplicitTypeArgumentsIntention.isApplicableTo(element, approximateFlexible = true) + + override fun apply(element: KtTypeArgumentList) { + element.delete() + } +} + +class RemoveRedundantOverrideVisibilityProcessing : InspectionLikeProcessing { + override val writeActionNeeded = true + + override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? { + if (element !is KtCallableDeclaration || !element.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return null + val modifier = element.visibilityModifierType() ?: return null + return { element.setVisibility(modifier) } + } +} + +class ConvertToStringTemplateProcessing : InspectionLikeProcessing { + override val writeActionNeeded = true + + val intention = ConvertToStringTemplateIntention() + + override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? { + if (element is KtBinaryExpression && intention.isApplicableTo(element) && ConvertToStringTemplateIntention.shouldSuggestToConvert( + element + ) + ) { + return { intention.applyTo(element, null) } + } else { + return null + } + } +} + +class UsePropertyAccessSyntaxProcessing : InspectionLikeProcessing { + override val writeActionNeeded = true + + val intention = UsePropertyAccessSyntaxIntention() + + override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? { + if (element !is KtCallExpression) return null + val propertyName = intention.detectPropertyNameToUse(element) ?: return null + return { intention.applyTo(element, propertyName, reformat = true) } + } +} + +class RemoveRedundantSamAdaptersProcessing : InspectionLikeProcessing { + override val writeActionNeeded = true + + override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? { + if (element !is KtCallExpression) return null + + val expressions = RedundantSamConstructorInspection.samConstructorCallsToBeConverted(element) + if (expressions.isEmpty()) return null + + return { + RedundantSamConstructorInspection.samConstructorCallsToBeConverted(element) + .forEach { RedundantSamConstructorInspection.replaceSamConstructorCall(it) } + } + } +} + +class UseExpressionBodyProcessing : InspectionLikeProcessing { + override val writeActionNeeded = true + + override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? { + if (element !is KtPropertyAccessor) return null + + val inspection = UseExpressionBodyInspection(convertEmptyToUnit = false) + if (!inspection.isActiveFor(element)) return null + + return { + if (inspection.isActiveFor(element)) { + inspection.simplify(element, false) + } + } + } +} + +class RemoveRedundantCastToNullableProcessing : InspectionLikeProcessing { + override val writeActionNeeded = true + + override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? { + if (element !is KtBinaryExpressionWithTypeRHS) return null + + val context = element.analyze() + val leftType = context.getType(element.left) ?: return null + val rightType = context.get(BindingContext.TYPE, element.right) ?: return null + + if (!leftType.isMarkedNullable && rightType.isMarkedNullable) { + return { + val type = element.right?.typeElement as? KtNullableType + type?.replace(type.innerType!!) + } + } + + return null + } +} + +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 + + val resolved = element.mainReference.resolve() ?: return null + if (resolved.isAncestor(element, strict = true)) { + if (resolved is KtVariableDeclaration && resolved.hasInitializer()) { + val anonymousObject = element.getParentOfType(true) ?: return null + if (resolved.initializer!!.getChildOfType() == anonymousObject) { + return { element.replaced(KtPsiFactory(element).createThisExpression()) } + } + } + } + + return null + } +} + +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 + + val anonymousObject = element.getParentOfType(true) ?: return null + + val variable = anonymousObject.getParentOfType(true) ?: return null + + if (variable.nameAsName == element.getReferencedNameAsName() && + variable.initializer?.getChildOfType() == anonymousObject + ) { + return { element.replaced(KtPsiFactory(element).createThisExpression()) } + } + + return null + } +} + +class VarToValProcessing : ApplicabilityBasedInspectionLikeProcessing(KtProperty::class) { + private fun KtProperty.hasWriteUsages(): Boolean = + ReferencesSearch.search(this, useScope).any { usage -> + (usage as? KtSimpleNameReference)?.element?.let { nameReference -> + val receiver = nameReference.parent?.safeAs()?.receiverExpression + if (nameReference.parentOfType() != null + && (receiver == null || receiver is KtThisExpression) + ) return@let false + nameReference.readWriteAccess(useResolveForReadWrite = true).isWrite + } == true + } + + override fun isApplicableTo(element: KtProperty, settings: ConverterSettings?): Boolean { + if (!element.isVar) return false + if (!element.isPrivate()) return false + val descriptor = element.resolveToDescriptorIfAny() ?: return false + if (descriptor.overriddenDescriptors.any { it.safeAs()?.isVar == true }) return false + return !element.hasWriteUsages() + } + + override fun apply(element: KtProperty) { + val factory = KtPsiFactory(element) + element.valOrVarKeyword.replace(factory.createValKeyword()) + } +} + +class JavaObjectEqualsToEqOperatorProcessing : ApplicabilityBasedInspectionLikeProcessing(KtCallExpression::class) { + companion object { + val CALL_FQ_NAME = FqName("java.util.Objects.equals") + } + + override fun isApplicableTo(element: KtCallExpression, settings: ConverterSettings?): Boolean { + if (element.valueArguments.size != 2) return false + if (element.valueArguments.any { it.getArgumentExpression() == null }) return false + val target = element.calleeExpression + .safeAs() + ?.resolve() + ?: return false + return target.getKotlinFqName() == CALL_FQ_NAME + } + + override fun apply(element: KtCallExpression) { + val factory = KtPsiFactory(element) + element.getQualifiedExpressionForSelectorOrThis().replace( + factory.createExpressionByPattern( + "($0 == $1)", + element.valueArguments[0].getArgumentExpression()!!, + element.valueArguments[1].getArgumentExpression()!! + ) + ) + } +} + + +class RemoveForExpressionLoopParameterTypeProcessing : + ApplicabilityBasedInspectionLikeProcessing(KtForExpression::class) { + override fun isApplicableTo(element: KtForExpression, settings: ConverterSettings?): Boolean = + element.loopParameter?.typeReference?.typeElement != null + && settings?.specifyLocalVariableTypeByDefault != true + + override fun apply(element: KtForExpression) { + element.loopParameter?.typeReference = null + } +} + +class RemoveRedundantConstructorKeywordProcessing : + ApplicabilityBasedInspectionLikeProcessing(KtPrimaryConstructor::class) { + override fun isApplicableTo(element: KtPrimaryConstructor, settings: ConverterSettings?): Boolean = + element.containingClassOrObject is KtClass + && element.getConstructorKeyword() != null + && element.annotationEntries.isEmpty() + && element.visibilityModifier() == null + + + override fun apply(element: KtPrimaryConstructor) { + element.removeRedundantConstructorKeywordAndSpace() + } +} + +class RemoveRedundantModalityModifierProcessing : ApplicabilityBasedInspectionLikeProcessing(KtDeclaration::class) { + override fun isApplicableTo(element: KtDeclaration, settings: ConverterSettings?): Boolean { + val modalityModifier = element.modalityModifier() ?: return false + val modalityModifierType = modalityModifier.node.elementType + val implicitModality = element.implicitModality() + + return modalityModifierType == implicitModality + } + + override fun apply(element: KtDeclaration) { + element.removeModifierSmart(element.modalityModifierType()!!) + } +} + + +class RemoveRedundantVisibilityModifierProcessing : ApplicabilityBasedInspectionLikeProcessing(KtDeclaration::class) { + override fun isApplicableTo(element: KtDeclaration, settings: ConverterSettings?): Boolean { + val visibilityModifier = element.visibilityModifier() ?: return false + val implicitVisibility = element.implicitVisibility() + return when { + visibilityModifier.node.elementType == implicitVisibility -> + true + element.hasModifier(KtTokens.INTERNAL_KEYWORD) && element.containingClassOrObject?.isLocal == true -> + true + else -> false + } + } + + override fun apply(element: KtDeclaration) { + element.removeModifierSmart(element.visibilityModifierType()!!) + } +} + +class RemoveExplicitPropertyTypeWithInspectionProcessing : + InspectionLikeProcessing { + override val writeActionNeeded: Boolean = true + private val processing = + generalInspectionBasedProcessing(RedundantExplicitTypeInspection()) + + override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? { + if (settings?.specifyLocalVariableTypeByDefault == true) return null + + return processing.createAction(element, diagnostics, settings) + } +} + +class RemoveExplicitOpenInInterfaceProcessing : ApplicabilityBasedInspectionLikeProcessing(KtClass::class) { + override fun isApplicableTo(element: KtClass, settings: ConverterSettings?): Boolean = + element.isValid + && element.isInterface() + && element.hasModifier(KtTokens.OPEN_KEYWORD) + + override fun apply(element: KtClass) { + element.removeModifier(KtTokens.OPEN_KEYWORD) + } +} + +class MoveGetterAndSetterAnnotationsToPropertyProcessing : ApplicabilityBasedInspectionLikeProcessing(KtProperty::class) { + override fun isApplicableTo(element: KtProperty, settings: ConverterSettings?): Boolean = + element.accessors.isNotEmpty() + + override fun apply(element: KtProperty) { + for (accessor in element.accessors.sortedBy { it.isGetter }) { + for (entry in accessor.annotationEntries) { + element.addAnnotationEntry(entry).also { + it.addUseSiteTarget( + if (accessor.isGetter) AnnotationUseSiteTarget.PROPERTY_GETTER + else AnnotationUseSiteTarget.PROPERTY_SETTER, + element.project + ) + } + } + accessor.annotationEntries.forEach { it.delete() } + } + } +} \ No newline at end of file diff --git a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/processings.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/processings.kt new file mode 100644 index 00000000000..85a64e135da --- /dev/null +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/processings.kt @@ -0,0 +1,48 @@ +/* + * 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.codeStyle.CodeStyleManager +import org.jetbrains.kotlin.idea.core.ShortenReferences +import org.jetbrains.kotlin.idea.formatter.commitAndUnblockDocument +import org.jetbrains.kotlin.nj2k.nullabilityAnalysis.AnalysisScope +import org.jetbrains.kotlin.nj2k.nullabilityAnalysis.NullabilityAnalysisFacade +import org.jetbrains.kotlin.nj2k.nullabilityAnalysis.nullabilityByUndefinedNullabilityComment +import org.jetbrains.kotlin.nj2k.nullabilityAnalysis.prepareTypeElementByMakingAllTypesNullableConsideringNullabilityComment +import org.jetbrains.kotlin.nj2k.postProcessing.postProcessing + +val formatCodeProcessing = + postProcessing { file, rangeMarker, _ -> + file.commitAndUnblockDocument() + val codeStyleManager = CodeStyleManager.getInstance(file.project) + if (rangeMarker != null) { + if (rangeMarker.isValid) { + codeStyleManager.reformatRange(file, rangeMarker.startOffset, rangeMarker.endOffset) + } + } else { + codeStyleManager.reformat(file) + } + } + +val nullabilityProcessing = + postProcessing { file, rangeMarker, converterContext -> + NullabilityAnalysisFacade( + converterContext, + getTypeElementNullability = ::nullabilityByUndefinedNullabilityComment, + prepareTypeElement = ::prepareTypeElementByMakingAllTypesNullableConsideringNullabilityComment, + debugPrint = false + ).fixNullability(AnalysisScope(file, rangeMarker)) + } + +val shortenReferencesProcessing = + postProcessing { file, rangeMarker, _ -> + if (rangeMarker != null) { + ShortenReferences.DEFAULT.process(file, rangeMarker.startOffset, rangeMarker.endOffset) + } else { + ShortenReferences.DEFAULT.process(file) + } + } + diff --git a/nj2k/tests/org/jetbrains/kotlin/nj2k/AbstractNewJavaToKotlinConverterSingleFileTest.kt b/nj2k/tests/org/jetbrains/kotlin/nj2k/AbstractNewJavaToKotlinConverterSingleFileTest.kt index b176943991c..2f88829a036 100644 --- a/nj2k/tests/org/jetbrains/kotlin/nj2k/AbstractNewJavaToKotlinConverterSingleFileTest.kt +++ b/nj2k/tests/org/jetbrains/kotlin/nj2k/AbstractNewJavaToKotlinConverterSingleFileTest.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor import org.jetbrains.kotlin.j2k.AbstractJavaToKotlinConverterSingleFileTest import org.jetbrains.kotlin.j2k.ConverterSettings +import org.jetbrains.kotlin.nj2k.postProcessing.NewJ2kPostProcessor import org.jetbrains.kotlin.test.KotlinTestUtils import java.io.File @@ -50,7 +51,7 @@ abstract class AbstractNewJavaToKotlinConverterSingleFileTest : AbstractJavaToKo override fun fileToKotlin(text: String, settings: ConverterSettings, project: Project): String { val file = createJavaFile(text) return NewJavaToKotlinConverter(project, settings, IdeaJavaToKotlinServices) - .filesToKotlin(listOf(file), NewJ2kPostProcessor(true)).results.single() + .filesToKotlin(listOf(file), NewJ2kPostProcessor()).results.single() } override fun provideExpectedFile(javaPath: String): File =