Report error if some deserialized classes are missing in dependencies

Technically we often can compile code which uses missing classes (as long as
nothing is called on them) but it seems better to let the user know something's
wrong in their setup before the error manifests itself at runtime. Also the
Java compiler does the same

 #KT-4328 Fixed
This commit is contained in:
Alexander Udalov
2016-03-10 19:07:40 +03:00
parent accf80a624
commit db5e00bcc0
14 changed files with 141 additions and 13 deletions
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2016 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.checkers
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.checkers.isComputingDeferredType
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import org.jetbrains.kotlin.serialization.deserialization.NotFoundClasses
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.newLinkedHashSetWithExpectedSize
class MissingDependencyClassChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, context: BasicCallResolutionContext) {
for (classId in collectNotFoundClasses(resolvedCall.resultingDescriptor)) {
context.trace.report(ErrorsJvm.MISSING_DEPENDENCY_CLASS.on(resolvedCall.call.callElement, classId.asSingleFqName()))
}
}
private fun collectNotFoundClasses(descriptor: CallableDescriptor): Set<ClassId> {
val result: MutableSet<ClassId> = newLinkedHashSetWithExpectedSize(1)
fun consider(classDescriptor: ClassDescriptor) {
if (classDescriptor is NotFoundClasses.MockClassDescriptor) {
result.add(classDescriptor.classId)
return
}
(classDescriptor.containingDeclaration as? ClassDescriptor)?.let(::consider)
}
fun consider(type: KotlinType) {
if (!isComputingDeferredType(type)) {
(type.constructor.declarationDescriptor as? ClassDescriptor)?.let(::consider)
}
}
descriptor.returnType?.let(::consider)
descriptor.extensionReceiverParameter?.value?.type?.let(::consider)
descriptor.valueParameters.forEach { consider(it.type) }
return result.orEmpty()
}
}
@@ -54,6 +54,9 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
MAP.put(ErrorsJvm.CONFLICTING_JVM_DECLARATIONS, "Platform declaration clash: {0}", CONFLICTING_JVM_DECLARATIONS_DATA);
MAP.put(ErrorsJvm.ACCIDENTAL_OVERRIDE, "Accidental override: {0}", CONFLICTING_JVM_DECLARATIONS_DATA);
MAP.put(ErrorsJvm.CONFLICTING_INHERITED_JVM_DECLARATIONS, "Inherited platform declarations clash: {0}", CONFLICTING_JVM_DECLARATIONS_DATA);
MAP.put(ErrorsJvm.MISSING_DEPENDENCY_CLASS, "Cannot access class ''{0}''. Check your module classpath for missing or conflicting dependencies", Renderers.TO_STRING);
MAP.put(ErrorsJvm.JVM_STATIC_NOT_IN_OBJECT, "Only functions in named objects and companion objects of classes can be annotated with ''@JvmStatic''");
MAP.put(ErrorsJvm.JVM_STATIC_ON_CONST_OR_JVM_FIELD, "''@JvmStatic'' annotation is useless for const or ''@JvmField'' properties");
MAP.put(ErrorsJvm.OVERRIDE_CANNOT_BE_STATIC, "Override member cannot be ''@JvmStatic'' in object");
@@ -41,6 +41,8 @@ public interface ErrorsJvm {
DiagnosticFactory1<PsiElement, ConflictingJvmDeclarationsData> CONFLICTING_INHERITED_JVM_DECLARATIONS =
DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory1<PsiElement, FqName> MISSING_DEPENDENCY_CLASS = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<KtDeclaration> OVERRIDE_CANNOT_BE_STATIC = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory0<KtDeclaration> JVM_STATIC_NOT_IN_OBJECT = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory0<KtDeclaration> JVM_STATIC_ON_CONST_OR_JVM_FIELD = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
@@ -27,7 +27,6 @@ import org.jetbrains.kotlin.resolve.jvm.checkers.*
import org.jetbrains.kotlin.synthetic.JavaSyntheticScopes
import org.jetbrains.kotlin.types.DynamicTypesSettings
object JvmPlatformConfigurator : PlatformConfigurator(
DynamicTypesSettings(),
additionalDeclarationCheckers = listOf(
@@ -51,7 +50,8 @@ object JvmPlatformConfigurator : PlatformConfigurator(
JavaClassOnCompanionChecker(),
ProtectedInSuperClassCompanionCallChecker(),
UnsupportedSyntheticCallableReferenceChecker(),
SuperCallWithDefaultArgumentsChecker()
SuperCallWithDefaultArgumentsChecker(),
MissingDependencyClassChecker()
),
additionalTypeCheckers = listOf(