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 org.jetbrains.kotlin.j2k.*
|
||||
import org.jetbrains.kotlin.nj2k.postProcessing.NewJ2kPostProcessor
|
||||
|
||||
class NewJ2kConverterExtension : J2kConverterExtension() {
|
||||
override val isNewJ2k = true
|
||||
@@ -14,5 +15,5 @@ class NewJ2kConverterExtension : J2kConverterExtension() {
|
||||
NewJavaToKotlinConverter(project, settings, services)
|
||||
|
||||
override fun createPostProcessor(formatCode: Boolean): PostProcessor =
|
||||
NewJ2kPostProcessor(formatCode)
|
||||
NewJ2kPostProcessor()
|
||||
}
|
||||
@@ -1,924 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.nj2k
|
||||
|
||||
import com.intellij.codeInspection.*
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.LocalSearchScope
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters2
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.core.implicitModality
|
||||
import org.jetbrains.kotlin.idea.core.implicitVisibility
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.idea.core.setVisibility
|
||||
import org.jetbrains.kotlin.idea.formatter.commitAndUnblockDocument
|
||||
import org.jetbrains.kotlin.idea.inspections.*
|
||||
import org.jetbrains.kotlin.idea.inspections.branchedTransformations.IfThenToSafeAccessInspection
|
||||
import org.jetbrains.kotlin.idea.inspections.conventionNameCalls.ReplaceGetOrSetInspection
|
||||
import org.jetbrains.kotlin.idea.intentions.*
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnAsymmetricallyIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IfThenToElvisIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isTrivialStatementBody
|
||||
import org.jetbrains.kotlin.idea.quickfix.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.references.readWriteAccess
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
import org.jetbrains.kotlin.j2k.ConverterSettings
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.nj2k.postProcessing.*
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.isNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.kotlin.utils.mapToIndex
|
||||
import java.util.*
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
|
||||
interface NewJ2kPostProcessing {
|
||||
fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? =
|
||||
createAction(element, diagnostics)
|
||||
|
||||
fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? =
|
||||
createAction(element, diagnostics, null)
|
||||
|
||||
val writeActionNeeded: Boolean
|
||||
}
|
||||
|
||||
interface Processing
|
||||
data class SingleOneTimeProcessing(val processing: NewJ2kPostProcessing) : Processing
|
||||
data class RepeatableProcessingGroup(val processings: List<NewJ2kPostProcessing>) : Processing {
|
||||
constructor(vararg processings: NewJ2kPostProcessing) : this(processings.toList())
|
||||
}
|
||||
|
||||
data class OneTimeProcessingGroup(val processings: List<Processing>) : Processing {
|
||||
constructor(vararg processings: Processing) : this(processings.toList())
|
||||
}
|
||||
|
||||
private abstract class CheckableProcessing<E : PsiElement>(private val classTag: KClass<E>) : NewJ2kPostProcessing {
|
||||
protected open fun check(element: E): Boolean =
|
||||
check(element, null)
|
||||
|
||||
protected open fun check(element: E, settings: ConverterSettings?): Boolean =
|
||||
check(element)
|
||||
|
||||
protected abstract fun action(element: E)
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
|
||||
if (!element::class.isSubclassOf(classTag)) return null
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
if (!check(element as E, settings)) return null
|
||||
return {
|
||||
if (element.isValid && check(element, settings)) {
|
||||
action(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val writeActionNeeded: Boolean = true
|
||||
}
|
||||
|
||||
object NewJ2KPostProcessingRegistrar {
|
||||
|
||||
private fun Processing.processings(): Sequence<NewJ2kPostProcessing> =
|
||||
when (this) {
|
||||
is SingleOneTimeProcessing -> sequenceOf(processing)
|
||||
is RepeatableProcessingGroup -> processings.asSequence()
|
||||
is OneTimeProcessingGroup -> processings.asSequence().flatMap { it.processings() }
|
||||
else -> sequenceOf()
|
||||
}
|
||||
|
||||
|
||||
private val processingsToPriorityMap = HashMap<NewJ2kPostProcessing, Int>()
|
||||
|
||||
fun priority(processing: NewJ2kPostProcessing): Int = processingsToPriorityMap[processing]!!
|
||||
|
||||
val mainProcessings = OneTimeProcessingGroup(
|
||||
SingleOneTimeProcessing(VarToVal()),
|
||||
SingleOneTimeProcessing(ConvertGettersAndSetters()),
|
||||
SingleOneTimeProcessing(MoveGetterAndSetterAnnotationsToProperty()),
|
||||
SingleOneTimeProcessing(registerGeneralInspectionBasedProcessing(RedundantGetterInspection())),
|
||||
SingleOneTimeProcessing(registerGeneralInspectionBasedProcessing(RedundantSetterInspection())),
|
||||
SingleOneTimeProcessing(ConvertDataClass()),
|
||||
RepeatableProcessingGroup(
|
||||
RemoveRedundantVisibilityModifierProcessing(),
|
||||
RemoveRedundantModalityModifierProcessing(),
|
||||
RemoveRedundantConstructorKeywordProcessing(),
|
||||
registerDiagnosticBasedProcessing(Errors.REDUNDANT_OPEN_IN_INTERFACE) { element: KtDeclaration, _ ->
|
||||
element.removeModifier(KtTokens.OPEN_KEYWORD)
|
||||
},
|
||||
object : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded: Boolean = true
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (element !is KtClass) return null
|
||||
|
||||
fun check(klass: KtClass): Boolean {
|
||||
return klass.isValid
|
||||
&& klass.isInterface()
|
||||
&& klass.hasModifier(KtTokens.OPEN_KEYWORD)
|
||||
}
|
||||
|
||||
if (!check(element)) return null
|
||||
return {
|
||||
if (check(element)) {
|
||||
element.removeModifier(KtTokens.OPEN_KEYWORD)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
registerGeneralInspectionBasedProcessing(ExplicitThisInspection()),
|
||||
RemoveExplicitTypeArgumentsProcessing(),
|
||||
RemoveRedundantOverrideVisibilityProcessing(),
|
||||
registerInspectionBasedProcessing(MoveLambdaOutsideParenthesesInspection()),
|
||||
registerGeneralInspectionBasedProcessing(RedundantCompanionReferenceInspection()),
|
||||
FixObjectStringConcatenationProcessing(),
|
||||
ConvertToStringTemplateProcessing(),
|
||||
UsePropertyAccessSyntaxProcessing(),
|
||||
UninitializedVariableReferenceFromInitializerToThisReferenceProcessing(),
|
||||
UnresolvedVariableReferenceFromInitializerToThisReferenceProcessing(),
|
||||
RemoveRedundantSamAdaptersProcessing(),
|
||||
RemoveRedundantCastToNullableProcessing(),
|
||||
registerInspectionBasedProcessing(ReplacePutWithAssignmentInspection()),
|
||||
UseExpressionBodyProcessing(),
|
||||
registerInspectionBasedProcessing(UnnecessaryVariableInspection()),
|
||||
|
||||
object : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded: Boolean = true
|
||||
private val processing = registerGeneralInspectionBasedProcessing(RedundantExplicitTypeInspection())
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
|
||||
if (settings?.specifyLocalVariableTypeByDefault == true) return null
|
||||
|
||||
return processing.createAction(element, diagnostics)
|
||||
}
|
||||
}
|
||||
,
|
||||
registerGeneralInspectionBasedProcessing(RedundantUnitReturnTypeInspection()),
|
||||
JavaObjectEqualsToEqOperator(),
|
||||
RemoveExplicitPropertyType(),
|
||||
RemoveRedundantNullability(),
|
||||
|
||||
registerGeneralInspectionBasedProcessing(CanBeValInspection(ignoreNotUsedVals = false)),
|
||||
|
||||
registerIntentionBasedProcessing(FoldInitializerAndIfToElvisIntention()),
|
||||
registerGeneralInspectionBasedProcessing(RedundantSemicolonInspection()),
|
||||
registerIntentionBasedProcessing(RemoveEmptyClassBodyIntention()),
|
||||
registerIntentionBasedProcessing(RemoveRedundantCallsOfConversionMethodsIntention()),
|
||||
registerInspectionBasedProcessing(JavaMapForEachInspection()),
|
||||
|
||||
registerIntentionBasedProcessing(FoldIfToReturnIntention()) { it.then.isTrivialStatementBody() && it.`else`.isTrivialStatementBody() },
|
||||
registerIntentionBasedProcessing(FoldIfToReturnAsymmetricallyIntention()) {
|
||||
it.then.isTrivialStatementBody() && (KtPsiUtil.skipTrailingWhitespacesAndComments(
|
||||
it
|
||||
) as KtReturnExpression).returnedExpression.isTrivialStatementBody()
|
||||
},
|
||||
|
||||
|
||||
registerInspectionBasedProcessing(IfThenToSafeAccessInspection()),
|
||||
registerInspectionBasedProcessing(IfThenToSafeAccessInspection()),
|
||||
registerIntentionBasedProcessing(IfThenToElvisIntention()),
|
||||
registerInspectionBasedProcessing(SimplifyNegatedBinaryExpressionInspection()),
|
||||
registerInspectionBasedProcessing(ReplaceGetOrSetInspection()),
|
||||
registerIntentionBasedProcessing(AddOperatorModifierIntention()),
|
||||
registerIntentionBasedProcessing(ObjectLiteralToLambdaIntention()),
|
||||
registerIntentionBasedProcessing(AnonymousFunctionToLambdaIntention()),
|
||||
registerIntentionBasedProcessing(RemoveUnnecessaryParenthesesIntention()),
|
||||
registerIntentionBasedProcessing(DestructureIntention()),
|
||||
registerInspectionBasedProcessing(SimplifyAssertNotNullInspection()),
|
||||
registerIntentionBasedProcessing(RemoveRedundantCallsOfConversionMethodsIntention()),
|
||||
registerGeneralInspectionBasedProcessing(LiftReturnOrAssignmentInspection()),
|
||||
registerGeneralInspectionBasedProcessing(MayBeConstantInspection()),
|
||||
registerIntentionBasedProcessing(RemoveEmptyPrimaryConstructorIntention()),
|
||||
registerDiagnosticBasedProcessing(Errors.PLATFORM_CLASS_MAPPED_TO_KOTLIN) { element: KtDotQualifiedExpression, _ ->
|
||||
val parent = element.parent as? KtImportDirective ?: return@registerDiagnosticBasedProcessing
|
||||
parent.delete()
|
||||
},
|
||||
|
||||
registerDiagnosticBasedProcessing(
|
||||
Errors.UNSAFE_CALL,
|
||||
Errors.UNSAFE_INFIX_CALL,
|
||||
Errors.UNSAFE_OPERATOR_CALL
|
||||
) { element: PsiElement, diagnostic ->
|
||||
val action =
|
||||
AddExclExclCallFix.createActions(diagnostic).singleOrNull()
|
||||
?: return@registerDiagnosticBasedProcessing
|
||||
action.invoke(element.project, null, element.containingFile)
|
||||
},
|
||||
|
||||
registerDiagnosticBasedProcessingWithFixFactory(MissingIteratorExclExclFixFactory, Errors.ITERATOR_ON_NULLABLE),
|
||||
registerDiagnosticBasedProcessingWithFixFactory(SmartCastImpossibleExclExclFixFactory, Errors.SMARTCAST_IMPOSSIBLE),
|
||||
registerDiagnosticBasedProcessing(Errors.TYPE_MISMATCH) { element: PsiElement, diagnostic ->
|
||||
@Suppress("UNCHECKED_CAST") val diagnosticWithParameters =
|
||||
diagnostic as? DiagnosticWithParameters2<KtExpression, KotlinType, KotlinType>
|
||||
?: return@registerDiagnosticBasedProcessing
|
||||
val expectedType = diagnosticWithParameters.a
|
||||
val realType = diagnosticWithParameters.b
|
||||
if (realType.makeNotNullable().isSubtypeOf(expectedType.makeNotNullable())
|
||||
&& realType.isNullable()
|
||||
&& !expectedType.isNullable()
|
||||
) {
|
||||
val factory = KtPsiFactory(element)
|
||||
element.replace(factory.createExpressionByPattern("($0)!!", element.text))
|
||||
}
|
||||
},
|
||||
|
||||
registerDiagnosticBasedProcessingWithFixFactory(
|
||||
ReplacePrimitiveCastWithNumberConversionFix.Factory,
|
||||
Errors.CAST_NEVER_SUCCEEDS
|
||||
),
|
||||
registerDiagnosticBasedProcessingWithFixFactory(
|
||||
ChangeCallableReturnTypeFix.ReturnTypeMismatchOnOverrideFactory,
|
||||
Errors.RETURN_TYPE_MISMATCH_ON_OVERRIDE
|
||||
),
|
||||
|
||||
RemoveRedundantTypeQualifierProcessing(),
|
||||
RemoveRedundantExpressionQualifierProcessing(),
|
||||
|
||||
registerDiagnosticBasedProcessing<KtBinaryExpressionWithTypeRHS>(Errors.USELESS_CAST) { element, _ ->
|
||||
if (element.left.isNullExpression()) return@registerDiagnosticBasedProcessing
|
||||
val expression = RemoveUselessCastFix.invoke(element)
|
||||
|
||||
val variable = expression.parent as? KtProperty
|
||||
if (variable != null && expression == variable.initializer && variable.isLocal) {
|
||||
val ref = ReferencesSearch.search(variable, LocalSearchScope(variable.containingFile)).findAll().singleOrNull()
|
||||
if (ref != null && ref.element is KtSimpleNameExpression) {
|
||||
ref.element.replace(expression)
|
||||
variable.delete()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
registerDiagnosticBasedProcessingWithFixFactory(
|
||||
RemoveModifierFix.createRemoveProjectionFactory(true),
|
||||
Errors.REDUNDANT_PROJECTION
|
||||
),
|
||||
registerDiagnosticBasedProcessingWithFixFactory(
|
||||
AddModifierFix.createFactory(KtTokens.OVERRIDE_KEYWORD),
|
||||
Errors.VIRTUAL_MEMBER_HIDDEN
|
||||
),
|
||||
registerDiagnosticBasedProcessingWithFixFactory(
|
||||
RemoveModifierFix.createRemoveModifierFromListOwnerFactory(KtTokens.OPEN_KEYWORD),
|
||||
Errors.NON_FINAL_MEMBER_IN_FINAL_CLASS, Errors.NON_FINAL_MEMBER_IN_OBJECT
|
||||
),
|
||||
registerDiagnosticBasedProcessingWithFixFactory(
|
||||
MakeVisibleFactory,
|
||||
Errors.INVISIBLE_MEMBER
|
||||
),
|
||||
|
||||
registerDiagnosticBasedProcessingFactory(
|
||||
Errors.VAL_REASSIGNMENT, Errors.CAPTURED_VAL_INITIALIZATION, Errors.CAPTURED_MEMBER_VAL_INITIALIZATION
|
||||
) { element: KtSimpleNameExpression, _: Diagnostic ->
|
||||
val property = element.mainReference.resolve() as? KtProperty
|
||||
if (property == null) {
|
||||
null
|
||||
} else {
|
||||
val action = {
|
||||
if (!property.isVar) {
|
||||
property.valOrVarKeyword.replace(KtPsiFactory(element.project).createVarKeyword())
|
||||
}
|
||||
}
|
||||
action
|
||||
}
|
||||
},
|
||||
|
||||
registerDiagnosticBasedProcessing<KtSimpleNameExpression>(Errors.UNNECESSARY_NOT_NULL_ASSERTION) { element, _ ->
|
||||
val exclExclExpr = element.parent as KtUnaryExpression
|
||||
val baseExpression = exclExclExpr.baseExpression!!
|
||||
val context = baseExpression.analyze(BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS)
|
||||
if (context.diagnostics.forElement(element).any { it.factory == Errors.UNNECESSARY_NOT_NULL_ASSERTION }) {
|
||||
exclExclExpr.replace(baseExpression)
|
||||
}
|
||||
},
|
||||
RemoveForExpressionLoopParameterTypeProcessing()
|
||||
)
|
||||
)
|
||||
|
||||
val processings: Collection<NewJ2kPostProcessing> =
|
||||
mainProcessings.processings().toList()
|
||||
|
||||
|
||||
init {
|
||||
processingsToPriorityMap.putAll(processings.mapToIndex())
|
||||
}
|
||||
|
||||
|
||||
private inline fun <reified TElement : PsiElement, TIntention : SelfTargetingRangeIntention<TElement>> registerIntentionBasedProcessing(
|
||||
intention: TIntention,
|
||||
noinline additionalChecker: (TElement) -> Boolean = { true }
|
||||
) = object : NewJ2kPostProcessing {
|
||||
// Intention can either need or not need write action
|
||||
override val writeActionNeeded = intention.startInWriteAction()
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (!TElement::class.java.isInstance(element)) return null
|
||||
val tElement = element as TElement
|
||||
if (intention.applicabilityRange(tElement) == null) return null
|
||||
if (!additionalChecker(tElement)) return null
|
||||
return {
|
||||
if (intention.applicabilityRange(tElement) != null) { // check availability of the intention again because something could change
|
||||
intention.applyTo(element, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun <TInspection : AbstractKotlinInspection> registerGeneralInspectionBasedProcessing(
|
||||
inspection: TInspection,
|
||||
acceptInformationLevel: Boolean = false
|
||||
) = (object : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded = false
|
||||
|
||||
fun <D : CommonProblemDescriptor> QuickFix<D>.applyFixSmart(project: Project, descriptor: D) {
|
||||
if (descriptor is ProblemDescriptor) {
|
||||
if (this is IntentionWrapper) {
|
||||
@Suppress("NOT_YET_SUPPORTED_IN_INLINE")
|
||||
fun applySelfTargetingIntention(action: SelfTargetingIntention<PsiElement>) {
|
||||
val target = action.getTarget(descriptor.psiElement.startOffset, descriptor.psiElement.containingFile) ?: return
|
||||
if (!action.isApplicableTo(target, descriptor.psiElement.startOffset)) return
|
||||
action.applyTo(target, null)
|
||||
}
|
||||
|
||||
@Suppress("NOT_YET_SUPPORTED_IN_INLINE")
|
||||
fun applyQuickFixActionBase(action: QuickFixActionBase<PsiElement>) {
|
||||
if (!action.isAvailable(project, null, descriptor.psiElement.containingFile)) return
|
||||
action.invoke(project, null, descriptor.psiElement.containingFile)
|
||||
}
|
||||
|
||||
|
||||
@Suppress("NOT_YET_SUPPORTED_IN_INLINE")
|
||||
fun applyIntention() {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
when (val action = this.action) {
|
||||
is SelfTargetingIntention<*> -> applySelfTargetingIntention(action as SelfTargetingIntention<PsiElement>)
|
||||
is QuickFixActionBase<*> -> applyQuickFixActionBase(action)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (this.startInWriteAction()) {
|
||||
ApplicationManager.getApplication().runWriteAction(::applyIntention)
|
||||
} else {
|
||||
applyIntention()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ApplicationManager.getApplication().runWriteAction {
|
||||
this.applyFix(project, descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
val holder = ProblemsHolder(InspectionManager.getInstance(element.project), element.containingFile, false)
|
||||
val visitor = inspection.buildVisitor(
|
||||
holder,
|
||||
false,
|
||||
LocalInspectionToolSession(element.containingFile, 0, element.containingFile.endOffset)
|
||||
)
|
||||
element.accept(visitor)
|
||||
if (!holder.hasResults()) return null
|
||||
return {
|
||||
holder.results.clear()
|
||||
element.accept(visitor)
|
||||
if (holder.hasResults()) {
|
||||
holder.results
|
||||
.filter { acceptInformationLevel || it.highlightType != ProblemHighlightType.INFORMATION }
|
||||
.forEach { it.fixes?.firstOrNull()?.applyFixSmart(element.project, it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
private inline fun
|
||||
<reified TElement : PsiElement,
|
||||
TInspection : AbstractApplicabilityBasedInspection<TElement>> registerInspectionBasedProcessing(
|
||||
|
||||
inspection: TInspection,
|
||||
acceptInformationLevel: Boolean = false
|
||||
) = object : NewJ2kPostProcessing {
|
||||
// Inspection can either need or not need write action
|
||||
override val writeActionNeeded = inspection.startFixInWriteAction
|
||||
|
||||
private fun isApplicable(element: TElement): Boolean {
|
||||
if (!inspection.isApplicable(element)) return false
|
||||
return acceptInformationLevel || inspection.inspectionHighlightType(element) != ProblemHighlightType.INFORMATION
|
||||
}
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (!TElement::class.java.isInstance(element)) return null
|
||||
val tElement = element as TElement
|
||||
if (!isApplicable(tElement)) return null
|
||||
return {
|
||||
if (isApplicable(tElement)) { // check availability of the inspection again because something could change
|
||||
inspection.applyTo(inspection.inspectionTarget(tElement))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerDiagnosticBasedProcessingWithFixFactory(
|
||||
fixFactory: KotlinIntentionActionsFactory,
|
||||
vararg diagnosticFactory: DiagnosticFactory<*>
|
||||
): NewJ2kPostProcessing = registerDiagnosticBasedProcessing(*diagnosticFactory) { element: PsiElement, diagnostic: Diagnostic ->
|
||||
fixFactory.createActions(diagnostic).singleOrNull()
|
||||
?.invoke(element.project, null, element.containingFile)
|
||||
}
|
||||
|
||||
|
||||
private inline fun <reified TElement : PsiElement> registerDiagnosticBasedProcessing(
|
||||
vararg diagnosticFactory: DiagnosticFactory<*>,
|
||||
crossinline fix: (TElement, Diagnostic) -> Unit
|
||||
) = object : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded = true
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (!TElement::class.java.isInstance(element)) return null
|
||||
val diagnostic = diagnostics.forElement(element).firstOrNull { it.factory in diagnosticFactory } ?: return null
|
||||
return {
|
||||
fix(element as TElement, diagnostic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <reified TElement : PsiElement> registerDiagnosticBasedProcessingFactory(
|
||||
vararg diagnosticFactory: DiagnosticFactory<*>,
|
||||
crossinline fixFactory: (TElement, Diagnostic) -> (() -> Unit)?
|
||||
) = object : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded = true
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (!TElement::class.java.isInstance(element)) return null
|
||||
val diagnostic = diagnostics.forElement(element).firstOrNull { it.factory in diagnosticFactory } ?: return null
|
||||
return fixFactory(element as TElement, diagnostic)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class RemoveExplicitPropertyType : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded = true
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics, settings: ConverterSettings?): (() -> Unit)? {
|
||||
if (element !is KtProperty) return null
|
||||
val needFieldTypes = settings?.specifyFieldTypeByDefault == true
|
||||
val needLocalVariablesTypes = settings?.specifyLocalVariableTypeByDefault == true
|
||||
|
||||
fun check(element: KtProperty): Boolean {
|
||||
if (needLocalVariablesTypes && element.isLocal) return false
|
||||
if (needFieldTypes && element.isMember) return false
|
||||
val initializer = element.initializer ?: return false
|
||||
val withoutExpectedType =
|
||||
initializer.analyzeInContext(initializer.getResolutionScope()).getType(initializer) ?: return false
|
||||
val descriptor = element.resolveToDescriptorIfAny() as? CallableDescriptor ?: return false
|
||||
return if (element.isVar) withoutExpectedType == descriptor.returnType
|
||||
else withoutExpectedType.makeNotNullable() == descriptor.returnType?.makeNotNullable()
|
||||
}
|
||||
|
||||
if (!check(element)) {
|
||||
return null
|
||||
} else {
|
||||
return {
|
||||
if (element.isValid && check(element)) {
|
||||
element.typeReference = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class RemoveRedundantNullability : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded: Boolean = true
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (element !is KtProperty) return null
|
||||
|
||||
fun check(element: KtProperty): Boolean {
|
||||
if (!element.isLocal) return false
|
||||
val typeReference = element.typeReference
|
||||
if (typeReference == null || typeReference.typeElement !is KtNullableType) return false
|
||||
val initializerType = element.initializer?.let {
|
||||
it.analyzeInContext(element.getResolutionScope()).getType(it)
|
||||
}
|
||||
if (initializerType?.isNullable() == true) return false
|
||||
|
||||
return ReferencesSearch.search(element, element.useScope).findAll().mapNotNull { ref ->
|
||||
val parent = (ref.element.parent as? KtExpression)?.asAssignment()
|
||||
parent?.takeIf { it.left == ref.element }
|
||||
}.all {
|
||||
val right = it.right
|
||||
val withoutExpectedType = right?.analyzeInContext(element.getResolutionScope())
|
||||
withoutExpectedType?.getType(right)?.isNullable() == false
|
||||
}
|
||||
}
|
||||
|
||||
if (!check(element)) {
|
||||
return null
|
||||
} else {
|
||||
return {
|
||||
val typeElement = element.typeReference?.typeElement
|
||||
if (element.isValid && check(element) && typeElement != null && typeElement is KtNullableType) {
|
||||
typeElement.replace(typeElement.innerType!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class RemoveExplicitTypeArgumentsProcessing : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded = true
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (element !is KtTypeArgumentList || !RemoveExplicitTypeArgumentsIntention.isApplicableTo(
|
||||
element,
|
||||
approximateFlexible = true
|
||||
)
|
||||
) return null
|
||||
|
||||
return {
|
||||
if (RemoveExplicitTypeArgumentsIntention.isApplicableTo(element, approximateFlexible = true)) {
|
||||
element.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class RemoveRedundantOverrideVisibilityProcessing : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded = true
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (element !is KtCallableDeclaration || !element.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return null
|
||||
val modifier = element.visibilityModifierType() ?: return null
|
||||
return { element.setVisibility(modifier) }
|
||||
}
|
||||
}
|
||||
|
||||
private class ConvertToStringTemplateProcessing : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded = true
|
||||
|
||||
private val intention = ConvertToStringTemplateIntention()
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (element is KtBinaryExpression && intention.isApplicableTo(element) && ConvertToStringTemplateIntention.shouldSuggestToConvert(
|
||||
element
|
||||
)
|
||||
) {
|
||||
return { intention.applyTo(element, null) }
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class UsePropertyAccessSyntaxProcessing : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded = true
|
||||
|
||||
private val intention = UsePropertyAccessSyntaxIntention()
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (element !is KtCallExpression) return null
|
||||
val propertyName = intention.detectPropertyNameToUse(element) ?: return null
|
||||
return { intention.applyTo(element, propertyName, reformat = true) }
|
||||
}
|
||||
}
|
||||
|
||||
private class RemoveRedundantSamAdaptersProcessing : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded = true
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (element !is KtCallExpression) return null
|
||||
|
||||
val expressions = RedundantSamConstructorInspection.samConstructorCallsToBeConverted(element)
|
||||
if (expressions.isEmpty()) return null
|
||||
|
||||
return {
|
||||
RedundantSamConstructorInspection.samConstructorCallsToBeConverted(element)
|
||||
.forEach { RedundantSamConstructorInspection.replaceSamConstructorCall(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class UseExpressionBodyProcessing : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded = true
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (element !is KtPropertyAccessor) return null
|
||||
|
||||
val inspection = UseExpressionBodyInspection(convertEmptyToUnit = false)
|
||||
if (!inspection.isActiveFor(element)) return null
|
||||
|
||||
return {
|
||||
if (inspection.isActiveFor(element)) {
|
||||
inspection.simplify(element, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class RemoveRedundantCastToNullableProcessing : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded = true
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (element !is KtBinaryExpressionWithTypeRHS) return null
|
||||
|
||||
val context = element.analyze()
|
||||
val leftType = context.getType(element.left) ?: return null
|
||||
val rightType = context.get(BindingContext.TYPE, element.right) ?: return null
|
||||
|
||||
if (!leftType.isMarkedNullable && rightType.isMarkedNullable) {
|
||||
return {
|
||||
val type = element.right?.typeElement as? KtNullableType
|
||||
type?.replace(type.innerType!!)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private class FixObjectStringConcatenationProcessing : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded = true
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (element !is KtBinaryExpression ||
|
||||
element.operationToken != KtTokens.PLUS ||
|
||||
diagnostics.forElement(element.operationReference).none {
|
||||
it.factory == Errors.UNRESOLVED_REFERENCE_WRONG_RECEIVER
|
||||
|| it.factory == Errors.NONE_APPLICABLE
|
||||
}
|
||||
)
|
||||
return null
|
||||
|
||||
val bindingContext = element.analyze()
|
||||
val rightType = element.right?.getType(bindingContext) ?: return null
|
||||
|
||||
if (KotlinBuiltIns.isString(rightType)) {
|
||||
return {
|
||||
val factory = KtPsiFactory(element)
|
||||
element.left!!.replace(factory.buildExpression {
|
||||
appendFixedText("(")
|
||||
appendExpression(element.left)
|
||||
appendFixedText(").toString()")
|
||||
})
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private class UninitializedVariableReferenceFromInitializerToThisReferenceProcessing : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded = true
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (element !is KtSimpleNameExpression || diagnostics.forElement(element).none { it.factory == Errors.UNINITIALIZED_VARIABLE }) return null
|
||||
|
||||
val resolved = element.mainReference.resolve() ?: return null
|
||||
if (resolved.isAncestor(element, strict = true)) {
|
||||
if (resolved is KtVariableDeclaration && resolved.hasInitializer()) {
|
||||
val anonymousObject = element.getParentOfType<KtClassOrObject>(true) ?: return null
|
||||
if (resolved.initializer!!.getChildOfType<KtClassOrObject>() == anonymousObject) {
|
||||
return { element.replaced(KtPsiFactory(element).createThisExpression()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private class UnresolvedVariableReferenceFromInitializerToThisReferenceProcessing : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded = true
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (element !is KtSimpleNameExpression || diagnostics.forElement(element).none { it.factory == Errors.UNRESOLVED_REFERENCE }) return null
|
||||
|
||||
val anonymousObject = element.getParentOfType<KtClassOrObject>(true) ?: return null
|
||||
|
||||
val variable = anonymousObject.getParentOfType<KtVariableDeclaration>(true) ?: return null
|
||||
|
||||
if (variable.nameAsName == element.getReferencedNameAsName() &&
|
||||
variable.initializer?.getChildOfType<KtClassOrObject>() == anonymousObject
|
||||
) {
|
||||
return { element.replaced(KtPsiFactory(element).createThisExpression()) }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private class VarToVal : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded = true
|
||||
|
||||
private fun KtProperty.hasWriteUsages(): Boolean =
|
||||
ReferencesSearch.search(this, useScope).any { usage ->
|
||||
(usage as? KtSimpleNameReference)?.element?.let {
|
||||
it.readWriteAccess(useResolveForReadWrite = true).isWrite
|
||||
&& it.parentOfType<KtAnonymousInitializer>() == null//TODO properly check
|
||||
} == true
|
||||
}
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (element !is KtProperty) return null
|
||||
|
||||
fun check(element: KtProperty): Boolean {
|
||||
if (!element.isVar) return false
|
||||
if (!element.isPrivate()) return false
|
||||
val descriptor = element.resolveToDescriptorIfAny() ?: return false
|
||||
if (descriptor.overriddenDescriptors.any { it.safeAs<VariableDescriptor>()?.isVar == true }) return false
|
||||
return !element.hasWriteUsages()
|
||||
}
|
||||
|
||||
if (!check(element)) {
|
||||
return null
|
||||
} else {
|
||||
return {
|
||||
if (element.isValid && check(element)) {
|
||||
val factory = KtPsiFactory(element)
|
||||
element.valOrVarKeyword.replace(factory.createValKeyword())
|
||||
println()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class JavaObjectEqualsToEqOperator : NewJ2kPostProcessing {
|
||||
companion object {
|
||||
private val CALL_FQ_NAME = FqName("java.util.Objects.equals")
|
||||
}
|
||||
|
||||
private fun check(callExpression: KtCallExpression): Boolean {
|
||||
if (callExpression.valueArguments.size != 2) return false
|
||||
if (callExpression.valueArguments.any { it.getArgumentExpression() == null }) return false
|
||||
val target = callExpression.calleeExpression
|
||||
.safeAs<KtReferenceExpression>()
|
||||
?.resolve()
|
||||
?: return false
|
||||
return target.getKotlinFqName() == CALL_FQ_NAME
|
||||
}
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (element !is KtCallExpression) return null
|
||||
if (!check(element)) return null
|
||||
return {
|
||||
if (element.isValid() && check(element)) {
|
||||
val factory = KtPsiFactory(element)
|
||||
element.getQualifiedExpressionForSelectorOrThis().replace(
|
||||
factory.createExpressionByPattern(
|
||||
"($0 == $1)",
|
||||
element.valueArguments[0].getArgumentExpression()!!,
|
||||
element.valueArguments[1].getArgumentExpression()!!
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val writeActionNeeded: Boolean = true
|
||||
}
|
||||
|
||||
private class RemoveForExpressionLoopParameterTypeProcessing : CheckableProcessing<KtForExpression>(KtForExpression::class) {
|
||||
override fun check(element: KtForExpression, settings: ConverterSettings?): Boolean =
|
||||
element.loopParameter?.typeReference?.typeElement != null
|
||||
&& settings?.specifyLocalVariableTypeByDefault != true
|
||||
|
||||
override fun action(element: KtForExpression) {
|
||||
element.loopParameter?.typeReference = null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class RemoveRedundantExpressionQualifierProcessing : NewJ2kPostProcessing {
|
||||
private fun check(qualifiedExpression: KtQualifiedExpression): Boolean {
|
||||
val qualifier = (qualifiedExpression.receiverExpression as? KtNameReferenceExpression)
|
||||
?.referenceExpression()
|
||||
?.resolve()?.safeAs<KtClassOrObject>()?.parentClassForCompanionOrThis() ?: return false
|
||||
return qualifier.parentClassForCompanionOrThis() in qualifiedExpression.parentsOfType<KtClassOrObject>()
|
||||
}
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (element !is KtQualifiedExpression) return null
|
||||
if (!check(element)) return null
|
||||
return {
|
||||
if (element.isValid() && check(element)) {
|
||||
element.replace(element.selectorExpression!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val writeActionNeeded: Boolean = true
|
||||
}
|
||||
|
||||
private class RemoveRedundantTypeQualifierProcessing : NewJ2kPostProcessing {
|
||||
private fun check(reference: KtUserType): Boolean {
|
||||
val qualifierClass = reference.qualifier
|
||||
?.referenceExpression
|
||||
?.resolve() as? KtClassOrObject ?: return false
|
||||
val topLevelClass = reference.topLevelContainingClassOrObject() ?: return false
|
||||
return topLevelClass.isAncestor(qualifierClass)
|
||||
}
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (element !is KtUserType) return null
|
||||
if (!check(element)) return null
|
||||
return {
|
||||
if (element.isValid && check(element)) {
|
||||
element.deleteQualifier()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val writeActionNeeded: Boolean = true
|
||||
}
|
||||
|
||||
private class RemoveRedundantConstructorKeywordProcessing : CheckableProcessing<KtPrimaryConstructor>(KtPrimaryConstructor::class) {
|
||||
override fun check(element: KtPrimaryConstructor): Boolean =
|
||||
element.containingClassOrObject is KtClass
|
||||
&& element.getConstructorKeyword() != null
|
||||
&& element.annotationEntries.isEmpty()
|
||||
&& element.visibilityModifier() == null
|
||||
|
||||
|
||||
override fun action(element: KtPrimaryConstructor) {
|
||||
element.removeRedundantConstructorKeywordAndSpace()
|
||||
}
|
||||
}
|
||||
|
||||
private class RemoveRedundantModalityModifierProcessing : CheckableProcessing<KtDeclaration>(KtDeclaration::class) {
|
||||
override fun check(element: KtDeclaration): Boolean {
|
||||
val modalityModifier = element.modalityModifier() ?: return false
|
||||
val modalityModifierType = modalityModifier.node.elementType
|
||||
val implicitModality = element.implicitModality()
|
||||
|
||||
return modalityModifierType == implicitModality
|
||||
}
|
||||
|
||||
override fun action(element: KtDeclaration) {
|
||||
element.removeModifierSmart(element.modalityModifierType()!!)
|
||||
}
|
||||
}
|
||||
|
||||
//hack until KT-30804 is fixed
|
||||
private fun KtModifierListOwner.removeModifierSmart(modifierToken: KtModifierKeywordToken) {
|
||||
val modifier = modifierList?.getModifier(modifierToken)
|
||||
val comment = modifier?.getPrevSiblingIgnoringWhitespace() as? PsiComment
|
||||
comment?.also {
|
||||
it.parent.addAfter(KtPsiFactory(this).createNewLine(), it)
|
||||
}
|
||||
val newElement = copy() as KtModifierListOwner
|
||||
newElement.removeModifier(modifierToken)
|
||||
replace(newElement)
|
||||
containingFile.commitAndUnblockDocument()
|
||||
}
|
||||
|
||||
private class RemoveRedundantVisibilityModifierProcessing : CheckableProcessing<KtDeclaration>(KtDeclaration::class) {
|
||||
override fun check(element: KtDeclaration): Boolean {
|
||||
val visibilityModifier = element.visibilityModifier() ?: return false
|
||||
val implicitVisibility = element.implicitVisibility()
|
||||
return when {
|
||||
visibilityModifier.node.elementType == implicitVisibility ->
|
||||
true
|
||||
element.hasModifier(KtTokens.INTERNAL_KEYWORD) && element.containingClassOrObject?.isLocal == true ->
|
||||
true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun action(element: KtDeclaration) {
|
||||
element.removeModifierSmart(element.visibilityModifierType()!!)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.nj2k.postProcessing
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.editor.RangeMarker
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.LocalSearchScope
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters2
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
|
||||
import org.jetbrains.kotlin.idea.core.util.EDT
|
||||
import org.jetbrains.kotlin.idea.inspections.*
|
||||
import org.jetbrains.kotlin.idea.inspections.branchedTransformations.IfThenToSafeAccessInspection
|
||||
import org.jetbrains.kotlin.idea.inspections.conventionNameCalls.ReplaceGetOrSetInspection
|
||||
import org.jetbrains.kotlin.idea.intentions.*
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnAsymmetricallyIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IfThenToElvisIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isTrivialStatementBody
|
||||
import org.jetbrains.kotlin.idea.quickfix.*
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.j2k.ConverterContext
|
||||
import org.jetbrains.kotlin.j2k.PostProcessor
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
|
||||
import org.jetbrains.kotlin.nj2k.postProcessing.processings.*
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.isNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
|
||||
class NewJ2kPostProcessor : PostProcessor {
|
||||
override fun insertImport(file: KtFile, fqName: FqName) {
|
||||
ApplicationManager.getApplication().invokeAndWait {
|
||||
runWriteAction {
|
||||
val descriptors = file.resolveImportReference(fqName)
|
||||
descriptors.firstOrNull()?.let { ImportInsertHelper.getInstance(file.project).importDescriptor(file, it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun doAdditionalProcessing(file: KtFile, converterContext: ConverterContext?, rangeMarker: RangeMarker?) {
|
||||
runBlocking(EDT.ModalityStateElement(ModalityState.defaultModalityState())) {
|
||||
for (processing in processings) {
|
||||
processing.runProcessing(file, rangeMarker, converterContext as NewJ2kConverterContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val processings: List<GeneralPostProcessing> = listOf(
|
||||
nullabilityProcessing,
|
||||
formatCodeProcessing,
|
||||
shortenReferencesProcessing,
|
||||
InspectionLikeProcessingGroup(VarToValProcessing()),
|
||||
ConvertGettersAndSettersToPropertyProcessing(),
|
||||
InspectionLikeProcessingGroup(MoveGetterAndSetterAnnotationsToPropertyProcessing()),
|
||||
InspectionLikeProcessingGroup(
|
||||
generalInspectionBasedProcessing(RedundantGetterInspection()),
|
||||
generalInspectionBasedProcessing(RedundantSetterInspection())
|
||||
),
|
||||
ConvertToDataClassProcessing(),
|
||||
InspectionLikeProcessingGroup(
|
||||
RemoveRedundantVisibilityModifierProcessing(),
|
||||
RemoveRedundantModalityModifierProcessing(),
|
||||
RemoveRedundantConstructorKeywordProcessing(),
|
||||
diagnosticBasedProcessing(Errors.REDUNDANT_OPEN_IN_INTERFACE) { element: KtDeclaration, _ ->
|
||||
element.removeModifier(KtTokens.OPEN_KEYWORD)
|
||||
},
|
||||
RemoveExplicitOpenInInterfaceProcessing(),
|
||||
generalInspectionBasedProcessing(ExplicitThisInspection()),
|
||||
RemoveExplicitTypeArgumentsProcessing(),
|
||||
RemoveRedundantOverrideVisibilityProcessing(),
|
||||
inspectionBasedProcessing(MoveLambdaOutsideParenthesesInspection()),
|
||||
generalInspectionBasedProcessing(RedundantCompanionReferenceInspection()),
|
||||
FixObjectStringConcatenationProcessing(),
|
||||
ConvertToStringTemplateProcessing(),
|
||||
UsePropertyAccessSyntaxProcessing(),
|
||||
UninitializedVariableReferenceFromInitializerToThisReferenceProcessing(),
|
||||
UnresolvedVariableReferenceFromInitializerToThisReferenceProcessing(),
|
||||
RemoveRedundantSamAdaptersProcessing(),
|
||||
RemoveRedundantCastToNullableProcessing(),
|
||||
inspectionBasedProcessing(ReplacePutWithAssignmentInspection()),
|
||||
UseExpressionBodyProcessing(),
|
||||
inspectionBasedProcessing(UnnecessaryVariableInspection()),
|
||||
RemoveExplicitPropertyTypeWithInspectionProcessing(),
|
||||
generalInspectionBasedProcessing(RedundantUnitReturnTypeInspection()),
|
||||
JavaObjectEqualsToEqOperatorProcessing(),
|
||||
RemoveExplicitPropertyTypeProcessing(),
|
||||
RemoveRedundantNullabilityProcessing(),
|
||||
generalInspectionBasedProcessing(CanBeValInspection(ignoreNotUsedVals = false)),
|
||||
intentionBasedProcessing(FoldInitializerAndIfToElvisIntention()),
|
||||
generalInspectionBasedProcessing(RedundantSemicolonInspection()),
|
||||
intentionBasedProcessing(RemoveEmptyClassBodyIntention()),
|
||||
intentionBasedProcessing(
|
||||
RemoveRedundantCallsOfConversionMethodsIntention()
|
||||
),
|
||||
inspectionBasedProcessing(JavaMapForEachInspection()),
|
||||
|
||||
intentionBasedProcessing(FoldIfToReturnIntention()) { it.then.isTrivialStatementBody() && it.`else`.isTrivialStatementBody() },
|
||||
intentionBasedProcessing(FoldIfToReturnAsymmetricallyIntention()) {
|
||||
it.then.isTrivialStatementBody() && (KtPsiUtil.skipTrailingWhitespacesAndComments(
|
||||
it
|
||||
) as KtReturnExpression).returnedExpression.isTrivialStatementBody()
|
||||
},
|
||||
|
||||
inspectionBasedProcessing(IfThenToSafeAccessInspection()),
|
||||
inspectionBasedProcessing(IfThenToSafeAccessInspection()),
|
||||
intentionBasedProcessing(IfThenToElvisIntention()),
|
||||
inspectionBasedProcessing(SimplifyNegatedBinaryExpressionInspection()),
|
||||
inspectionBasedProcessing(ReplaceGetOrSetInspection()),
|
||||
intentionBasedProcessing(AddOperatorModifierIntention()),
|
||||
intentionBasedProcessing(ObjectLiteralToLambdaIntention()),
|
||||
intentionBasedProcessing(AnonymousFunctionToLambdaIntention()),
|
||||
intentionBasedProcessing(RemoveUnnecessaryParenthesesIntention()),
|
||||
intentionBasedProcessing(DestructureIntention()),
|
||||
inspectionBasedProcessing(SimplifyAssertNotNullInspection()),
|
||||
intentionBasedProcessing(
|
||||
RemoveRedundantCallsOfConversionMethodsIntention()
|
||||
),
|
||||
generalInspectionBasedProcessing(LiftReturnOrAssignmentInspection()),
|
||||
generalInspectionBasedProcessing(MayBeConstantInspection()),
|
||||
intentionBasedProcessing(RemoveEmptyPrimaryConstructorIntention()),
|
||||
diagnosticBasedProcessing(Errors.PLATFORM_CLASS_MAPPED_TO_KOTLIN) { element: KtDotQualifiedExpression, _ ->
|
||||
val parent = element.parent as? KtImportDirective ?: return@diagnosticBasedProcessing
|
||||
parent.delete()
|
||||
},
|
||||
|
||||
diagnosticBasedProcessing(
|
||||
Errors.UNSAFE_CALL,
|
||||
Errors.UNSAFE_INFIX_CALL,
|
||||
Errors.UNSAFE_OPERATOR_CALL
|
||||
) { element: PsiElement, diagnostic ->
|
||||
val action =
|
||||
AddExclExclCallFix.createActions(diagnostic).singleOrNull()
|
||||
?: return@diagnosticBasedProcessing
|
||||
action.invoke(element.project, null, element.containingFile)
|
||||
},
|
||||
|
||||
diagnosticBasedProcessingWithFixFactory(
|
||||
MissingIteratorExclExclFixFactory,
|
||||
Errors.ITERATOR_ON_NULLABLE
|
||||
),
|
||||
diagnosticBasedProcessingWithFixFactory(
|
||||
SmartCastImpossibleExclExclFixFactory,
|
||||
Errors.SMARTCAST_IMPOSSIBLE
|
||||
),
|
||||
diagnosticBasedProcessing(Errors.TYPE_MISMATCH) { element: PsiElement, diagnostic ->
|
||||
@Suppress("UNCHECKED_CAST") val diagnosticWithParameters =
|
||||
diagnostic as? DiagnosticWithParameters2<KtExpression, KotlinType, KotlinType>
|
||||
?: return@diagnosticBasedProcessing
|
||||
val expectedType = diagnosticWithParameters.a
|
||||
val realType = diagnosticWithParameters.b
|
||||
if (realType.makeNotNullable().isSubtypeOf(expectedType.makeNotNullable())
|
||||
&& realType.isNullable()
|
||||
&& !expectedType.isNullable()
|
||||
) {
|
||||
val factory = KtPsiFactory(element)
|
||||
element.replace(factory.createExpressionByPattern("($0)!!", element.text))
|
||||
}
|
||||
},
|
||||
|
||||
diagnosticBasedProcessingWithFixFactory(
|
||||
ReplacePrimitiveCastWithNumberConversionFix,
|
||||
Errors.CAST_NEVER_SUCCEEDS
|
||||
),
|
||||
diagnosticBasedProcessingWithFixFactory(
|
||||
ChangeCallableReturnTypeFix.ReturnTypeMismatchOnOverrideFactory,
|
||||
Errors.RETURN_TYPE_MISMATCH_ON_OVERRIDE
|
||||
),
|
||||
|
||||
diagnosticBasedProcessing<KtBinaryExpressionWithTypeRHS>(Errors.USELESS_CAST) { element, _ ->
|
||||
if (element.left.isNullExpression()) return@diagnosticBasedProcessing
|
||||
val expression = RemoveUselessCastFix.invoke(element)
|
||||
|
||||
val variable = expression.parent as? KtProperty
|
||||
if (variable != null && expression == variable.initializer && variable.isLocal) {
|
||||
val ref = ReferencesSearch.search(variable, LocalSearchScope(variable.containingFile)).findAll().singleOrNull()
|
||||
if (ref != null && ref.element is KtSimpleNameExpression) {
|
||||
ref.element.replace(expression)
|
||||
variable.delete()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
diagnosticBasedProcessingWithFixFactory(
|
||||
RemoveModifierFix.createRemoveProjectionFactory(true),
|
||||
Errors.REDUNDANT_PROJECTION
|
||||
),
|
||||
diagnosticBasedProcessingWithFixFactory(
|
||||
AddModifierFix.createFactory(KtTokens.OVERRIDE_KEYWORD),
|
||||
Errors.VIRTUAL_MEMBER_HIDDEN
|
||||
),
|
||||
diagnosticBasedProcessingWithFixFactory(
|
||||
RemoveModifierFix.createRemoveModifierFromListOwnerFactory(KtTokens.OPEN_KEYWORD),
|
||||
Errors.NON_FINAL_MEMBER_IN_FINAL_CLASS, Errors.NON_FINAL_MEMBER_IN_OBJECT
|
||||
),
|
||||
diagnosticBasedProcessingWithFixFactory(
|
||||
MakeVisibleFactory,
|
||||
Errors.INVISIBLE_MEMBER
|
||||
),
|
||||
|
||||
diagnosticBasedProcessingFactory(
|
||||
Errors.VAL_REASSIGNMENT, Errors.CAPTURED_VAL_INITIALIZATION, Errors.CAPTURED_MEMBER_VAL_INITIALIZATION
|
||||
) { element: KtSimpleNameExpression, _: Diagnostic ->
|
||||
val property = element.mainReference.resolve() as? KtProperty
|
||||
if (property == null) {
|
||||
null
|
||||
} else {
|
||||
val action = {
|
||||
if (!property.isVar) {
|
||||
property.valOrVarKeyword.replace(KtPsiFactory(element.project).createVarKeyword())
|
||||
}
|
||||
}
|
||||
action
|
||||
}
|
||||
},
|
||||
|
||||
diagnosticBasedProcessing<KtSimpleNameExpression>(Errors.UNNECESSARY_NOT_NULL_ASSERTION) { element, _ ->
|
||||
val exclExclExpr = element.parent as KtUnaryExpression
|
||||
val baseExpression = exclExclExpr.baseExpression!!
|
||||
val context = baseExpression.analyze(BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS)
|
||||
if (context.diagnostics.forElement(element).any { it.factory == Errors.UNNECESSARY_NOT_NULL_ASSERTION }) {
|
||||
exclExclExpr.replace(baseExpression)
|
||||
}
|
||||
},
|
||||
RemoveForExpressionLoopParameterTypeProcessing()
|
||||
),
|
||||
formatCodeProcessing
|
||||
)
|
||||
-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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.nj2k.postProcessing
|
||||
package org.jetbrains.kotlin.nj2k.postProcessing.processings
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.LocalSearchScope
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||
import org.jetbrains.kotlin.idea.core.setVisibility
|
||||
import org.jetbrains.kotlin.idea.intentions.addUseSiteTarget
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.util.CommentSaver
|
||||
import org.jetbrains.kotlin.nj2k.NewJ2kPostProcessing
|
||||
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
|
||||
import org.jetbrains.kotlin.nj2k.escaped
|
||||
import org.jetbrains.kotlin.nj2k.postProcessing.ElementsBasedPostProcessing
|
||||
import org.jetbrains.kotlin.nj2k.postProcessing.descendantsOfType
|
||||
import org.jetbrains.kotlin.nj2k.postProcessing.type
|
||||
import org.jetbrains.kotlin.nj2k.postProcessing.unpackedReferenceToProperty
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.addRemoveModifier.setModifierList
|
||||
import org.jetbrains.kotlin.psi.psiUtil.asAssignment
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClass
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierTypeOrDefault
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
|
||||
class ConvertDataClass : NewJ2kPostProcessing {
|
||||
override val writeActionNeeded: Boolean = true
|
||||
|
||||
class ConvertToDataClassProcessing : ElementsBasedPostProcessing() {
|
||||
private fun KtCallableDeclaration.rename(newName: String) {
|
||||
val factory = KtPsiFactory(this)
|
||||
val escapedName = newName.escaped()
|
||||
@@ -49,6 +52,7 @@ class ConvertDataClass : NewJ2kPostProcessing {
|
||||
?.unpackedReferenceToProperty()
|
||||
?.takeIf { it.containingClass() == klass } ?: return@mapNotNull null
|
||||
if (property.getter != null || property.setter != null) return@mapNotNull null
|
||||
if (property.initializer != null) return@mapNotNull null
|
||||
val constructorParameter =
|
||||
((statement.right as? KtReferenceExpression)
|
||||
?.references
|
||||
@@ -62,41 +66,51 @@ class ConvertDataClass : NewJ2kPostProcessing {
|
||||
|
||||
if (constructorParameterType.makeNotNullable() != propertyType.makeNotNullable()) return@mapNotNull null
|
||||
|
||||
DataClassInfo(constructorParameter, property, statement)
|
||||
DataClassInfo(
|
||||
constructorParameter,
|
||||
property,
|
||||
statement
|
||||
)
|
||||
}.toList()
|
||||
|
||||
override fun createAction(element: PsiElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||
if (element !is KtClass) return null
|
||||
return {
|
||||
val factory = KtPsiFactory(element)
|
||||
for ((constructorParameter, property, statement) in collectPropertiesData(element)) {
|
||||
constructorParameter.addBefore(property.valOrVarKeyword, constructorParameter.nameIdentifier!!)
|
||||
constructorParameter.addAfter(factory.createWhiteSpace(), constructorParameter.valOrVarKeyword!!)
|
||||
constructorParameter.rename(property.name!!)
|
||||
val propertyCommentSaver = CommentSaver(property, saveLineBreaks = true)
|
||||
if (property.modifierList != null) {
|
||||
constructorParameter.setModifierList(property.modifierList!!)
|
||||
constructorParameter.annotationEntries.forEach { it.delete() }
|
||||
for (entry in constructorParameter.annotationEntries) {
|
||||
entry.addUseSiteTarget(AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER, element.project)
|
||||
}
|
||||
}
|
||||
override fun runProcessing(elements: List<PsiElement>, converterContext: NewJ2kConverterContext) {
|
||||
for (klass in elements.descendantsOfType<KtClass>()) {
|
||||
convertClass(klass)
|
||||
}
|
||||
}
|
||||
|
||||
for (annotationEntry in property.annotationEntries) {
|
||||
constructorParameter.addAnnotationEntry(annotationEntry).also { entry ->
|
||||
if (entry.useSiteTarget == null) {
|
||||
entry.addUseSiteTarget(AnnotationUseSiteTarget.FIELD, element.project)
|
||||
}
|
||||
private fun convertClass(klass: KtClass) {
|
||||
val factory = KtPsiFactory(klass)
|
||||
for ((constructorParameter, property, statement) in collectPropertiesData(klass)) {
|
||||
constructorParameter.addBefore(property.valOrVarKeyword, constructorParameter.nameIdentifier!!)
|
||||
constructorParameter.addAfter(factory.createWhiteSpace(), constructorParameter.valOrVarKeyword!!)
|
||||
constructorParameter.rename(property.name!!)
|
||||
val propertyCommentSaver = CommentSaver(property, saveLineBreaks = true)
|
||||
|
||||
constructorParameter.setVisibility(property.visibilityModifierTypeOrDefault())
|
||||
for (annotationEntry in constructorParameter.annotationEntries) {
|
||||
if (annotationEntry.useSiteTarget == null) {
|
||||
annotationEntry.addUseSiteTarget(AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER, klass.project)
|
||||
}
|
||||
}
|
||||
|
||||
for (annotationEntry in property.annotationEntries) {
|
||||
constructorParameter.addAnnotationEntry(annotationEntry).also { entry ->
|
||||
if (entry.useSiteTarget == null) {
|
||||
entry.addUseSiteTarget(AnnotationUseSiteTarget.FIELD, klass.project)
|
||||
}
|
||||
}
|
||||
property.delete()
|
||||
statement.delete()
|
||||
propertyCommentSaver.restore(constructorParameter, forceAdjustIndent = false)
|
||||
}
|
||||
for (initBlock in element.getAnonymousInitializers()) {
|
||||
if ((initBlock.body as KtBlockExpression).statements.isEmpty()) {
|
||||
initBlock.delete()
|
||||
}
|
||||
property.delete()
|
||||
statement.delete()
|
||||
propertyCommentSaver.restore(constructorParameter, forceAdjustIndent = false)
|
||||
}
|
||||
|
||||
for (initBlock in klass.getAnonymousInitializers()) {
|
||||
if ((initBlock.body as KtBlockExpression).statements.isEmpty()) {
|
||||
val commentSaver = CommentSaver(initBlock)
|
||||
initBlock.delete()
|
||||
klass.primaryConstructor?.let { commentSaver.restore(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
+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.j2k.AbstractJavaToKotlinConverterSingleFileTest
|
||||
import org.jetbrains.kotlin.j2k.ConverterSettings
|
||||
import org.jetbrains.kotlin.nj2k.postProcessing.NewJ2kPostProcessor
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import java.io.File
|
||||
|
||||
@@ -50,7 +51,7 @@ abstract class AbstractNewJavaToKotlinConverterSingleFileTest : AbstractJavaToKo
|
||||
override fun fileToKotlin(text: String, settings: ConverterSettings, project: Project): String {
|
||||
val file = createJavaFile(text)
|
||||
return NewJavaToKotlinConverter(project, settings, IdeaJavaToKotlinServices)
|
||||
.filesToKotlin(listOf(file), NewJ2kPostProcessor(true)).results.single()
|
||||
.filesToKotlin(listOf(file), NewJ2kPostProcessor()).results.single()
|
||||
}
|
||||
|
||||
override fun provideExpectedFile(javaPath: String): File =
|
||||
|
||||
Reference in New Issue
Block a user