New J2K: separate inspection like processings from others & remove boilerplate code in processings
This commit is contained in:
@@ -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 com.intellij.openapi.project.Project
|
||||||
import org.jetbrains.kotlin.j2k.*
|
import org.jetbrains.kotlin.j2k.*
|
||||||
|
import org.jetbrains.kotlin.nj2k.postProcessing.NewJ2kPostProcessor
|
||||||
|
|
||||||
class NewJ2kConverterExtension : J2kConverterExtension() {
|
class NewJ2kConverterExtension : J2kConverterExtension() {
|
||||||
override val isNewJ2k = true
|
override val isNewJ2k = true
|
||||||
@@ -14,5 +15,5 @@ class NewJ2kConverterExtension : J2kConverterExtension() {
|
|||||||
NewJavaToKotlinConverter(project, settings, services)
|
NewJavaToKotlinConverter(project, settings, services)
|
||||||
|
|
||||||
override fun createPostProcessor(formatCode: Boolean): PostProcessor =
|
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
|
||||||
|
)
|
||||||
-36
@@ -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() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+327
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+50
-36
@@ -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.
|
* 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.PsiElement
|
||||||
import com.intellij.psi.search.LocalSearchScope
|
import com.intellij.psi.search.LocalSearchScope
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
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.intentions.addUseSiteTarget
|
||||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||||
import org.jetbrains.kotlin.idea.util.CommentSaver
|
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.*
|
||||||
import org.jetbrains.kotlin.psi.addRemoveModifier.setModifierList
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.asAssignment
|
import org.jetbrains.kotlin.psi.psiUtil.asAssignment
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.containingClass
|
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
|
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||||
|
|
||||||
class ConvertDataClass : NewJ2kPostProcessing {
|
class ConvertToDataClassProcessing : ElementsBasedPostProcessing() {
|
||||||
override val writeActionNeeded: Boolean = true
|
|
||||||
|
|
||||||
private fun KtCallableDeclaration.rename(newName: String) {
|
private fun KtCallableDeclaration.rename(newName: String) {
|
||||||
val factory = KtPsiFactory(this)
|
val factory = KtPsiFactory(this)
|
||||||
val escapedName = newName.escaped()
|
val escapedName = newName.escaped()
|
||||||
@@ -49,6 +52,7 @@ class ConvertDataClass : NewJ2kPostProcessing {
|
|||||||
?.unpackedReferenceToProperty()
|
?.unpackedReferenceToProperty()
|
||||||
?.takeIf { it.containingClass() == klass } ?: return@mapNotNull null
|
?.takeIf { it.containingClass() == klass } ?: return@mapNotNull null
|
||||||
if (property.getter != null || property.setter != null) return@mapNotNull null
|
if (property.getter != null || property.setter != null) return@mapNotNull null
|
||||||
|
if (property.initializer != null) return@mapNotNull null
|
||||||
val constructorParameter =
|
val constructorParameter =
|
||||||
((statement.right as? KtReferenceExpression)
|
((statement.right as? KtReferenceExpression)
|
||||||
?.references
|
?.references
|
||||||
@@ -62,41 +66,51 @@ class ConvertDataClass : NewJ2kPostProcessing {
|
|||||||
|
|
||||||
if (constructorParameterType.makeNotNullable() != propertyType.makeNotNullable()) return@mapNotNull null
|
if (constructorParameterType.makeNotNullable() != propertyType.makeNotNullable()) return@mapNotNull null
|
||||||
|
|
||||||
DataClassInfo(constructorParameter, property, statement)
|
DataClassInfo(
|
||||||
|
constructorParameter,
|
||||||
|
property,
|
||||||
|
statement
|
||||||
|
)
|
||||||
}.toList()
|
}.toList()
|
||||||
|
|
||||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
override fun runProcessing(elements: List<PsiElement>, converterContext: NewJ2kConverterContext) {
|
||||||
if (element !is KtClass) return null
|
for (klass in elements.descendantsOfType<KtClass>()) {
|
||||||
return {
|
convertClass(klass)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (annotationEntry in property.annotationEntries) {
|
private fun convertClass(klass: KtClass) {
|
||||||
constructorParameter.addAnnotationEntry(annotationEntry).also { entry ->
|
val factory = KtPsiFactory(klass)
|
||||||
if (entry.useSiteTarget == null) {
|
for ((constructorParameter, property, statement) in collectPropertiesData(klass)) {
|
||||||
entry.addUseSiteTarget(AnnotationUseSiteTarget.FIELD, element.project)
|
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()) {
|
property.delete()
|
||||||
if ((initBlock.body as KtBlockExpression).statements.isEmpty()) {
|
statement.delete()
|
||||||
initBlock.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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+420
@@ -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() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+48
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+2
-1
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
|
|||||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||||
import org.jetbrains.kotlin.j2k.AbstractJavaToKotlinConverterSingleFileTest
|
import org.jetbrains.kotlin.j2k.AbstractJavaToKotlinConverterSingleFileTest
|
||||||
import org.jetbrains.kotlin.j2k.ConverterSettings
|
import org.jetbrains.kotlin.j2k.ConverterSettings
|
||||||
|
import org.jetbrains.kotlin.nj2k.postProcessing.NewJ2kPostProcessor
|
||||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ abstract class AbstractNewJavaToKotlinConverterSingleFileTest : AbstractJavaToKo
|
|||||||
override fun fileToKotlin(text: String, settings: ConverterSettings, project: Project): String {
|
override fun fileToKotlin(text: String, settings: ConverterSettings, project: Project): String {
|
||||||
val file = createJavaFile(text)
|
val file = createJavaFile(text)
|
||||||
return NewJavaToKotlinConverter(project, settings, IdeaJavaToKotlinServices)
|
return NewJavaToKotlinConverter(project, settings, IdeaJavaToKotlinServices)
|
||||||
.filesToKotlin(listOf(file), NewJ2kPostProcessor(true)).results.single()
|
.filesToKotlin(listOf(file), NewJ2kPostProcessor()).results.single()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun provideExpectedFile(javaPath: String): File =
|
override fun provideExpectedFile(javaPath: String): File =
|
||||||
|
|||||||
Reference in New Issue
Block a user