New J2K: separate inspection like processings from others & remove boilerplate code in processings

This commit is contained in:
Ilya Kirillov
2019-05-30 16:36:59 +03:00
parent d6e07ca693
commit 63434fc46c
11 changed files with 1151 additions and 1228 deletions
@@ -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<NewJ2kPostProcessing>.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<NewJ2kPostProcessing>,
file: KtFile,
context: NewJ2kConverterContext,
rangeMarker: RangeMarker?
): List<ActionData> {
val diagnostics = analyzeFileRange(file, rangeMarker)
val availableActions = ArrayList<ActionData>()
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<KtElement>()
return if (elements.isNotEmpty())
file.getResolutionFacade().analyzeWithAllCompilerChecks(elements).bindingContext.diagnostics
else
Diagnostics.EMPTY
}
private fun rangeFilter(element: PsiElement, rangeMarker: RangeMarker?): RangeFilterResult {
if (rangeMarker == null) return RangeFilterResult.PROCESS
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
}
}
}
@@ -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()
}
@@ -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<NewJ2kPostProcessing>) : Processing {
constructor(vararg processings: NewJ2kPostProcessing) : this(processings.toList())
}
data class OneTimeProcessingGroup(val processings: List<Processing>) : Processing {
constructor(vararg processings: Processing) : this(processings.toList())
}
private abstract class CheckableProcessing<E : PsiElement>(private val classTag: KClass<E>) : 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<NewJ2kPostProcessing> =
when (this) {
is SingleOneTimeProcessing -> sequenceOf(processing)
is RepeatableProcessingGroup -> processings.asSequence()
is OneTimeProcessingGroup -> processings.asSequence().flatMap { it.processings() }
else -> sequenceOf()
}
private val processingsToPriorityMap = HashMap<NewJ2kPostProcessing, Int>()
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<KtExpression, KotlinType, KotlinType>
?: 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<KtBinaryExpressionWithTypeRHS>(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<KtSimpleNameExpression>(Errors.UNNECESSARY_NOT_NULL_ASSERTION) { element, _ ->
val exclExclExpr = element.parent as KtUnaryExpression
val baseExpression = exclExclExpr.baseExpression!!
val context = baseExpression.analyze(BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS)
if (context.diagnostics.forElement(element).any { it.factory == Errors.UNNECESSARY_NOT_NULL_ASSERTION }) {
exclExclExpr.replace(baseExpression)
}
},
RemoveForExpressionLoopParameterTypeProcessing()
)
)
val processings: Collection<NewJ2kPostProcessing> =
mainProcessings.processings().toList()
init {
processingsToPriorityMap.putAll(processings.mapToIndex())
}
private inline fun <reified TElement : PsiElement, TIntention : SelfTargetingRangeIntention<TElement>> 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 <TInspection : AbstractKotlinInspection> registerGeneralInspectionBasedProcessing(
inspection: TInspection,
acceptInformationLevel: Boolean = false
) = (object : NewJ2kPostProcessing {
override val writeActionNeeded = false
fun <D : CommonProblemDescriptor> QuickFix<D>.applyFixSmart(project: Project, descriptor: D) {
if (descriptor is ProblemDescriptor) {
if (this is IntentionWrapper) {
@Suppress("NOT_YET_SUPPORTED_IN_INLINE")
fun applySelfTargetingIntention(action: SelfTargetingIntention<PsiElement>) {
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<PsiElement>) {
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<PsiElement>)
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
<reified TElement : PsiElement,
TInspection : AbstractApplicabilityBasedInspection<TElement>> 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 <reified TElement : PsiElement> 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 <reified TElement : PsiElement> 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<KtClassOrObject>(true) ?: return null
if (resolved.initializer!!.getChildOfType<KtClassOrObject>() == 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<KtClassOrObject>(true) ?: return null
val variable = anonymousObject.getParentOfType<KtVariableDeclaration>(true) ?: return null
if (variable.nameAsName == element.getReferencedNameAsName() &&
variable.initializer?.getChildOfType<KtClassOrObject>() == 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<KtAnonymousInitializer>() == 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<VariableDescriptor>()?.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<KtReferenceExpression>()
?.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>(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<KtClassOrObject>()?.parentClassForCompanionOrThis() ?: return false
return qualifier.parentClassForCompanionOrThis() in qualifiedExpression.parentsOfType<KtClassOrObject>()
}
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>(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>(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>(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()!!)
}
}
}
@@ -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<GeneralPostProcessing> = 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<KtExpression, KotlinType, KotlinType>
?: 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<KtBinaryExpressionWithTypeRHS>(Errors.USELESS_CAST) { element, _ ->
if (element.left.isNullExpression()) return@diagnosticBasedProcessing
val expression = RemoveUselessCastFix.invoke(element)
val variable = expression.parent as? KtProperty
if (variable != null && expression == variable.initializer && variable.isLocal) {
val ref = ReferencesSearch.search(variable, LocalSearchScope(variable.containingFile)).findAll().singleOrNull()
if (ref != null && ref.element is KtSimpleNameExpression) {
ref.element.replace(expression)
variable.delete()
}
}
},
diagnosticBasedProcessingWithFixFactory(
RemoveModifierFix.createRemoveProjectionFactory(true),
Errors.REDUNDANT_PROJECTION
),
diagnosticBasedProcessingWithFixFactory(
AddModifierFix.createFactory(KtTokens.OVERRIDE_KEYWORD),
Errors.VIRTUAL_MEMBER_HIDDEN
),
diagnosticBasedProcessingWithFixFactory(
RemoveModifierFix.createRemoveModifierFromListOwnerFactory(KtTokens.OPEN_KEYWORD),
Errors.NON_FINAL_MEMBER_IN_FINAL_CLASS, Errors.NON_FINAL_MEMBER_IN_OBJECT
),
diagnosticBasedProcessingWithFixFactory(
MakeVisibleFactory,
Errors.INVISIBLE_MEMBER
),
diagnosticBasedProcessingFactory(
Errors.VAL_REASSIGNMENT, Errors.CAPTURED_VAL_INITIALIZATION, Errors.CAPTURED_MEMBER_VAL_INITIALIZATION
) { element: KtSimpleNameExpression, _: Diagnostic ->
val property = element.mainReference.resolve() as? KtProperty
if (property == null) {
null
} else {
val action = {
if (!property.isVar) {
property.valOrVarKeyword.replace(KtPsiFactory(element.project).createVarKeyword())
}
}
action
}
},
diagnosticBasedProcessing<KtSimpleNameExpression>(Errors.UNNECESSARY_NOT_NULL_ASSERTION) { element, _ ->
val exclExclExpr = element.parent as KtUnaryExpression
val baseExpression = exclExclExpr.baseExpression!!
val context = baseExpression.analyze(BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS)
if (context.diagnostics.forElement(element).any { it.factory == Errors.UNNECESSARY_NOT_NULL_ASSERTION }) {
exclExclExpr.replace(baseExpression)
}
},
RemoveForExpressionLoopParameterTypeProcessing()
),
formatCodeProcessing
)
@@ -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() }
}
}
}
}
@@ -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<InspectionLikeProcessing>) : 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<ActionData> {
val diagnostics = analyzeFileRange(file, rangeMarker)
val availableActions = ArrayList<ActionData>()
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<KtElement>()
return if (elements.isNotEmpty())
file.getResolutionFacade().analyzeWithAllCompilerChecks(elements).bindingContext.diagnostics
else
Diagnostics.EMPTY
}
private fun rangeFilter(element: PsiElement, rangeMarker: RangeMarker?): RangeFilterResult {
if (rangeMarker == null) return RangeFilterResult.PROCESS
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<E : PsiElement>(private val classTag: KClass<E>) : 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 <reified TElement : PsiElement, TIntention : SelfTargetingRangeIntention<TElement>> 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 <TInspection : AbstractKotlinInspection> generalInspectionBasedProcessing(
inspection: TInspection,
acceptInformationLevel: Boolean = false
) = (object : InspectionLikeProcessing {
override val writeActionNeeded = false
fun <D : CommonProblemDescriptor> QuickFix<D>.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<PsiElement>) {
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<PsiElement>) {
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<PsiElement>)
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 <reified TElement : PsiElement, TInspection : AbstractApplicabilityBasedInspection<TElement>>
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 <reified TElement : PsiElement> diagnosticBasedProcessing(
vararg diagnosticFactory: DiagnosticFactory<*>,
crossinline fix: (TElement, Diagnostic) -> Unit
) = object : InspectionLikeProcessing {
override val writeActionNeeded = true
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
if (!TElement::class.java.isInstance(element)) return null
val diagnostic = diagnostics.forElement(element).firstOrNull { it.factory in diagnosticFactory } ?: return null
return {
fix(element as TElement, diagnostic)
}
}
}
inline fun <reified TElement : PsiElement> diagnosticBasedProcessingFactory(
vararg diagnosticFactory: DiagnosticFactory<*>,
crossinline fixFactory: (TElement, Diagnostic) -> (() -> Unit)?
) = object : InspectionLikeProcessing {
override val writeActionNeeded = true
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
if (!TElement::class.java.isInstance(element)) return null
val diagnostic = diagnostics.forElement(element).firstOrNull { it.factory in diagnosticFactory } ?: return null
return fixFactory(element as TElement, diagnostic)
}
}
@@ -0,0 +1,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<PsiElement>, 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)
}
}
@@ -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<PsiElement>, converterContext: NewJ2kConverterContext) {
for (klass in elements.descendantsOfType<KtClass>()) {
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) }
}
}
}
@@ -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>(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>(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<KtNullableType>().innerType!!)
}
}
class RemoveExplicitTypeArgumentsProcessing : ApplicabilityBasedInspectionLikeProcessing<KtTypeArgumentList>(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<KtClassOrObject>(true) ?: return null
if (resolved.initializer!!.getChildOfType<KtClassOrObject>() == 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<KtClassOrObject>(true) ?: return null
val variable = anonymousObject.getParentOfType<KtVariableDeclaration>(true) ?: return null
if (variable.nameAsName == element.getReferencedNameAsName() &&
variable.initializer?.getChildOfType<KtClassOrObject>() == anonymousObject
) {
return { element.replaced(KtPsiFactory(element).createThisExpression()) }
}
return null
}
}
class VarToValProcessing : ApplicabilityBasedInspectionLikeProcessing<KtProperty>(KtProperty::class) {
private fun KtProperty.hasWriteUsages(): Boolean =
ReferencesSearch.search(this, useScope).any { usage ->
(usage as? KtSimpleNameReference)?.element?.let { nameReference ->
val receiver = nameReference.parent?.safeAs<KtDotQualifiedExpression>()?.receiverExpression
if (nameReference.parentOfType<KtAnonymousInitializer>() != 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<VariableDescriptor>()?.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>(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<KtReferenceExpression>()
?.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>(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>(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>(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>(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>(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>(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() }
}
}
}
@@ -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)
}
}
@@ -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 =