From 6e7ea662c8b3c1ffebb96863d7b8bd9f5f50d042 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 20 May 2015 13:09:23 +0200 Subject: [PATCH] "Code Cleanup" action to migrate all instances of deprecated syntax to new one --- .../kotlin/idea/highlighter/JetPsiChecker.kt | 24 +++--- idea/src/META-INF/plugin.xml | 8 ++ .../inspections/KotlinCleanupInspection.kt | 84 +++++++++++++++++++ .../quickfix/DeprecatedLambdaSyntaxFix.kt | 2 +- idea/testData/inspections/cleanup/cleanup.kt | 20 +++++ .../inspections/cleanup/cleanup.kt.after | 22 +++++ .../KotlinCleanupInspectionTest.kt | 47 +++++++++++ 7 files changed, 194 insertions(+), 13 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt create mode 100644 idea/testData/inspections/cleanup/cleanup.kt create mode 100644 idea/testData/inspections/cleanup/cleanup.kt.after create mode 100644 idea/tests/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspectionTest.kt diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/JetPsiChecker.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/JetPsiChecker.kt index 8e1b92138a6..cd33537b839 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/JetPsiChecker.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/JetPsiChecker.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.highlighter import com.intellij.codeInsight.daemon.impl.HighlightRangeExtension import com.intellij.codeInsight.intention.EmptyIntentionAction +import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInspection.ProblemHighlightType import com.intellij.lang.annotation.Annotation import com.intellij.lang.annotation.AnnotationHolder @@ -179,18 +180,8 @@ public open class JetPsiChecker : Annotator, HighlightRangeExtension { } private fun registerQuickFix(annotation: Annotation, diagnostic: Diagnostic) { - val intentionActionsFactories = QuickFixes.getActionsFactories(diagnostic.getFactory()) - for (intentionActionsFactory in intentionActionsFactories) { - if (intentionActionsFactory != null) { - for (action in intentionActionsFactory.createActions(diagnostic)) { - annotation.registerFix(action) - } - } - } - - val actions = QuickFixes.getActions(diagnostic.getFactory()) - for (action in actions) { - annotation.registerFix(action) + createQuickfixes(diagnostic).forEach { + annotation.registerFix(it) } // Making warnings suppressable @@ -261,5 +252,14 @@ public open class JetPsiChecker : Annotator, HighlightRangeExtension { //DeprecatedAnnotationVisitor(holder, bindingContext) ) + public fun createQuickfixes(diagnostic: Diagnostic): Collection { + val result = arrayListOf() + val intentionActionsFactories = QuickFixes.getActionsFactories(diagnostic.getFactory()) + for (intentionActionsFactory in intentionActionsFactories.filterNotNull()) { + result.addAll(intentionActionsFactory.createActions(diagnostic)) + } + result.addAll(QuickFixes.getActions(diagnostic.getFactory())) + return result + } } } diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 29eecf8879a..38c9a88c3da 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1007,6 +1007,14 @@ level="WARNING" /> + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt new file mode 100644 index 00000000000..40ae98bd759 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt @@ -0,0 +1,84 @@ +/* + * 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.idea.inspections + +import com.intellij.codeInspection.* +import com.intellij.openapi.progress.ProcessCanceledException +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.DiagnosticFactory +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages +import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult +import org.jetbrains.kotlin.idea.highlighter.JetPsiChecker +import org.jetbrains.kotlin.idea.quickfix.JetWholeProjectModalAction +import org.jetbrains.kotlin.idea.util.ProjectRootsUtil +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.psi.JetTreeVisitorVoid +import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm + +public class KotlinCleanupInspection(): LocalInspectionTool(), CleanupLocalInspectionTool { + // required to simplify the inspection registration in tests + override fun getDisplayName(): String = "Deprecated language feature" + + override fun checkFile(file: PsiFile, manager: InspectionManager, isOnTheFly: Boolean): Array? { + if (isOnTheFly || file !is JetFile || !ProjectRootsUtil.isInProjectSource(file)) { + return null + } + val analysisResult = file.analyzeFullyAndGetResult() + if (analysisResult.isError()) { + throw ProcessCanceledException(analysisResult.error) + } + + val diagnostics = analysisResult.bindingContext.getDiagnostics() + val problemDescriptors = arrayListOf() + file.acceptChildren(object: JetTreeVisitorVoid() { + override fun visitElement(element: PsiElement) { + super.visitElement(element) + val collection = diagnostics.forElement(element) + collection.forEach { + if (it.getFactory().isCleanup()) { + problemDescriptors.add(it.toProblemDescriptor(file, manager)) + } + } + } + }) + return problemDescriptors.toTypedArray() + } + + private fun DiagnosticFactory<*>.isCleanup() = + this == Errors.DEPRECATED_TRAIT_KEYWORD || + this == Errors.DEPRECATED_ANNOTATION_SYNTAX || + this == Errors.ENUM_ENTRY_USES_DEPRECATED_OR_NO_DELIMITER || + this == Errors.ENUM_ENTRY_USES_DEPRECATED_SUPER_CONSTRUCTOR || + this == Errors.DEPRECATED_LAMBDA_SYNTAX || + this == Errors.JAVA_LANG_CLASS_PARAMETER_IN_ANNOTATION || + this == ErrorsJvm.JAVA_LANG_CLASS_ARGUMENT_IN_ANNOTATION + + private fun Diagnostic.toProblemDescriptor(file: JetFile, manager: InspectionManager): ProblemDescriptor? { + val quickFixes = JetPsiChecker.createQuickfixes(this) + .filter { it !is JetWholeProjectModalAction<*> } + .map { IntentionWrapper(it, file) } + + return manager.createProblemDescriptor(getPsiElement(), + DefaultErrorMessages.render(this), + false, + quickFixes.toTypedArray(), + ProblemHighlightType.GENERIC_ERROR_OR_WARNING) + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedLambdaSyntaxFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedLambdaSyntaxFix.kt index 93cca5850b0..f5b4cb43942 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedLambdaSyntaxFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedLambdaSyntaxFix.kt @@ -39,7 +39,7 @@ public class DeprecatedLambdaSyntaxFix(element: JetFunctionLiteralExpression) : override fun getText() = JetBundle.message("migrate.lambda.syntax") override fun getFamilyName() = JetBundle.message("migrate.lambda.syntax.family") - override fun invoke(project: Project, editor: Editor, file: JetFile) { + override fun invoke(project: Project, editor: Editor?, file: JetFile) { DeprecatedSyntaxFix.createFix(element).runFix() } diff --git a/idea/testData/inspections/cleanup/cleanup.kt b/idea/testData/inspections/cleanup/cleanup.kt new file mode 100644 index 00000000000..d1cf1f50c7c --- /dev/null +++ b/idea/testData/inspections/cleanup/cleanup.kt @@ -0,0 +1,20 @@ +trait Foo { +} + + +[deprecated("boo")] fun bar() {} + +enum class E { + First Second +} + +enum class F(val name: String) { + First: F("First") + Second: F("Second") +} + +val f = { (a: Int, b: Int) -> a + b } + +annotation class Ann(val arg1: Class<*>, val arg2: Class) + +Ann(javaClass(), javaClass()) class MyClass diff --git a/idea/testData/inspections/cleanup/cleanup.kt.after b/idea/testData/inspections/cleanup/cleanup.kt.after new file mode 100644 index 00000000000..8fa7a99f0bc --- /dev/null +++ b/idea/testData/inspections/cleanup/cleanup.kt.after @@ -0,0 +1,22 @@ +import kotlin.reflect.KClass + +interface Foo { +} + + +@deprecated("boo") fun bar() {} + +enum class E { + First, Second +} + +enum class F(val name: String) { + First("First"), + Second("Second") +} + +val f = { a: Int, b: Int -> a + b } + +annotation class Ann(val arg1: KClass<*>, val arg2: KClass) + +Ann(String::class, Int::class) class MyClass diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspectionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspectionTest.kt new file mode 100644 index 00000000000..0449be6e7a8 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspectionTest.kt @@ -0,0 +1,47 @@ +/* + * 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.idea.inspections + +import com.intellij.analysis.AnalysisScope +import com.intellij.codeInspection.InspectionManager +import com.intellij.codeInspection.ex.GlobalInspectionContextBase +import com.intellij.profile.codeInspection.InspectionProjectProfileManager +import com.intellij.testFramework.LightProjectDescriptor +import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.JetWithJdkAndRuntimeLightProjectDescriptor +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase + +class KotlinCleanupInspectionTest(): JetLightCodeInsightFixtureTestCase() { + override fun getTestDataPath(): String + = PluginTestCaseBase.getTestDataPathBase() + "/inspections/cleanup" + + override fun getProjectDescriptor(): LightProjectDescriptor = JetWithJdkAndRuntimeLightProjectDescriptor.INSTANCE + + public fun testCleanup() { + myFixture.enableInspections(javaClass()) + myFixture.configureByFile("cleanup.kt") + + val project = myFixture.getProject() + val managerEx = InspectionManager.getInstance(project) + val globalContext = managerEx.createNewGlobalContext(false) as GlobalInspectionContextBase + val analysisScope = AnalysisScope(myFixture.getFile()) + val profile = InspectionProjectProfileManager.getInstance(project).getInspectionProfile() + globalContext.codeCleanup(project, analysisScope, profile, "Cleanup", null, true) + + myFixture.checkResultByFile("cleanup.kt.after") + } +} \ No newline at end of file