diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties index c1646c43e85..5520aee3357 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties @@ -441,6 +441,9 @@ replace.by.reconstructed.type=Replace by ''{0}'' suppress.warnings.family=Suppress Warnings suppress.warning.for=Suppress ''{0}'' for {1} {2} +reflection.not.found=Reflection not found in the classpath +add.reflection.to.classpath=Add kotlin-reflect.jar to the classpath + # Kotlin Compiler Settings Tab #Common diff --git a/idea/resources/inspectionDescriptions/ReflectionNotFound.html b/idea/resources/inspectionDescriptions/ReflectionNotFound.html new file mode 100644 index 00000000000..10b74f40c98 --- /dev/null +++ b/idea/resources/inspectionDescriptions/ReflectionNotFound.html @@ -0,0 +1,5 @@ + + +This inspection reports expressions which use reflection features in the module which doesn't have a dependency on kotlin-reflect.jar. + + diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index a20598ccd35..2d34fbbb9d7 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -868,6 +868,13 @@ enabledByDefault="true" level="WARNING" /> + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ReflectionNotFoundInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ReflectionNotFoundInspection.kt new file mode 100644 index 00000000000..31a6afece84 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ReflectionNotFoundInspection.kt @@ -0,0 +1,118 @@ +/* + * 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.LocalQuickFix +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.openapi.extensions.Extensions +import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.OrderRootType +import com.intellij.openapi.vfs.VfsUtil +import com.intellij.openapi.vfs.VfsUtilCore +import com.intellij.psi.PsiElementVisitor +import org.jetbrains.kotlin.idea.JetBundle +import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor +import org.jetbrains.kotlin.idea.configuration.ConfigureKotlinInProjectUtils +import org.jetbrains.kotlin.idea.configuration.KotlinJavaModuleConfigurator +import org.jetbrains.kotlin.idea.configuration.KotlinProjectConfigurator +import org.jetbrains.kotlin.idea.framework.JavaRuntimePresentationProvider +import org.jetbrains.kotlin.idea.util.ProjectRootsUtil +import org.jetbrains.kotlin.idea.versions.KotlinRuntimeLibraryUtil +import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.psi.JetDoubleColonExpression +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.psi.JetVisitorVoid +import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies +import org.jetbrains.kotlin.utils.PathUtil +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull +import org.jetbrains.kotlin.utils.singletonOrEmptyList +import java.io.File + +public class ReflectionNotFoundInspection : AbstractKotlinInspection() { + override fun runForWholeFile() = true + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { + val file = holder.getFile() + val reportProblem = + file is JetFile && + ProjectRootsUtil.isInProjectSource(file) && + file.findModuleDescriptor().findClassAcrossModuleDependencies(JvmAbi.REFLECTION_FACTORY_IMPL) == null + + return object : JetVisitorVoid() { + private fun createQuickFix(): LocalQuickFix? { + val pluginReflectJar = PathUtil.getKotlinPathsForIdeaPlugin().getReflectPath() + if (pluginReflectJar.exists()) { + val configurator = + Extensions.getExtensions(KotlinProjectConfigurator.EP_NAME).firstIsInstanceOrNull() + + if (configurator != null) { + return AddReflectJarQuickFix(configurator, pluginReflectJar) + } + } + + return null + } + + override fun visitDoubleColonExpression(expression: JetDoubleColonExpression) { + if (reportProblem) { + holder.registerProblem( + expression.getDoubleColonTokenReference(), + JetBundle.message("reflection.not.found"), + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + *(createQuickFix().singletonOrEmptyList().copyToArray()) + ) + } + } + } + } + + class AddReflectJarQuickFix( + val configurator: KotlinJavaModuleConfigurator, + val pluginReflectJar: File + ) : LocalQuickFix { + override fun getName() = JetBundle.message("add.reflection.to.classpath") + + override fun getFamilyName() = getName() + + override fun applyFix(project: Project, descriptor: ProblemDescriptor?) { + for (library in KotlinRuntimeLibraryUtil.findKotlinLibraries(project)) { + val runtimeJar = JavaRuntimePresentationProvider.getRuntimeJar(library) ?: continue + if (JavaRuntimePresentationProvider.getReflectJar(library) != null) continue + + val model = library.getModifiableModel() + + val libFilesDir = VfsUtilCore.virtualToIoFile(runtimeJar).getParent() + val reflectIoFile = File(libFilesDir, PathUtil.KOTLIN_JAVA_REFLECT_JAR) + if (reflectIoFile.exists()) { + model.addRoot(VfsUtil.getUrlForLibraryRoot(reflectIoFile), OrderRootType.CLASSES) + } + else { + val copied = configurator.copyFileToDir(pluginReflectJar, libFilesDir)!! + model.addRoot(VfsUtil.getUrlForLibraryRoot(copied), OrderRootType.CLASSES) + } + + model.commit() + + ConfigureKotlinInProjectUtils.showInfoNotification( + "${PathUtil.KOTLIN_JAVA_REFLECT_JAR} was added to the library ${library.getName()}" + ) + } + } + } +}