Shortcut quick fix for migrating all coroutines in the project (KT-27164)
#KT-27164 Fixed
This commit is contained in:
+64
-3
@@ -5,9 +5,15 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.inspections.migration
|
||||
|
||||
import com.intellij.analysis.AnalysisScope
|
||||
import com.intellij.codeInspection.*
|
||||
import com.intellij.codeInspection.actions.RunInspectionIntention
|
||||
import com.intellij.codeInspection.ex.InspectionManagerEx
|
||||
import com.intellij.codeInspection.ex.InspectionProfileImpl
|
||||
import com.intellij.codeInspection.ex.InspectionToolWrapper
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.profile.codeInspection.InspectionProjectProfileManager
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.config.LanguageVersion
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
@@ -22,6 +28,7 @@ import org.jetbrains.kotlin.idea.quickfix.migration.MigrationFix
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelFunctionFqnNameIndex
|
||||
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
@@ -82,6 +89,52 @@ private interface CoroutineMigrationProblem {
|
||||
fun report(holder: ProblemsHolder, isOnTheFly: Boolean, simpleNameExpression: KtSimpleNameExpression): Boolean
|
||||
}
|
||||
|
||||
// Shortcut quick fix for running inspection in the project scope.
|
||||
// Should work like RunInspectionAction.runInspection.
|
||||
private class ObsoleteCoroutineUsageInWholeFix : LocalQuickFix {
|
||||
override fun getFamilyName(): String = "Fix experimental coroutines usages in the project"
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
val toolWrapper = InspectionProjectProfileManager.getInstance(project).currentProfile.getInspectionTool(
|
||||
ObsoleteExperimentalCoroutinesInspection().shortName,
|
||||
project
|
||||
)!!
|
||||
|
||||
runToolInProject(project, toolWrapper)
|
||||
}
|
||||
|
||||
override fun startInWriteAction(): Boolean = false
|
||||
|
||||
companion object {
|
||||
val INSTANCE = ObsoleteCoroutineUsageInWholeFix()
|
||||
|
||||
private fun runToolInProject(project: Project, toolWrapper: InspectionToolWrapper<*, *>) {
|
||||
val managerEx = InspectionManager.getInstance(project) as InspectionManagerEx
|
||||
val kotlinSourcesScope: GlobalSearchScope = KotlinSourceFilterScope.projectSources(GlobalSearchScope.allScope(project), project)
|
||||
val cleanupScope = AnalysisScope(kotlinSourcesScope, project)
|
||||
|
||||
val cleanupToolProfile = runInInspectionProfileInitMode { RunInspectionIntention.createProfile(toolWrapper, managerEx, null) }
|
||||
managerEx.createNewGlobalContext(false)
|
||||
.codeCleanup(cleanupScope, cleanupToolProfile, "Apply in the project: " + toolWrapper.displayName, null, false)
|
||||
}
|
||||
|
||||
// Overcome failure during profile createin because of absent tools in tests
|
||||
inline fun <T> runInInspectionProfileInitMode(runnable: () -> T): T {
|
||||
return if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
runnable()
|
||||
} else {
|
||||
val old = InspectionProfileImpl.INIT_INSPECTIONS
|
||||
try {
|
||||
InspectionProfileImpl.INIT_INSPECTIONS = true
|
||||
runnable()
|
||||
} finally {
|
||||
InspectionProfileImpl.INIT_INSPECTIONS = old
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* There should be a single fix class with the same family name, this way it can be executed for all found coroutines problems from UI.
|
||||
*/
|
||||
@@ -117,6 +170,14 @@ private fun isTopLevelCallForReplace(simpleNameExpression: KtSimpleNameExpressio
|
||||
return !isInIndex
|
||||
}
|
||||
|
||||
private fun fixesWithWholeProject(isOnTheFly: Boolean, fix: LocalQuickFix): Array<LocalQuickFix> {
|
||||
if (!isOnTheFly) {
|
||||
return arrayOf(fix)
|
||||
}
|
||||
|
||||
return arrayOf(fix, ObsoleteCoroutineUsageInWholeFix.INSTANCE)
|
||||
}
|
||||
|
||||
private class ObsoleteTopLevelFunctionUsage(
|
||||
val textMarker: String, val oldFqName: String, val newFqName: String
|
||||
) : CoroutineMigrationProblem {
|
||||
@@ -131,7 +192,7 @@ private class ObsoleteTopLevelFunctionUsage(
|
||||
simpleNameExpression,
|
||||
"`$newFqName` is expected to be used since Kotlin 1.3",
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
ObsoleteCoroutineUsageFix(fix)
|
||||
*fixesWithWholeProject(isOnTheFly, ObsoleteCoroutineUsageFix(fix))
|
||||
)
|
||||
|
||||
return true
|
||||
@@ -167,7 +228,7 @@ private class ObsoleteExtensionFunctionUsage(
|
||||
simpleNameExpression,
|
||||
"Methods are absent in coroutines class since 1.3",
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
ObsoleteCoroutineUsageFix(fix)
|
||||
*fixesWithWholeProject(isOnTheFly, ObsoleteCoroutineUsageFix(fix))
|
||||
)
|
||||
|
||||
return true
|
||||
@@ -207,7 +268,7 @@ private class ExperimentalImportUsage : CoroutineMigrationProblem {
|
||||
reportExpression,
|
||||
"Experimental coroutines usages are obsolete since 1.3",
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
ObsoleteCoroutineUsageFix(ObsoleteCoroutineImportFix)
|
||||
*fixesWithWholeProject(isOnTheFly, ObsoleteCoroutineUsageFix(ObsoleteCoroutineImportFix))
|
||||
)
|
||||
|
||||
return true
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// "Fix experimental coroutines usages in the project" "true"
|
||||
// WITH_RUNTIME
|
||||
package migrate
|
||||
|
||||
import kotlin.coroutines.experimental.buildIterator
|
||||
import kotlin.coroutines.experimental.buildSequence
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val one = <caret>buildSequence {
|
||||
yield(1)
|
||||
}
|
||||
|
||||
val two = buildIterator {
|
||||
yield(1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Fix experimental coroutines usages in the project" "true"
|
||||
// WITH_RUNTIME
|
||||
package migrate
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val one = sequence {
|
||||
yield(1)
|
||||
}
|
||||
|
||||
val two = iterator {
|
||||
yield(1)
|
||||
}
|
||||
}
|
||||
@@ -8347,6 +8347,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
runTest("idea/testData/quickfix/obsoleteCoroutines/buildSequenceImport.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inProjectFix.kt")
|
||||
public void testInProjectFix() throws Exception {
|
||||
runTest("idea/testData/quickfix/obsoleteCoroutines/inProjectFix.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("resume.kt")
|
||||
public void testResume() throws Exception {
|
||||
runTest("idea/testData/quickfix/obsoleteCoroutines/resume.kt");
|
||||
|
||||
Reference in New Issue
Block a user