diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/calls/checkers/JavaClassOnCompanionChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/calls/checkers/JavaClassOnCompanionChecker.kt new file mode 100644 index 00000000000..47be0f3dd27 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/calls/checkers/JavaClassOnCompanionChecker.kt @@ -0,0 +1,52 @@ +/* + * 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.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.annotations.Annotations +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 +import org.jetbrains.kotlin.types.JetTypeImpl +import org.jetbrains.kotlin.types.TypeProjectionImpl + +public class JavaClassOnCompanionChecker : CallChecker { + override fun check(resolvedCall: ResolvedCall, context: BasicCallResolutionContext) { + val descriptor = resolvedCall.resultingDescriptor + if (descriptor !is PropertyDescriptor || descriptor.name.asString() != "javaClass") return + + val container = descriptor.containingDeclaration + if (container !is PackageFragmentDescriptor || container.fqName.asString() != "kotlin") return + + val actualType = descriptor.type + + val companionObject = actualType.arguments.singleOrNull()?.type?.constructor?.declarationDescriptor as? ClassDescriptor ?: return + if (companionObject.isCompanionObject) { + val containingClass = companionObject.containingDeclaration as ClassDescriptor + val javaLangClass = actualType.constructor.declarationDescriptor as? ClassDescriptor ?: return + val expectedType = JetTypeImpl.create( + Annotations.EMPTY, javaLangClass, actualType.isMarkedNullable, + listOf(TypeProjectionImpl(containingClass.defaultType)) + ) + context.trace.report(ErrorsJvm.JAVA_CLASS_ON_COMPANION.on(resolvedCall.call.callElement, actualType, expectedType)) + } + } +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java index 3950023223a..c6428484d35 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java @@ -75,6 +75,12 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension { MAP.put(ErrorsJvm.WHEN_ENUM_CAN_BE_NULL_IN_JAVA, "Enum argument can be null in Java, but exhaustive when contains no null branch"); MAP.put(ErrorsJvm.INAPPLICABLE_PUBLIC_FIELD, "publicField annotation is not applicable to this declaration"); + + MAP.put(ErrorsJvm.JAVA_CLASS_ON_COMPANION, + "The resulting type of this ''javaClass'' call is {0} and not {1}. " + + "Please use the more clear ''::class.java'' syntax to avoid confusion", + Renderers.RENDER_TYPE, Renderers.RENDER_TYPE + ); } @NotNull diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java index 5c0218e1b1f..ce9246d1f60 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.psi.JetAnnotationEntry; import org.jetbrains.kotlin.psi.JetDeclaration; import org.jetbrains.kotlin.psi.JetElement; import org.jetbrains.kotlin.psi.JetExpression; +import org.jetbrains.kotlin.types.JetType; import static org.jetbrains.kotlin.diagnostics.PositioningStrategies.*; import static org.jetbrains.kotlin.diagnostics.Severity.ERROR; @@ -65,6 +66,8 @@ public interface ErrorsJvm { DiagnosticFactory0 NO_REFLECTION_IN_CLASS_PATH = DiagnosticFactory0.create(WARNING); + DiagnosticFactory2 JAVA_CLASS_ON_COMPANION = DiagnosticFactory2.create(WARNING); + enum NullabilityInformationSource { KOTLIN { @NotNull diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt index ca67d3b2715..74b1ea05f75 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt @@ -50,6 +50,7 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability import org.jetbrains.kotlin.resolve.descriptorUtil.getAnnotationRetention import org.jetbrains.kotlin.resolve.descriptorUtil.isRepeatableAnnotation +import org.jetbrains.kotlin.resolve.jvm.calls.checkers.JavaClassOnCompanionChecker 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 @@ -76,7 +77,8 @@ public object JvmPlatformConfigurator : PlatformConfigurator( NeedSyntheticChecker(), JavaAnnotationCallChecker(), JavaAnnotationMethodCallChecker(), - TraitDefaultMethodCallChecker() + TraitDefaultMethodCallChecker(), + JavaClassOnCompanionChecker() ), additionalTypeCheckers = listOf( diff --git a/compiler/testData/diagnostics/testsWithStdLib/javaClassOnCompanion.kt b/compiler/testData/diagnostics/testsWithStdLib/javaClassOnCompanion.kt new file mode 100644 index 00000000000..ae7324b91d9 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/javaClassOnCompanion.kt @@ -0,0 +1,32 @@ +package test + +class A { + companion object +} + +object O + +enum class E { + ENTRY +} + + +val a0 = A.javaClass +val a1 = test.A.javaClass +val a2 = A.Companion.javaClass +val a3 = A::class.java +val a4 = test.A::class.java +val a5 = A.Companion::class.java + +val o0 = O.javaClass +val o1 = O::class.java + +val e0 = E.javaClass +val e1 = E::class.java +val e2 = E.ENTRY.javaClass + +val int0 = Int.javaClass +val int1 = Int::class.java + +val string0 = String.javaClass +val string1 = String::class.java diff --git a/compiler/testData/diagnostics/testsWithStdLib/javaClassOnCompanion.txt b/compiler/testData/diagnostics/testsWithStdLib/javaClassOnCompanion.txt new file mode 100644 index 00000000000..e243251f9b6 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/javaClassOnCompanion.txt @@ -0,0 +1,57 @@ +package + +package test { + internal val a0: java.lang.Class + internal val a1: java.lang.Class + internal val a2: java.lang.Class + internal val a3: java.lang.Class + internal val a4: java.lang.Class + internal val a5: java.lang.Class + internal val e0: [ERROR : Type for E.javaClass] + internal val e1: java.lang.Class + internal val e2: java.lang.Class + internal val int0: java.lang.Class + internal val int1: java.lang.Class + internal val o0: java.lang.Class + internal val o1: java.lang.Class + internal val string0: java.lang.Class + internal val string1: java.lang.Class + + internal final class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public companion object Companion { + private constructor Companion() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + } + + internal final enum class E : kotlin.Enum { + enum entry ENTRY + + private constructor E() + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: test.E): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): test.E + public final /*synthesized*/ fun values(): kotlin.Array + } + + internal object O { + private constructor O() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java index 50589db6596..195ab3eab7b 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java @@ -47,6 +47,12 @@ public class JetDiagnosticsTestWithStdLibGenerated extends AbstractJetDiagnostic doTest(fileName); } + @TestMetadata("javaClassOnCompanion.kt") + public void testJavaClassOnCompanion() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/javaClassOnCompanion.kt"); + doTest(fileName); + } + @TestMetadata("outstar.kt") public void testOutstar() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/outstar.kt");