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:
@@ -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>() {
|
||||
|
||||
+3
-1
@@ -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()
|
||||
|
||||
+70
@@ -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()))
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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);
|
||||
|
||||
+1
-2
@@ -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$
|
||||
@@ -1,4 +0,0 @@
|
||||
class Foo(val prop: Any)
|
||||
|
||||
fun t1() = Foo::prop
|
||||
fun t2() = Foo::class
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user