From 5cf6b860a607c030e759835b2aaf98531038277e Mon Sep 17 00:00:00 2001 From: Roman Golyshev Date: Sat, 4 Jul 2020 21:07:30 +0300 Subject: [PATCH] KT-39869 Add inspection for FQN usages of `kotlin.browser` package - `kotlin.dom` does not need this because it contains only extensions - Add test for launching whole project fix; mute it because currently it does not pass - The test fails because in tests `RefJavaManager` tries to create `RefJavaFileImpl` for .kt files. It will try to use UAST, but it does not work for JS files. In production this is disabled, so no problems occur --- .../ObsoleteKotlinJsPackagesInspection.kt | 58 ++++++++++++++++++- .../kotlinBrowserFullyQualifiedProperty.kt | 10 ++++ ...tlinBrowserFullyQualifiedProperty.kt.after | 10 ++++ .../kotlinDomAndBrowserImport.kt | 11 ++++ .../kotlinDomAndBrowserImport.kt.after | 11 ++++ .../idea/quickfix/QuickFixTestGenerated.java | 10 ++++ tests/mute-common.csv | 1 + 7 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinBrowserFullyQualifiedProperty.kt create mode 100644 idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinBrowserFullyQualifiedProperty.kt.after create mode 100644 idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinDomAndBrowserImport.kt create mode 100644 idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinDomAndBrowserImport.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/migration/ObsoleteKotlinJsPackagesInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/migration/ObsoleteKotlinJsPackagesInspection.kt index 998d76c2e83..05fbda5288d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/migration/ObsoleteKotlinJsPackagesInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/migration/ObsoleteKotlinJsPackagesInspection.kt @@ -6,14 +6,23 @@ package org.jetbrains.kotlin.idea.inspections.migration import com.intellij.codeInspection.* +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.backend.common.serialization.findPackage import org.jetbrains.kotlin.config.LanguageVersion +import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.idea.KotlinBundle +import org.jetbrains.kotlin.idea.codeInsight.shorten.performDelayedRefactoringRequests +import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors +import org.jetbrains.kotlin.psi.KtDotQualifiedExpression +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.psi.KtSimpleNameExpression internal class ObsoleteKotlinJsPackagesInspection : ObsoleteCodeMigrationInspection() { override val fromVersion: LanguageVersion = LanguageVersion.KOTLIN_1_3 override val toVersion: LanguageVersion = LanguageVersion.KOTLIN_1_4 override val problemReporters: List = listOf( + KotlinBrowserFullyQualifiedUsageReporter, KotlinBrowserImportUsageReporter, KotlinDomImportUsageReporter ) @@ -25,6 +34,7 @@ private object ObsoleteKotlinJsPackagesUsagesInWholeProjectFix : ObsoleteCodeInW } private const val KOTLIN_BROWSER_PACKAGE = "kotlin.browser" +private const val KOTLINX_BROWSER_PACKAGE = "kotlinx.browser" private const val KOTLIN_DOM_PACKAGE = "kotlin.dom" private class ObsoleteKotlinBrowserUsageFix(delegate: ObsoleteCodeFix) : ObsoleteCodeFixDelegateQuickFix(delegate) { @@ -35,9 +45,55 @@ private class ObsoleteKotlinDomUsageFix(delegate: ObsoleteCodeFix) : ObsoleteCod override fun getFamilyName(): String = KotlinBundle.message("obsolete.package.usage.fix.family.name", KOTLIN_DOM_PACKAGE) } +/** + * We have such inspection only for 'kotlin.browser' package; it is because 'kotlin.dom' package has only extension functions. Such + * functions cannot be used with fully qualified name. + */ +private object KotlinBrowserFullyQualifiedUsageReporter : ObsoleteCodeProblemReporter { + override fun report(holder: ProblemsHolder, isOnTheFly: Boolean, simpleNameExpression: KtSimpleNameExpression): Boolean { + val fullyQualifiedExpression = simpleNameExpression.parent as? KtDotQualifiedExpression ?: return false + + val kotlinBrowserQualifier = fullyQualifiedExpression.receiverExpression as? KtDotQualifiedExpression ?: return false + if (kotlinBrowserQualifier.text != KOTLIN_BROWSER_PACKAGE) return false + + if (!resolvesToKotlinBrowserPackage(simpleNameExpression)) return false + + holder.registerProblem( + kotlinBrowserQualifier, + KotlinBundle.message("package.usages.are.obsolete.since.1.4", KOTLIN_DOM_PACKAGE), + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + *fixesWithWholeProject( + isOnTheFly, + fix = ObsoleteKotlinBrowserUsageFix(KotlinBrowserFullyQualifiedUsageFix), + wholeProjectFix = ObsoleteKotlinJsPackagesUsagesInWholeProjectFix + ) + ) + + return true + } + + private fun resolvesToKotlinBrowserPackage(simpleNameExpression: KtSimpleNameExpression): Boolean { + val referencedDescriptor = + simpleNameExpression.resolveMainReferenceToDescriptors().singleOrNull() as? CallableDescriptor ?: return false + + return referencedDescriptor.findPackage().fqName.asString() == KOTLIN_BROWSER_PACKAGE + } + + object KotlinBrowserFullyQualifiedUsageFix : ObsoleteCodeFix { + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val oldQualifier = descriptor.psiElement as? KtDotQualifiedExpression ?: return + + val newQualifier = KtPsiFactory(oldQualifier).createExpression(KOTLINX_BROWSER_PACKAGE) + oldQualifier.replace(newQualifier) + + performDelayedRefactoringRequests(project) + } + } +} + private object KotlinBrowserImportUsageReporter : ObsoleteImportsUsageReporter() { override val textMarker: String = "browser" - override val packageBindings: Map = mapOf(KOTLIN_BROWSER_PACKAGE to "kotlinx.browser") + override val packageBindings: Map = mapOf(KOTLIN_BROWSER_PACKAGE to KOTLINX_BROWSER_PACKAGE) override val wholeProjectFix: LocalQuickFix = ObsoleteKotlinJsPackagesUsagesInWholeProjectFix override fun problemMessage(): String = KotlinBundle.message("package.usages.are.obsolete.since.1.4", KOTLIN_BROWSER_PACKAGE) diff --git a/idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinBrowserFullyQualifiedProperty.kt b/idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinBrowserFullyQualifiedProperty.kt new file mode 100644 index 00000000000..a79c822736b --- /dev/null +++ b/idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinBrowserFullyQualifiedProperty.kt @@ -0,0 +1,10 @@ +// "Fix 'kotlin.browser' package usage" "true" +// JS + +package test + +fun use(a: Any) {} + +fun usage() { + use(kotlin.browser.localStorage.toString()) +} diff --git a/idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinBrowserFullyQualifiedProperty.kt.after b/idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinBrowserFullyQualifiedProperty.kt.after new file mode 100644 index 00000000000..1d43e869337 --- /dev/null +++ b/idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinBrowserFullyQualifiedProperty.kt.after @@ -0,0 +1,10 @@ +// "Fix 'kotlin.browser' package usage" "true" +// JS + +package test + +fun use(a: Any) {} + +fun usage() { + use(kotlinx.browser.localStorage.toString()) +} diff --git a/idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinDomAndBrowserImport.kt b/idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinDomAndBrowserImport.kt new file mode 100644 index 00000000000..81918b6e5f1 --- /dev/null +++ b/idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinDomAndBrowserImport.kt @@ -0,0 +1,11 @@ +// "Fix 'kotlin.dom' and 'kotlin.browser' packages usages in the project" "true" +// JS + +package test + +import kotlin.browser.localStorage +import kotlin.dom.addClass + +fun usage() { + kotlin.browser.document.toString() +} \ No newline at end of file diff --git a/idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinDomAndBrowserImport.kt.after b/idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinDomAndBrowserImport.kt.after new file mode 100644 index 00000000000..d72b1376956 --- /dev/null +++ b/idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinDomAndBrowserImport.kt.after @@ -0,0 +1,11 @@ +// "Fix 'kotlin.dom' and 'kotlin.browser' packages usages in the project" "true" +// JS + +package test + +import kotlinx.browser.localStorage +import kotlinx.dom.addClass + +fun usage() { + kotlinx.browser.document.toString() +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 6a39f5d6ed0..ca87d2fafd2 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -9898,6 +9898,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/obsoleteKotlinJsPackages"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true); } + @TestMetadata("kotlinBrowserFullyQualifiedProperty.kt") + public void testKotlinBrowserFullyQualifiedProperty() throws Exception { + runTest("idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinBrowserFullyQualifiedProperty.kt"); + } + @TestMetadata("kotlinBrowserPropertyImport.kt") public void testKotlinBrowserPropertyImport() throws Exception { runTest("idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinBrowserPropertyImport.kt"); @@ -9908,6 +9913,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { runTest("idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinBrowserStarImport.kt"); } + @TestMetadata("kotlinDomAndBrowserImport.kt") + public void testKotlinDomAndBrowserImport() throws Exception { + runTest("idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinDomAndBrowserImport.kt"); + } + @TestMetadata("kotlinDomPropertyImport.kt") public void testKotlinDomPropertyImport() throws Exception { runTest("idea/testData/quickfix/obsoleteKotlinJsPackages/kotlinDomPropertyImport.kt"); diff --git a/tests/mute-common.csv b/tests/mute-common.csv index 07263e90713..7cf54fa235e 100644 --- a/tests/mute-common.csv +++ b/tests/mute-common.csv @@ -45,6 +45,7 @@ org.jetbrains.kotlin.idea.quickfix.QuickFixMultiModuleTestGenerated.CreateActual org.jetbrains.kotlin.idea.quickfix.QuickFixTestGenerated.CreateFromUsage.CreateClass.CallExpression.testCallInWhenEntry, KT-35728,, org.jetbrains.kotlin.idea.quickfix.QuickFixTestGenerated.Libraries.testJunit, ERROR: Couldn't find the following libraries,, org.jetbrains.kotlin.idea.quickfix.QuickFixTestGenerated.Libraries.testTestNG, ERROR: Couldn't find the following libraries,, +org.jetbrains.kotlin.idea.quickfix.QuickFixTestGenerated.ObsoleteKotlinJsPackages.testKotlinDomAndBrowserImport, Always red because UAST does not work with JS,, org.jetbrains.kotlin.idea.refactoring.move.MultiModuleMoveTestGenerated.testMoveFromJvmModuleToJsModule_MoveFromJvmModuleToJsModule, KT-34106,, org.jetbrains.kotlin.idea.refactoring.move.MultiModuleMoveTestGenerated.testMoveJdkDependentToJsModule_MoveJdkDependentToJsModule, KT-34106,, org.jetbrains.kotlin.idea.resolve.PartialBodyResolveTestGenerated.testSmartCastInTheSameStatement, KT-35922,,