diff --git a/gradle.properties b/gradle.properties index f762f1cfc..1f26744ed 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,5 +2,5 @@ group=org.sonarsource.kotlin version=2.21-SNAPSHOT description=Code Analyzer for Kotlin projectTitle=Kotlin -kotlinVersion=2.0.20 +kotlinVersion=2.1.0-RC2 org.gradle.jvmargs=-Xmx4096M diff --git a/sonar-kotlin-api/build.gradle.kts b/sonar-kotlin-api/build.gradle.kts index 34b195a09..5f2e3757f 100644 --- a/sonar-kotlin-api/build.gradle.kts +++ b/sonar-kotlin-api/build.gradle.kts @@ -36,6 +36,7 @@ dependencies { implementation(libs.staxmate) implementation(libs.gson) implementation(libs.sonar.analyzer.commons.recognizers) + implementation("com.github.ben-manes.caffeine:caffeine:2.9.3") testImplementation(testLibs.junit.jupiter) testRuntimeOnly("org.junit.platform:junit-platform-launcher") diff --git a/sonar-kotlin-api/src/main/java/org/sonarsource/kotlin/api/frontend/K1.java b/sonar-kotlin-api/src/main/java/org/sonarsource/kotlin/api/frontend/K1.java new file mode 100644 index 000000000..95356feab --- /dev/null +++ b/sonar-kotlin-api/src/main/java/org/sonarsource/kotlin/api/frontend/K1.java @@ -0,0 +1,141 @@ +/* + * SonarSource Kotlin + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.kotlin.api.frontend; + + +import com.intellij.core.CoreApplicationEnvironment; +import com.intellij.mock.MockApplication; +import com.intellij.mock.MockProject; +import com.intellij.openapi.Disposable; +import com.intellij.psi.ClassTypePointerFactory; +import com.intellij.psi.impl.smartPointers.PsiClassReferenceTypePointerFactory; +import org.jetbrains.kotlin.analysis.api.descriptors.CliFe10AnalysisFacade; +import org.jetbrains.kotlin.analysis.api.descriptors.Fe10AnalysisFacade; +import org.jetbrains.kotlin.analysis.api.descriptors.KaFe10AnalysisHandlerExtension; +import org.jetbrains.kotlin.analysis.api.platform.lifetime.KotlinAlwaysAccessibleLifetimeTokenFactory; +import org.jetbrains.kotlin.analysis.api.platform.lifetime.KotlinLifetimeTokenFactory; +import org.jetbrains.kotlin.analysis.api.platform.modification.KotlinGlobalModificationService; +import org.jetbrains.kotlin.analysis.api.platform.modification.KotlinModificationTrackerFactory; +import org.jetbrains.kotlin.analysis.api.platform.projectStructure.KotlinByModulesResolutionScopeProvider; +import org.jetbrains.kotlin.analysis.api.platform.projectStructure.KotlinProjectStructureProvider; +import org.jetbrains.kotlin.analysis.api.platform.projectStructure.KotlinResolutionScopeProvider; +import org.jetbrains.kotlin.analysis.api.standalone.base.modification.KotlinStandaloneGlobalModificationService; +import org.jetbrains.kotlin.analysis.api.standalone.base.modification.KotlinStandaloneModificationTrackerFactory; +import org.jetbrains.kotlin.analysis.api.standalone.base.projectStructure.AnalysisApiSimpleServiceRegistrar; +import org.jetbrains.kotlin.analysis.api.standalone.base.projectStructure.PluginStructureProvider; +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment; +import org.jetbrains.kotlin.references.fe10.base.DummyKtFe10ReferenceResolutionHelper; +import org.jetbrains.kotlin.references.fe10.base.KtFe10ReferenceResolutionHelper; +import org.jetbrains.kotlin.resolve.extensions.AnalysisHandlerExtension; + +import java.util.Collections; + +@SuppressWarnings("ALL") +public class K1 { + + private K1() { + // Utility class + } + + public static void configureK1AnalysisApiServices(KotlinCoreEnvironment env) { + MockApplication application = env.getProjectEnvironment().getEnvironment().getApplication(); + + if (application.getServiceIfCreated(KtFe10ReferenceResolutionHelper.class) == null) { + AnalysisApiFe10ServiceRegistrar.INSTANCE.registerApplicationServices(application); + } + final var project = env.getProjectEnvironment().getProject(); + AnalysisApiFe10ServiceRegistrar.INSTANCE.registerProjectServices(project); + AnalysisApiFe10ServiceRegistrar.INSTANCE.registerProjectModelServices( + project, + env.getProjectEnvironment().getParentDisposable() + ); + + project.registerService( + KotlinModificationTrackerFactory.class, + KotlinStandaloneModificationTrackerFactory.class + ); + project.registerService( + KotlinGlobalModificationService.class, + KotlinStandaloneGlobalModificationService.class + ); + project.registerService( + KotlinLifetimeTokenFactory.class, + KotlinAlwaysAccessibleLifetimeTokenFactory.class + ); + project.registerService( + KotlinResolutionScopeProvider.class, + KotlinByModulesResolutionScopeProvider.class + ); + project.registerService( + KotlinProjectStructureProvider.class, + KtModuleProviderByCompilerConfiguration.build( + env.getProjectEnvironment(), + env.getConfiguration(), + Collections.emptyList() + ) + ); + } + + private static class AnalysisApiFe10ServiceRegistrar extends AnalysisApiSimpleServiceRegistrar { + public static final AnalysisApiSimpleServiceRegistrar INSTANCE = new AnalysisApiFe10ServiceRegistrar(); + private static final String PLUGIN_RELATIVE_PATH = "/META-INF/analysis-api/analysis-api-fe10.xml"; + + private AnalysisApiFe10ServiceRegistrar() { + } + + @Override + public void registerApplicationServices(MockApplication application) { + PluginStructureProvider.INSTANCE.registerApplicationServices(application, PLUGIN_RELATIVE_PATH); + application.registerService( + KtFe10ReferenceResolutionHelper.class, + DummyKtFe10ReferenceResolutionHelper.INSTANCE + ); + + final var applicationArea = application.getExtensionArea(); + if (!applicationArea.hasExtensionPoint(ClassTypePointerFactory.EP_NAME)) { + CoreApplicationEnvironment.registerApplicationExtensionPoint( + ClassTypePointerFactory.EP_NAME, + ClassTypePointerFactory.class + ); + applicationArea + .getExtensionPoint(ClassTypePointerFactory.EP_NAME) + .registerExtension(new PsiClassReferenceTypePointerFactory(), application); + } + } + + @Override + public void registerProjectExtensionPoints(MockProject project) { + AnalysisHandlerExtension.Companion.registerExtensionPoint(project); + PluginStructureProvider.INSTANCE.registerProjectExtensionPoints(project, PLUGIN_RELATIVE_PATH); + } + + @Override + public void registerProjectServices(MockProject project) { + PluginStructureProvider.INSTANCE.registerProjectServices(project, PLUGIN_RELATIVE_PATH); + PluginStructureProvider.INSTANCE.registerProjectListeners(project, PLUGIN_RELATIVE_PATH); + } + + @Override + public void registerProjectModelServices(MockProject project, Disposable disposable) { + project.registerService(Fe10AnalysisFacade.class, new CliFe10AnalysisFacade()); + AnalysisHandlerExtension.Companion.registerExtension(project, new KaFe10AnalysisHandlerExtension()); + } + } +} diff --git a/sonar-kotlin-api/src/main/java/org/sonarsource/kotlin/api/frontend/K1.kt b/sonar-kotlin-api/src/main/java/org/sonarsource/kotlin/api/frontend/K1.kt deleted file mode 100644 index a9edce366..000000000 --- a/sonar-kotlin-api/src/main/java/org/sonarsource/kotlin/api/frontend/K1.kt +++ /dev/null @@ -1,125 +0,0 @@ -/* - * SonarSource Kotlin - * Copyright (C) 2018-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonarsource.kotlin.api.frontend - -import com.intellij.core.CoreApplicationEnvironment -import com.intellij.mock.MockApplication -import com.intellij.mock.MockProject -import com.intellij.openapi.Disposable -import com.intellij.psi.ClassTypePointerFactory -import com.intellij.psi.impl.smartPointers.PsiClassReferenceTypePointerFactory -import org.jetbrains.kotlin.analysis.api.KaAnalysisNonPublicApi -import org.jetbrains.kotlin.analysis.api.descriptors.CliFe10AnalysisFacade -import org.jetbrains.kotlin.analysis.api.descriptors.Fe10AnalysisFacade -import org.jetbrains.kotlin.analysis.api.descriptors.KaFe10AnalysisHandlerExtension -import org.jetbrains.kotlin.analysis.api.platform.lifetime.KotlinAlwaysAccessibleLifetimeTokenProvider -import org.jetbrains.kotlin.analysis.api.platform.lifetime.KotlinLifetimeTokenProvider -import org.jetbrains.kotlin.analysis.api.platform.modification.KotlinGlobalModificationService -import org.jetbrains.kotlin.analysis.api.platform.modification.KotlinModificationTrackerFactory -import org.jetbrains.kotlin.analysis.api.platform.projectStructure.KotlinByModulesResolutionScopeProvider -import org.jetbrains.kotlin.analysis.api.platform.projectStructure.KotlinProjectStructureProvider -import org.jetbrains.kotlin.analysis.api.platform.projectStructure.KotlinResolutionScopeProvider -import org.jetbrains.kotlin.analysis.api.standalone.base.modification.KotlinStandaloneGlobalModificationService -import org.jetbrains.kotlin.analysis.api.standalone.base.modification.KotlinStandaloneModificationTrackerFactory -import org.jetbrains.kotlin.analysis.api.standalone.base.projectStructure.AnalysisApiSimpleServiceRegistrar -import org.jetbrains.kotlin.analysis.api.standalone.base.projectStructure.PluginStructureProvider -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.references.fe10.base.DummyKtFe10ReferenceResolutionHelper -import org.jetbrains.kotlin.references.fe10.base.KtFe10ReferenceResolutionHelper -import org.jetbrains.kotlin.resolve.extensions.AnalysisHandlerExtension - -@Deprecated("") -annotation class K1only(val comments: String = "") - -internal fun configureK1AnalysisApiServices(env: KotlinCoreEnvironment) { - val application = env.projectEnvironment.environment.application - if (application.getServiceIfCreated(KtFe10ReferenceResolutionHelper::class.java) == null) { - AnalysisApiFe10ServiceRegistrar.registerApplicationServices(application) - } - val project = env.projectEnvironment.project - AnalysisApiFe10ServiceRegistrar.registerProjectServices(project) - AnalysisApiFe10ServiceRegistrar.registerProjectModelServices( - project, - env.projectEnvironment.parentDisposable - ) - - project.registerService( - KotlinModificationTrackerFactory::class.java, - KotlinStandaloneModificationTrackerFactory::class.java, - ) - project.registerService( - KotlinGlobalModificationService::class.java, - KotlinStandaloneGlobalModificationService::class.java, - ) - project.registerService( - KotlinLifetimeTokenProvider::class.java, - KotlinAlwaysAccessibleLifetimeTokenProvider::class.java, - ) - project.registerService( - KotlinResolutionScopeProvider::class.java, - KotlinByModulesResolutionScopeProvider::class.java, - ); - project.registerService( - KotlinProjectStructureProvider::class.java, - KtModuleProviderByCompilerConfiguration.build( - env.projectEnvironment, - env.configuration, - listOf() - ) - ) -} - -@OptIn(KaAnalysisNonPublicApi::class) -private object AnalysisApiFe10ServiceRegistrar : AnalysisApiSimpleServiceRegistrar() { - private const val PLUGIN_RELATIVE_PATH = "/META-INF/analysis-api/analysis-api-fe10.xml" - - override fun registerApplicationServices(application: MockApplication) { - PluginStructureProvider.registerApplicationServices(application, PLUGIN_RELATIVE_PATH) - application.registerService( - KtFe10ReferenceResolutionHelper::class.java, - DummyKtFe10ReferenceResolutionHelper, - ) - val applicationArea = application.extensionArea - if (!applicationArea.hasExtensionPoint(ClassTypePointerFactory.EP_NAME)) { - CoreApplicationEnvironment.registerApplicationExtensionPoint( - ClassTypePointerFactory.EP_NAME, - ClassTypePointerFactory::class.java, - ) - applicationArea - .getExtensionPoint(ClassTypePointerFactory.EP_NAME) - .registerExtension(PsiClassReferenceTypePointerFactory(), application) - } - } - - override fun registerProjectExtensionPoints(project: MockProject) { - AnalysisHandlerExtension.registerExtensionPoint(project) - PluginStructureProvider.registerProjectExtensionPoints(project, PLUGIN_RELATIVE_PATH) - } - - override fun registerProjectServices(project: MockProject) { - PluginStructureProvider.registerProjectServices(project, PLUGIN_RELATIVE_PATH) - PluginStructureProvider.registerProjectListeners(project, PLUGIN_RELATIVE_PATH) - } - - override fun registerProjectModelServices(project: MockProject, disposable: Disposable) { - project.apply { registerService(Fe10AnalysisFacade::class.java, CliFe10AnalysisFacade()) } - AnalysisHandlerExtension.registerExtension(project, KaFe10AnalysisHandlerExtension()) - } -} diff --git a/sonar-kotlin-api/src/main/java/org/sonarsource/kotlin/api/frontend/K1only.kt b/sonar-kotlin-api/src/main/java/org/sonarsource/kotlin/api/frontend/K1only.kt new file mode 100644 index 000000000..86964ccec --- /dev/null +++ b/sonar-kotlin-api/src/main/java/org/sonarsource/kotlin/api/frontend/K1only.kt @@ -0,0 +1,23 @@ +/* + * SonarSource Kotlin + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.kotlin.api.frontend + +@Deprecated("") +annotation class K1only(val comments: String = "") diff --git a/sonar-kotlin-api/src/main/java/org/sonarsource/kotlin/api/frontend/K2.kt b/sonar-kotlin-api/src/main/java/org/sonarsource/kotlin/api/frontend/K2.kt index 9f51da1b1..ca05836d2 100644 --- a/sonar-kotlin-api/src/main/java/org/sonarsource/kotlin/api/frontend/K2.kt +++ b/sonar-kotlin-api/src/main/java/org/sonarsource/kotlin/api/frontend/K2.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSdkModule import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSourceModule import org.jetbrains.kotlin.cli.common.CliModuleVisibilityManagerImpl import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots +import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.JVMConfigurationKeys import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager @@ -54,13 +55,15 @@ fun createK2AnalysisSession( // https://github.com/JetBrains/kotlin/blob/a9ff22693479cabd201909a06e6764c00eddbf7b/analysis/analysis-api-fe10/tests/org/jetbrains/kotlin/analysis/api/fe10/test/configurator/AnalysisApiFe10TestServiceRegistrar.kt#L49 registerProjectService(ModuleVisibilityManager::class.java, CliModuleVisibilityManagerImpl(enabled = true)) - // TODO language version, jvm target, etc val platform = JvmPlatforms.defaultJvmPlatform buildKtModuleProvider { this.platform = platform addModule(buildKtSourceModule { this.platform = platform moduleName = "module" + compilerConfiguration[CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS]?.let { + languageVersionSettings = it + } addSourceVirtualFiles(virtualFiles) addRegularDependency(buildKtLibraryModule { this.platform = platform diff --git a/sonar-kotlin-api/src/main/java/org/sonarsource/kotlin/api/frontend/KotlinCoreEnvironmentTools.kt b/sonar-kotlin-api/src/main/java/org/sonarsource/kotlin/api/frontend/KotlinCoreEnvironmentTools.kt index 7f076c7ac..8378c116b 100644 --- a/sonar-kotlin-api/src/main/java/org/sonarsource/kotlin/api/frontend/KotlinCoreEnvironmentTools.kt +++ b/sonar-kotlin-api/src/main/java/org/sonarsource/kotlin/api/frontend/KotlinCoreEnvironmentTools.kt @@ -47,6 +47,7 @@ import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory import org.jetbrains.kotlin.util.slicedMap.WritableSlice +import org.sonarsource.kotlin.api.frontend.K1.configureK1AnalysisApiServices import java.io.File /** diff --git a/sonar-kotlin-api/src/test/java/org/sonarsource/kotlin/api/frontend/K2Test.kt b/sonar-kotlin-api/src/test/java/org/sonarsource/kotlin/api/frontend/K2Test.kt index 651307775..eb916c58d 100644 --- a/sonar-kotlin-api/src/test/java/org/sonarsource/kotlin/api/frontend/K2Test.kt +++ b/sonar-kotlin-api/src/test/java/org/sonarsource/kotlin/api/frontend/K2Test.kt @@ -21,21 +21,142 @@ package org.sonarsource.kotlin.api.frontend import com.intellij.openapi.util.Disposer import com.intellij.psi.util.descendantsOfType +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.jetbrains.kotlin.analysis.api.components.KaDiagnosticCheckerFilter +import org.jetbrains.kotlin.analysis.api.diagnostics.KaSeverity +import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol +import org.jetbrains.kotlin.analysis.api.types.symbol import org.jetbrains.kotlin.config.JvmTarget import org.jetbrains.kotlin.config.LanguageVersion -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.diagnostics.Severity +import org.jetbrains.kotlin.psi.* +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import java.io.File +private const val TYPE_RESOLUTION_SAMPLE = """ +package org.example + +class Cat { + fun purr() { + println("Purr purr") + } +} + +fun petAnimal(animal: Any) { + val isCat = animal is Cat + if (isCat) { + animal.purr() // doesn't work in 1.9 + } +} + +fun main() { + val kitty = Cat() + petAnimal(kitty) +} """ + class K2Test { @Test - fun test() { + fun `test with Kotlin 1_9 resolves to Any`() { + analyzeContent(LanguageVersion.KOTLIN_1_9, TYPE_RESOLUTION_SAMPLE) { + org.jetbrains.kotlin.analysis.api.analyze(this) { + val type = ((((declarations[1] as KtNamedFunction).bodyBlockExpression + ?.statements!![1] as KtIfExpression).then as KtBlockExpression) + .statements[0] as KtDotQualifiedExpression) + .receiverExpression.expressionType + assertThat(type?.isAnyType).isTrue() + } + } + } + + @Test + fun `test with Kotlin 2+ resolves to correct type Cat`() { + analyzeContent(LanguageVersion.LATEST_STABLE, TYPE_RESOLUTION_SAMPLE) { + org.jetbrains.kotlin.analysis.api.analyze(this) { + val type = ((((declarations[1] as KtNamedFunction).bodyBlockExpression + ?.statements!![1] as KtIfExpression).then as KtBlockExpression) + .statements[0] as KtDotQualifiedExpression) + .receiverExpression.expressionType + assertThat(type?.symbol?.classId?.asFqNameString()).isEqualTo("org.example.Cat") + } + } + } + + @Test + fun `test with Kotlin 1_7+ non-exhaustive 'when' is an Error`() { + analyzeContent(LanguageVersion.LATEST_STABLE, """ + fun f(s: String){ + when(s.isEmpty()) { + true -> println(s) + } + } + """ + ) { + org.jetbrains.kotlin.analysis.api.analyze(this) { + assertThat(collectDiagnostics(KaDiagnosticCheckerFilter.EXTENDED_AND_COMMON_CHECKERS) + .first().severity).isEqualTo(KaSeverity.ERROR) + } + } + } + + @Test + fun `test with Kotlin 1_6 non-exhaustive 'when' is a Warning`() { + analyzeContent(LanguageVersion.KOTLIN_1_6, """ + fun f(s: String){ + when(s.isEmpty()) { + true -> println(s) + } + } + """ + ) { + org.jetbrains.kotlin.analysis.api.analyze(this) { + assertThat(collectDiagnostics(KaDiagnosticCheckerFilter.EXTENDED_AND_COMMON_CHECKERS) + .first().severity).isEqualTo(KaSeverity.WARNING) + } + } + } + + @Test + fun `test with Kotlin 1_5 non-exhaustive 'when' is a Warning as 1_5 API is unsupported`() { + analyzeContent(LanguageVersion.KOTLIN_1_5, """ + fun f(s: String){ + when(s.isEmpty()) { + true -> println(s) + } + } + """ + ) { + org.jetbrains.kotlin.analysis.api.analyze(this) { + assertThat(collectDiagnostics(KaDiagnosticCheckerFilter.EXTENDED_AND_COMMON_CHECKERS) + .first().severity).isEqualTo(KaSeverity.WARNING) + } + } + } + + @Test + fun `test Kotlin 1_4 API is unsupported`() { + analyzeContent(LanguageVersion.KOTLIN_1_4, """ + sealed interface MyInterface + """ + ) { + org.jetbrains.kotlin.analysis.api.analyze(this) { + assertThat(collectDiagnostics(KaDiagnosticCheckerFilter.EXTENDED_AND_COMMON_CHECKERS)) + .isEmpty() + } + } + } + + private fun analyzeContent( + version: LanguageVersion, + content: String, + assertion: KtFile.() -> Unit + ) { val disposable = Disposer.newDisposable() val compilerConfiguration = compilerConfiguration( System.getProperty("java.class.path").split(File.pathSeparatorChar), - LanguageVersion.LATEST_STABLE, + version, JvmTarget.JVM_1_8, 0 ) @@ -44,19 +165,14 @@ class K2Test { compilerConfiguration, listOf( KotlinVirtualFile( - KotlinFileSystem(), File("/fake.kt"), """ - fun main() { - return 0 - } - """.trimIndent() + KotlinFileSystem(), + File("/fake.kt"), content.trimIndent(), ) ), ) val ktFile: KtFile = analysisSession.modulesWithFiles.entries.first().value[0] as KtFile org.jetbrains.kotlin.analysis.api.analyze(ktFile) { - ktFile.descendantsOfType().forEach { - println(it.expressionType) - } + ktFile.assertion() } Disposer.dispose(disposable) } diff --git a/sonar-kotlin-checks/src/main/java/org/sonarsource/kotlin/checks/UnnecessaryImportsCheck.kt b/sonar-kotlin-checks/src/main/java/org/sonarsource/kotlin/checks/UnnecessaryImportsCheck.kt index e7a816ec1..ac9d927bf 100644 --- a/sonar-kotlin-checks/src/main/java/org/sonarsource/kotlin/checks/UnnecessaryImportsCheck.kt +++ b/sonar-kotlin-checks/src/main/java/org/sonarsource/kotlin/checks/UnnecessaryImportsCheck.kt @@ -20,27 +20,13 @@ package org.sonarsource.kotlin.checks import org.jetbrains.kotlin.analysis.api.KaIdeApi -import org.jetbrains.kotlin.analysis.api.descriptors.KaFe10Session +import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.descriptors.VariableAccessorDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors import org.jetbrains.kotlin.descriptors.accessors import org.jetbrains.kotlin.kdoc.psi.impl.KDocLink import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.KtArrayAccessExpression -import org.jetbrains.kotlin.psi.KtCallExpression -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.psi.KtImportDirective -import org.jetbrains.kotlin.psi.KtImportList -import org.jetbrains.kotlin.psi.KtOperationReferenceExpression -import org.jetbrains.kotlin.psi.KtPackageDirective -import org.jetbrains.kotlin.psi.KtPrefixExpression -import org.jetbrains.kotlin.psi.KtProperty -import org.jetbrains.kotlin.psi.KtPropertyDelegate -import org.jetbrains.kotlin.psi.KtReferenceExpression -import org.jetbrains.kotlin.psi.KtSimpleNameExpression -import org.jetbrains.kotlin.psi.KtTreeVisitorVoid -import org.jetbrains.kotlin.psi.KtUserType +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi2ir.deparenthesize @@ -55,6 +41,7 @@ import org.sonarsource.kotlin.api.checks.AbstractCheck import org.sonarsource.kotlin.api.frontend.KotlinFileContext import org.sonarsource.kotlin.api.visiting.analyze +private const val KA_FIR_SESSION = "org.jetbrains.kotlin.analysis.api.fir.KaFirSession" private const val MESSAGE_UNUSED = "Remove this unused import." private const val MESSAGE_REDUNDANT = "Remove this redundant import." private val DELEGATES_IMPORTED_NAMES = setOf("getValue", "setValue", "provideDelegate") @@ -68,7 +55,7 @@ class UnnecessaryImportsCheck : AbstractCheck() { override fun visitKtFile(file: KtFile, context: KotlinFileContext) { analyze { - if (this !is KaFe10Session) { + if (isK2()) { val analyzeImportsToOptimize = analyzeImportsToOptimize(file) file.importDirectives.mapNotNull { import -> import.importedFqName?.let { import to it } } @@ -285,3 +272,6 @@ private fun KtReferenceExpression.importableSimpleName() = is KtSimpleNameExpression -> getReferencedName() else -> null } + +fun KaSession.isK2(): Boolean = + KA_FIR_SESSION == javaClass.name diff --git a/sonar-kotlin-plugin/src/main/java/org/sonarsource/kotlin/plugin/linking/WorkaroundForJarMinimization.java b/sonar-kotlin-plugin/src/main/java/org/sonarsource/kotlin/plugin/linking/WorkaroundForJarMinimization.java index e875f0d38..542707e4f 100644 --- a/sonar-kotlin-plugin/src/main/java/org/sonarsource/kotlin/plugin/linking/WorkaroundForJarMinimization.java +++ b/sonar-kotlin-plugin/src/main/java/org/sonarsource/kotlin/plugin/linking/WorkaroundForJarMinimization.java @@ -49,6 +49,8 @@ public class WorkaroundForJarMinimization { org.jetbrains.kotlin.analysis.api.descriptors.KaFe10SessionProvider.class, org.jetbrains.kotlin.references.fe10.base.KtFe10KotlinReferenceProviderContributor.class, org.jetbrains.kotlin.analysis.api.descriptors.references.ReadWriteAccessCheckerDescriptorsImpl.class, + org.jetbrains.kotlin.analysis.api.descriptors.modification.KaFe10SourceModificationService.class, + org.jetbrains.kotlin.analysis.api.fir.KaFirSession.class, /** Used to have proper named groups behavior in regular expressions */ kotlin.internal.jdk8.JDK8PlatformImplementations.class diff --git a/sonar-kotlin-plugin/src/test/java/org/sonarsource/kotlin/plugin/linking/WorkaroundForJarMinimizationTest.kt b/sonar-kotlin-plugin/src/test/java/org/sonarsource/kotlin/plugin/linking/WorkaroundForJarMinimizationTest.kt index c83b5b26f..5c0d10722 100644 --- a/sonar-kotlin-plugin/src/test/java/org/sonarsource/kotlin/plugin/linking/WorkaroundForJarMinimizationTest.kt +++ b/sonar-kotlin-plugin/src/test/java/org/sonarsource/kotlin/plugin/linking/WorkaroundForJarMinimizationTest.kt @@ -25,6 +25,6 @@ import org.junit.jupiter.api.Test internal class WorkaroundForJarMinimizationTest { @Test fun minimizing_class_count() { - assertThat(WorkaroundForJarMinimization.CLASSES_TO_KEEP_WHEN_MINIMIZING_JAR).hasSize(16) + assertThat(WorkaroundForJarMinimization.CLASSES_TO_KEEP_WHEN_MINIMIZING_JAR).hasSize(18) } }