Make "reflection not found" a warning, provide a quick fix

Reporting the warning on each "::", as ReflectionNotFoundInspection did, is not
correct anymore, because for example name/get/set on properties works perfectly
without kotlin-reflect.jar in the classpath. So instead we report the warning
on calls to functions from reflection interfaces. This is not perfect either
because it's wrong in projects with custom implementations of reflection
interfaces, but this case is so rare that the users can suppress the warning
there anyway

 #KT-7176 Fixed
This commit is contained in:
Alexander Udalov
2015-07-02 17:39:16 +03:00
parent 4f1247f03f
commit 636b63a8c5
24 changed files with 240 additions and 261 deletions
@@ -70,7 +70,6 @@ import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluat
import org.jetbrains.kotlin.resolve.constants.evaluate.EvaluatePackage;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage;
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
@@ -103,7 +102,6 @@ import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage.
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.DiagnosticsPackage.OtherOrigin;
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.DiagnosticsPackage.TraitImpl;
import static org.jetbrains.kotlin.serialization.deserialization.DeserializationPackage.findClassAcrossModuleDependencies;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implements LocalLookup {
@@ -2733,8 +2731,6 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
@Override
public StackValue visitClassLiteralExpression(@NotNull JetClassLiteralExpression expression, StackValue data) {
checkReflectionIsAvailable(expression);
JetType type = bindingContext.getType(expression);
assert type != null;
@@ -2754,9 +2750,6 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
(FunctionDescriptor) resolvedCall.getResultingDescriptor());
}
// TODO: this diagnostic should also be reported on function references once they obtain reflection
checkReflectionIsAvailable(expression);
VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, expression);
if (variableDescriptor != null) {
return generatePropertyReference(expression, variableDescriptor, resolvedCall);
@@ -2789,12 +2782,6 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
return codegen.putInstanceOnStack();
}
private void checkReflectionIsAvailable(@NotNull JetExpression expression) {
if (findClassAcrossModuleDependencies(state.getModule(), JvmAbi.REFLECTION_FACTORY_IMPL) == null) {
state.getDiagnostics().report(ErrorsJvm.NO_REFLECTION_IN_CLASS_PATH.on(expression, expression));
}
}
@NotNull
public static StackValue generateClassLiteralReference(@NotNull final JetTypeMapper typeMapper, @NotNull final JetType type) {
return StackValue.operation(K_CLASS_TYPE, new Function1<InstructionAdapter, Unit>() {
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability
import org.jetbrains.kotlin.resolve.jvm.calls.checkers.NeedSyntheticChecker
import org.jetbrains.kotlin.resolve.jvm.calls.checkers.ReflectionAPICallChecker
import org.jetbrains.kotlin.resolve.jvm.calls.checkers.TraitDefaultMethodCallChecker
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.NullabilityInformationSource
@@ -59,7 +60,8 @@ public class KotlinJvmCheckerProvider(private val module: ModuleDescriptor) : Ad
PublicFieldAnnotationChecker()),
additionalCallCheckers = listOf(NeedSyntheticChecker(), JavaAnnotationCallChecker(),
JavaAnnotationMethodCallChecker(), TraitDefaultMethodCallChecker()),
JavaAnnotationMethodCallChecker(), TraitDefaultMethodCallChecker(),
ReflectionAPICallChecker(module)),
additionalTypeCheckers = listOf(JavaNullabilityWarningsChecker()),
additionalSymbolUsageValidators = listOf()
@@ -0,0 +1,70 @@
/*
* 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.resolve.jvm.calls.checkers
import org.jetbrains.kotlin.builtins.KOTLIN_REFLECT_FQ_NAME
import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.NO_REFLECTION_IN_CLASS_PATH
import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import kotlin.reflect.jvm.java
/**
* If there's no Kotlin reflection implementation found in the classpath, checks that there are no usages
* of reflection API which will fail at runtime.
*/
class ReflectionAPICallChecker(private val module: ModuleDescriptor) : CallChecker {
private val isReflectionAvailable by lazy {
module.findClassAcrossModuleDependencies(JvmAbi.REFLECTION_FACTORY_IMPL) != null
}
private val kPropertyClasses by lazy {
val reflectionTypes = ReflectionTypes(module)
setOf(reflectionTypes.kProperty0, reflectionTypes.kProperty1, reflectionTypes.kProperty2)
}
override fun <F : CallableDescriptor> check(resolvedCall: ResolvedCall<F>, context: BasicCallResolutionContext) {
if (isReflectionAvailable) return
val descriptor = resolvedCall.getResultingDescriptor()
val containingClass = descriptor.getContainingDeclaration() as? ClassDescriptor ?: return
if (!ReflectionTypes.isReflectionClass(containingClass)) return
// Skip some symbols which are supposed to work fine without kotlin-reflect.jar:
// - 'name' on anything
// - 'invoke' on functions (or on anything else for that matter)
// - 'get'/'set' on properties
val name = descriptor.getName()
when {
name == OperatorConventions.INVOKE -> return
name.asString() == "name" -> return
(name.asString() == "get" || name.asString() == "set") &&
kPropertyClasses.any { kProperty -> DescriptorUtils.isSubclass(containingClass, kProperty) } -> return
}
context.trace.report(NO_REFLECTION_IN_CLASS_PATH.on(resolvedCall.getCall().getCallElement()))
}
}
@@ -59,8 +59,8 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
MAP.put(ErrorsJvm.POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION, "Only named arguments are available for Java annotations");
MAP.put(ErrorsJvm.DEPRECATED_ANNOTATION_METHOD_CALL, "Annotation methods are deprecated. Use property instead");
MAP.put(ErrorsJvm.NO_REFLECTION_IN_CLASS_PATH, "Expression ''{0}'' uses reflection which is not found in compilation classpath. " +
"Make sure you have kotlin-reflect.jar in the classpath", Renderers.ELEMENT_TEXT);
MAP.put(ErrorsJvm.NO_REFLECTION_IN_CLASS_PATH, "Call uses reflection API which is not found in compilation classpath. " +
"Make sure you have kotlin-reflect.jar in the classpath");
MAP.put(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS,
"Expected type does not accept nulls in {0}, but the value may be null in {1}", Renderers.TO_STRING, Renderers.TO_STRING);
@@ -58,8 +58,7 @@ public interface ErrorsJvm {
DiagnosticFactory0<JetElement> INAPPLICABLE_PUBLIC_FIELD = DiagnosticFactory0.create(ERROR);
// TODO: make this a warning
DiagnosticFactory1<JetExpression, JetExpression> NO_REFLECTION_IN_CLASS_PATH = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<JetElement> NO_REFLECTION_IN_CLASS_PATH = DiagnosticFactory0.create(WARNING);
enum NullabilityInformationSource {
KOTLIN {
@@ -1,3 +0,0 @@
$TESTDATA_DIR$/noReflectionInClasspath.kt
-d
$TEMP_DIR$
-4
View File
@@ -1,4 +0,0 @@
class Foo(val prop: Any)
fun t1() = Foo::prop
fun t2() = Foo::class
-7
View File
@@ -1,7 +0,0 @@
compiler/testData/cli/jvm/noReflectionInClasspath.kt:3:12: error: expression 'Foo::prop' uses reflection which is not found in compilation classpath. Make sure you have kotlin-reflect.jar in the classpath
fun t1() = Foo::prop
^
compiler/testData/cli/jvm/noReflectionInClasspath.kt:4:12: error: expression 'Foo::class' uses reflection which is not found in compilation classpath. Make sure you have kotlin-reflect.jar in the classpath
fun t2() = Foo::class
^
COMPILATION_ERROR
@@ -0,0 +1,21 @@
import kotlin.reflect.*
class Foo(val prop: Any) {
fun func() {}
}
fun n01() = Foo::prop
fun n02() = Foo::func
fun n03() = Foo::class
fun n04(p: KProperty0<Int>) = p.get()
fun n05(p: KMutableProperty0<String>) = p.set("")
fun n06(p: KProperty0<Int>) = p.get()
fun n07(p: KFunction<String>) = p.name
fun n08(p: KProperty1<String, Int>) = p[""]
fun n09(p: KProperty2<String, String, Int>) = p["", ""]
fun n10() = (Foo::func).invoke(Foo(""))
fun n11() = (Foo::func)(Foo(""))
fun y01() = Foo::prop.<!NO_REFLECTION_IN_CLASS_PATH!>getter<!>
fun y02() = Foo::class.<!NO_REFLECTION_IN_CLASS_PATH!>properties<!>
fun y03() = Foo::class.<!NO_REFLECTION_IN_CLASS_PATH!>simpleName<!>
@@ -0,0 +1,25 @@
package
internal fun n01(): kotlin.reflect.KProperty1<Foo, kotlin.Any>
internal fun n02(): kotlin.reflect.KFunction1<Foo, kotlin.Unit>
internal fun n03(): kotlin.reflect.KClass<Foo>
internal fun n04(/*0*/ p: kotlin.reflect.KProperty0<kotlin.Int>): kotlin.Int
internal fun n05(/*0*/ p: kotlin.reflect.KMutableProperty0<kotlin.String>): kotlin.Unit
internal fun n06(/*0*/ p: kotlin.reflect.KProperty0<kotlin.Int>): kotlin.Int
internal fun n07(/*0*/ p: kotlin.reflect.KFunction<kotlin.String>): kotlin.String
internal fun n08(/*0*/ p: kotlin.reflect.KProperty1<kotlin.String, kotlin.Int>): kotlin.Int
internal fun n09(/*0*/ p: kotlin.reflect.KProperty2<kotlin.String, kotlin.String, kotlin.Int>): kotlin.Int
internal fun n10(): kotlin.Unit
internal fun n11(): kotlin.Unit
internal fun y01(): kotlin.reflect.KProperty1.Getter<Foo, kotlin.Any>
internal fun y02(): kotlin.Collection<kotlin.reflect.KProperty1<Foo, *>>
internal fun y03(): kotlin.String?
internal final class Foo {
public constructor Foo(/*0*/ prop: kotlin.Any)
internal final val prop: kotlin.Any
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
internal final fun func(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -10435,6 +10435,21 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
}
}
@TestMetadata("compiler/testData/diagnostics/tests/reflection")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Reflection extends AbstractJetDiagnosticsTest {
public void testAllFilesPresentInReflection() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/reflection"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("noReflectionInClassPath.kt")
public void testNoReflectionInClassPath() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/reflection/noReflectionInClassPath.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/diagnostics/tests/regressions")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -97,12 +97,6 @@ public class KotlincExecutableTestGenerated extends AbstractKotlincExecutableTes
doJvmTest(fileName);
}
@TestMetadata("noReflectionInClasspath.args")
public void testNoReflectionInClasspath() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cli/jvm/noReflectionInClasspath.args");
doJvmTest(fileName);
}
@TestMetadata("nonExistingClassPathAndAnnotationsPath.args")
public void testNonExistingClassPathAndAnnotationsPath() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cli/jvm/nonExistingClassPathAndAnnotationsPath.args");
@@ -21,6 +21,7 @@ import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
@@ -126,8 +127,10 @@ public class JetExpectedResolveDataUtil {
Project project,
JetType... parameterTypes
) {
ModuleDescriptor emptyModule = JetTestUtils.createEmptyModule();
ModuleDescriptorImpl emptyModule = JetTestUtils.createEmptyModule();
ContainerForTests container = DiPackage.createContainerForTests(project, emptyModule);
emptyModule.setDependencies(emptyModule);
emptyModule.initialize(PackageFragmentProvider.Empty.INSTANCE$);
ExpressionTypingContext context = ExpressionTypingContext.newContext(
container.getAdditionalCheckerProvider(), new BindingTraceContext(), classDescriptor.getDefaultType().getMemberScope(),
@@ -23,8 +23,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
import org.jetbrains.kotlin.descriptors.ModuleDescriptor;
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider;
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor;
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor;
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl;
import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
@@ -73,7 +75,10 @@ public class JetTypeCheckerTest extends JetLiteFixture {
builtIns = KotlinBuiltIns.getInstance();
ContainerForTests container = DiPackage.createContainerForTests(getProject(), JetTestUtils.createEmptyModule());
ModuleDescriptorImpl module = JetTestUtils.createEmptyModule();
ContainerForTests container = DiPackage.createContainerForTests(getProject(), module);
module.setDependencies(Collections.singletonList(module));
module.initialize(PackageFragmentProvider.Empty.INSTANCE$);
typeResolver = container.getTypeResolver();
expressionTypingServices = container.getExpressionTypingServices();
@@ -18,20 +18,19 @@ package org.jetbrains.kotlin.builtins
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.isSubpackageOf
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.types.*
import java.util.ArrayList
import kotlin.properties.Delegates
val KOTLIN_REFLECT_FQ_NAME = FqName("kotlin.reflect")
public class ReflectionTypes(private val module: ModuleDescriptor) {
private val kotlinReflectScope: JetScope by Delegates.lazy {
private val kotlinReflectScope: JetScope by lazy {
module.getPackage(KOTLIN_REFLECT_FQ_NAME).memberScope
}
@@ -52,6 +51,7 @@ public class ReflectionTypes(private val module: ModuleDescriptor) {
public val kClass: ClassDescriptor by ClassLookup
public val kProperty0: ClassDescriptor by ClassLookup
public val kProperty1: ClassDescriptor by ClassLookup
public val kProperty2: ClassDescriptor by ClassLookup
public val kMutableProperty0: ClassDescriptor by ClassLookup
public val kMutableProperty1: ClassDescriptor by ClassLookup
@@ -108,10 +108,9 @@ public class ReflectionTypes(private val module: ModuleDescriptor) {
}
companion object {
public fun isReflectionType(type: JetType): Boolean {
val descriptor = type.getConstructor().getDeclarationDescriptor() ?: return false
val fqName = DescriptorUtils.getFqName(descriptor)
return fqName.isSafe() && fqName.toSafe().isSubpackageOf(KOTLIN_REFLECT_FQ_NAME)
public fun isReflectionClass(descriptor: ClassDescriptor): Boolean {
val containingPackage = DescriptorUtils.getParentOfType(descriptor, javaClass<PackageFragmentDescriptor>())
return containingPackage != null && containingPackage.fqName == KOTLIN_REFLECT_FQ_NAME
}
}
}
-7
View File
@@ -1047,13 +1047,6 @@
level="WARNING"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ReflectionNotFoundInspection"
displayName="Reflection not found"
groupName="Kotlin"
enabledByDefault="true"
level="WARNING"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.refactoring.move.changePackage.PackageDirectoryMismatchInspection"
displayName="Package name does not match containing directory"
groupName="Kotlin"
@@ -0,0 +1,79 @@
/*
* 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.openapi.editor.Editor
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 org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.JetBundle
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.quickfix.JetIntentionAction
import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory
import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionForFirstParentOfType
import org.jetbrains.kotlin.idea.versions.KotlinRuntimeLibraryUtil
import org.jetbrains.kotlin.psi.JetElement
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.File
public class AddReflectionQuickFix(element: JetElement) : JetIntentionAction<JetElement>(element) {
override fun getText() = JetBundle.message("add.reflection.to.classpath")
override fun getFamilyName() = getText()
override fun invoke(project: Project, editor: Editor?, file: JetFile) {
val pluginReflectJar = PathUtil.getKotlinPathsForIdeaPlugin().getReflectPath()
if (!pluginReflectJar.exists()) return
val configurator = Extensions.getExtensions(KotlinProjectConfigurator.EP_NAME)
.firstIsInstanceOrNull<KotlinJavaModuleConfigurator>() ?: return
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()}"
)
}
}
companion object : JetSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic) = diagnostic.createIntentionForFirstParentOfType(::AddReflectionQuickFix)
}
}
@@ -1,137 +0,0 @@
/*
* 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.module.ModuleUtilCore
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 com.intellij.psi.PsiFile
import org.jetbrains.kotlin.idea.JetBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyze
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.project.ProjectStructureUtil
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.resolve.BindingContext
import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies
import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.psi.JetAnnotationEntry
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
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 {
if (!shouldReportInFile(holder.getFile())) return PsiElementVisitor.EMPTY_VISITOR
return object : JetVisitorVoid() {
private fun createQuickFix(): LocalQuickFix? {
val pluginReflectJar = PathUtil.getKotlinPathsForIdeaPlugin().getReflectPath()
if (pluginReflectJar.exists()) {
val configurator =
Extensions.getExtensions(KotlinProjectConfigurator.EP_NAME).firstIsInstanceOrNull<KotlinJavaModuleConfigurator>()
if (configurator != null) {
return AddReflectJarQuickFix(configurator, pluginReflectJar)
}
}
return null
}
override fun visitDoubleColonExpression(expression: JetDoubleColonExpression) {
val expectedType = expression.analyze().get(BindingContext.EXPECTED_EXPRESSION_TYPE, expression)
if (expectedType != null && !ReflectionTypes.isReflectionType(expectedType)) return
if (expression.getStrictParentOfType<JetAnnotationEntry>() != null) return
// If a callable reference is used where a KFunction/KProperty/... expected, we should report that usage as dangerous
// because reflection features will fail without kotlin-reflect.jar in the classpath.
// If it's only used as a Function however (for example, "list.map(::function)"), we should not report anything
holder.registerProblem(
expression.getDoubleColonTokenReference(),
JetBundle.message("reflection.not.found"),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
*(createQuickFix().singletonOrEmptyList().toTypedArray())
)
}
}
}
private fun shouldReportInFile(file: PsiFile): Boolean {
if (file !is JetFile || !ProjectRootsUtil.isInProjectSource(file)) return false
val module = ModuleUtilCore.findModuleForPsiElement(file)
if (module == null || !ProjectStructureUtil.isJavaKotlinModule(module)) return false
return file.findModuleDescriptor().findClassAcrossModuleDependencies(JvmAbi.REFLECTION_FACTORY_IMPL) == null
}
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()}"
)
}
}
}
}
@@ -20,6 +20,7 @@ import com.intellij.codeInsight.intention.IntentionAction
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.idea.core.codeInsight.ImplementMethodsHandler
import org.jetbrains.kotlin.idea.inspections.AddReflectionQuickFix
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable.*
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClassFromCallWithConstructorCalleeActionFactory
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClassFromConstructorCallActionFactory
@@ -30,7 +31,9 @@ import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateP
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterByNamedArgumentActionFactory
import org.jetbrains.kotlin.lexer.JetTokens.*
import org.jetbrains.kotlin.psi.JetClass
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.DEPRECATED_ANNOTATION_METHOD_CALL
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.NO_REFLECTION_IN_CLASS_PATH
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION
public class QuickFixRegistrar : QuickFixContributor {
public override fun registerQuickFixes(quickFixes: QuickFixes) {
@@ -306,8 +309,7 @@ public class QuickFixRegistrar : QuickFixContributor {
EXPLICIT_DELEGATION_CALL_REQUIRED.registerFactory(InsertDelegationCallQuickfix.InsertThisDelegationCallFactory)
EXPLICIT_DELEGATION_CALL_REQUIRED.registerFactory(InsertDelegationCallQuickfix.InsertSuperDelegationCallFactory)
ErrorsJvm.DEPRECATED_ANNOTATION_METHOD_CALL.registerFactory(MigrateAnnotationMethodCallFix,
MigrateAnnotationMethodCallInWholeFile)
DEPRECATED_ANNOTATION_METHOD_CALL.registerFactory(MigrateAnnotationMethodCallFix, MigrateAnnotationMethodCallInWholeFile)
ENUM_ENTRY_USES_DEPRECATED_SUPER_CONSTRUCTOR.registerFactory(DeprecatedEnumEntrySuperConstructorSyntaxFix,
DeprecatedEnumEntrySuperConstructorSyntaxFix.createWholeProjectFixFactory())
@@ -327,6 +329,8 @@ public class QuickFixRegistrar : QuickFixContributor {
DEPRECATED_SYMBOL_WITH_MESSAGE.registerFactory(DeprecatedSymbolUsageFix,
DeprecatedSymbolUsageInWholeProjectFix)
ErrorsJvm.POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION.registerFactory(ReplaceJavaAnnotationPositionedArgumentsFix)
POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION.registerFactory(ReplaceJavaAnnotationPositionedArgumentsFix)
NO_REFLECTION_IN_CLASS_PATH.registerFactory(AddReflectionQuickFix)
}
}
@@ -1,18 +0,0 @@
package test
// WITH_RUNTIME
import kotlin.reflect.KFunction0
fun foo() {}
fun bar(f: () -> Unit) = f()
// Inspection should be reported here because '::foo' may be used as a reflection object
val p1 = ::foo // the type is KFunction0 by default
val p2: KFunction0<Unit> = ::foo // the expected type is KFunction0
// But shouldn't be reported here
val p3 = bar(::foo) // the expected type is Function0, '::foo' is used as an ordinary function
val p4: Any = ::foo // the expected type is Any
val p5: UnresolvedClass = ::foo // an error, another warning would be useless
@@ -1,26 +0,0 @@
<problems>
<problem>
<file>functionReference.kt</file>
<line>12</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/functionReference.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Reflection not found</problem_class>
<description>Reflection not found in the classpath</description>
</problem>
<problem>
<file>functionReference.kt</file>
<line>13</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/functionReference.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Reflection not found</problem_class>
<description>Reflection not found in the classpath</description>
</problem>
<problem>
<file>withinAnnotationEntry.kt</file>
<line>13</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/withinAnnotationEntry.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Reflection not found</problem_class>
<description>Reflection not found in the classpath</description>
</problem>
</problems>
@@ -1 +0,0 @@
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.ReflectionNotFoundInspection
@@ -1,15 +0,0 @@
package test
// WITH_RUNTIME
import kotlin.reflect.KClass
annotation class A(val arg: KClass<*>, val args: Array<KClass<*>>, vararg val other: KClass<*>)
A(Int::class, array(String::class), Double::class, Char::class)
class MyClass {
throws(Exception::class)
fun foo() {
Exception::class
}
}
@@ -88,12 +88,6 @@ public class JetInspectionTestGenerated extends AbstractJetInspectionTest {
JetTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/inspections"), Pattern.compile("^(inspections\\.test)$"));
}
@TestMetadata("reflectionNotFound/inspectionData/inspections.test")
public void testReflectionNotFound_inspectionData_Inspections_test() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/inspections/reflectionNotFound/inspectionData/inspections.test");
doTest(fileName);
}
@TestMetadata("spelling/inspectionData/inspections.test")
public void testSpelling_inspectionData_Inspections_test() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/inspections/spelling/inspectionData/inspections.test");