Add frontend checks for missing dependency supertypes
Call checker and declaration checker are used in order to preserve backward compatibility. Attempt to use classifier usage checker was not good enouth, since not all errors found with it would actually be reported before. For example types and constructor calls don't cause supertypes to resolve, so missing supertypes would not lead to errors in case they are the only use of class name. Updated tests failing due to missing Java dependencies in superclasses.
This commit is contained in:
@@ -100,6 +100,7 @@ public interface Errors {
|
||||
DiagnosticFactory2<PsiElement, String, String> API_NOT_AVAILABLE = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
DiagnosticFactory1<PsiElement, FqName> MISSING_DEPENDENCY_CLASS = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory2<PsiElement, FqName, FqName> MISSING_DEPENDENCY_SUPERCLASS = DiagnosticFactory2.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, FqName> MISSING_BUILT_IN_DECLARATION = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, String> MISSING_SCRIPT_BASE_CLASS = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, String> MISSING_SCRIPT_STANDARD_TEMPLATE = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
+1
@@ -375,6 +375,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(API_NOT_AVAILABLE, "This declaration is only available since Kotlin {0} and cannot be used with the specified API version {1}", STRING, STRING);
|
||||
|
||||
MAP.put(MISSING_DEPENDENCY_CLASS, "Cannot access class ''{0}''. Check your module classpath for missing or conflicting dependencies", TO_STRING);
|
||||
MAP.put(MISSING_DEPENDENCY_SUPERCLASS, "Cannot access ''{0}'' which is a supertype of ''{1}''. Check your module classpath for missing or conflicting dependencies", TO_STRING, TO_STRING);
|
||||
MAP.put(MISSING_BUILT_IN_DECLARATION, "Cannot access built-in declaration ''{0}''. Ensure that you have a dependency on the Kotlin standard library", TO_STRING);
|
||||
MAP.put(MISSING_SCRIPT_BASE_CLASS, "Cannot access script base class ''{0}''. Check your module classpath for missing or conflicting dependencies", TO_STRING);
|
||||
MAP.put(MISSING_SCRIPT_STANDARD_TEMPLATE, "No script runtime was found in the classpath: class ''{0}'' not found. Please add kotlin-script-runtime.jar to the module dependencies.", TO_STRING);
|
||||
|
||||
@@ -34,7 +34,8 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
|
||||
LocalVariableTypeParametersChecker(),
|
||||
ExplicitApiDeclarationChecker(),
|
||||
TailrecFunctionChecker,
|
||||
TrailingCommaDeclarationChecker
|
||||
TrailingCommaDeclarationChecker,
|
||||
MissingDependencySupertypeChecker.ForDeclarations
|
||||
)
|
||||
|
||||
private val DEFAULT_CALL_CHECKERS = listOf(
|
||||
@@ -46,7 +47,8 @@ private val DEFAULT_CALL_CHECKERS = listOf(
|
||||
UnderscoreUsageChecker, AssigningNamedArgumentToVarargChecker(), ImplicitNothingAsTypeParameterCallChecker,
|
||||
PrimitiveNumericComparisonCallChecker, LambdaWithSuspendModifierCallChecker,
|
||||
UselessElvisCallChecker(), ResultTypeWithNullableOperatorsChecker(), NullableVarargArgumentCallChecker,
|
||||
NamedFunAsExpressionChecker, ContractNotAllowedCallChecker, ReifiedTypeParameterSubstitutionChecker(), TypeOfChecker
|
||||
NamedFunAsExpressionChecker, ContractNotAllowedCallChecker, ReifiedTypeParameterSubstitutionChecker(), TypeOfChecker,
|
||||
MissingDependencySupertypeChecker.ForCalls
|
||||
)
|
||||
private val DEFAULT_TYPE_CHECKERS = emptyList<AdditionalTypeChecker>()
|
||||
private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf(
|
||||
|
||||
@@ -9,18 +9,12 @@ import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
|
||||
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
|
||||
import org.jetbrains.kotlin.builtins.isFunctionOrSuspendFunctionType
|
||||
import org.jetbrains.kotlin.contracts.EffectSystem
|
||||
import org.jetbrains.kotlin.contracts.description.ContractProviderKey
|
||||
import org.jetbrains.kotlin.contracts.description.LazyContractProvider
|
||||
import org.jetbrains.kotlin.contracts.parsing.isContractCallDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isContractDescriptionCallPsiCheck
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isFirstStatement
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext.CONSTRAINT_SYSTEM_COMPLETER
|
||||
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode.RESOLVE_FUNCTION_ARGUMENTS
|
||||
@@ -46,10 +40,12 @@ import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy
|
||||
import org.jetbrains.kotlin.resolve.checkers.MissingDependencySupertypeChecker
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
|
||||
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant
|
||||
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor
|
||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.expressions.DataFlowAnalyzer
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
@@ -80,21 +76,25 @@ class CallCompleter(
|
||||
completeAllCandidates(context, results)
|
||||
}
|
||||
|
||||
if (resolvedCall != null && context.trace.wantsDiagnostics()) {
|
||||
val calleeExpression = if (resolvedCall is VariableAsFunctionResolvedCall)
|
||||
resolvedCall.variableCall.call.calleeExpression
|
||||
else
|
||||
resolvedCall.call.calleeExpression
|
||||
val reportOn =
|
||||
if (calleeExpression != null && !calleeExpression.isFakeElement) calleeExpression
|
||||
else resolvedCall.call.callElement
|
||||
if (context.trace.wantsDiagnostics()) {
|
||||
if (resolvedCall == null) {
|
||||
checkMissingSupertypes(context, moduleDescriptor)
|
||||
} else {
|
||||
val calleeExpression = if (resolvedCall is VariableAsFunctionResolvedCall)
|
||||
resolvedCall.variableCall.call.calleeExpression
|
||||
else
|
||||
resolvedCall.call.calleeExpression
|
||||
val reportOn =
|
||||
if (calleeExpression != null && !calleeExpression.isFakeElement) calleeExpression
|
||||
else resolvedCall.call.callElement
|
||||
|
||||
val callCheckerContext = CallCheckerContext(context, deprecationResolver, moduleDescriptor)
|
||||
for (callChecker in callCheckers) {
|
||||
callChecker.check(resolvedCall, reportOn, callCheckerContext)
|
||||
val callCheckerContext = CallCheckerContext(context, deprecationResolver, moduleDescriptor)
|
||||
for (callChecker in callCheckers) {
|
||||
callChecker.check(resolvedCall, reportOn, callCheckerContext)
|
||||
|
||||
if (resolvedCall is VariableAsFunctionResolvedCall) {
|
||||
callChecker.check(resolvedCall.variableCall, reportOn, callCheckerContext)
|
||||
if (resolvedCall is VariableAsFunctionResolvedCall) {
|
||||
callChecker.check(resolvedCall.variableCall, reportOn, callCheckerContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,6 +105,15 @@ class CallCompleter(
|
||||
return results
|
||||
}
|
||||
|
||||
private fun checkMissingSupertypes(context: BasicCallResolutionContext, moduleDescriptor: ModuleDescriptor) {
|
||||
val call = context.call
|
||||
val explicitReceiver = call.explicitReceiver.safeAs<ReceiverValue>()
|
||||
?: return
|
||||
MissingDependencySupertypeChecker.checkSupertypes(
|
||||
explicitReceiver.type, call.callElement, context.trace, moduleDescriptor
|
||||
)
|
||||
}
|
||||
|
||||
private fun <D : CallableDescriptor> completeAllCandidates(
|
||||
context: BasicCallResolutionContext,
|
||||
results: OverloadResolutionResultsImpl<D>
|
||||
|
||||
+19
-4
@@ -7,10 +7,7 @@ package org.jetbrains.kotlin.resolve.calls.tower
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.createFunctionType
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.FunctionDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
@@ -31,6 +28,7 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategyImpl
|
||||
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
|
||||
import org.jetbrains.kotlin.resolve.checkers.MissingDependencySupertypeChecker
|
||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.expressions.DoubleColonExpressionResolver
|
||||
@@ -92,6 +90,7 @@ class ResolvedAtomCompleter(
|
||||
val lastCall = if (resolvedCall is VariableAsFunctionResolvedCall) resolvedCall.functionCall else resolvedCall
|
||||
if (ErrorUtils.isError(resolvedCall.candidateDescriptor)) {
|
||||
kotlinToResolvedCallTransformer.runArgumentsChecks(topLevelCallContext, topLevelTrace, lastCall as NewResolvedCallImpl<*>)
|
||||
checkMissingReceiverSupertypes(resolvedCall, moduleDescriptor, topLevelTrace)
|
||||
return resolvedCall
|
||||
}
|
||||
|
||||
@@ -118,6 +117,22 @@ class ResolvedAtomCompleter(
|
||||
return resolvedCall
|
||||
}
|
||||
|
||||
private fun checkMissingReceiverSupertypes(
|
||||
resolvedCall: ResolvedCall<CallableDescriptor>,
|
||||
moduleDescriptor: ModuleDescriptor,
|
||||
trace: BindingTrace
|
||||
) {
|
||||
val receiverValue = resolvedCall.dispatchReceiver ?: resolvedCall.extensionReceiver
|
||||
receiverValue?.type?.let { receiverType ->
|
||||
MissingDependencySupertypeChecker.checkSupertypes(
|
||||
receiverType,
|
||||
resolvedCall.call.callElement,
|
||||
trace,
|
||||
moduleDescriptor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearPartiallyResolvedCall(resolvedCallAtom: ResolvedCallAtom) {
|
||||
val psiCall = KotlinToResolvedCallTransformer.keyForPartiallyResolvedCall(resolvedCallAtom)
|
||||
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.checkers
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtTypeParameterListOwner
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||
|
||||
object MissingDependencySupertypeChecker {
|
||||
object ForDeclarations : DeclarationChecker {
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
val trace = context.trace
|
||||
val module = context.moduleDescriptor
|
||||
|
||||
if (descriptor is ClassDescriptor) {
|
||||
checkSupertypes(descriptor.defaultType, declaration, trace, module)
|
||||
}
|
||||
|
||||
if (declaration is KtTypeParameterListOwner) {
|
||||
for (ktTypeParameter in declaration.typeParameters) {
|
||||
val typeParameterDescriptor = trace.bindingContext.get(BindingContext.TYPE_PARAMETER, ktTypeParameter) ?: continue
|
||||
for (upperBound in typeParameterDescriptor.upperBounds) {
|
||||
checkSupertypes(upperBound, ktTypeParameter, trace, module)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ForCalls : CallChecker {
|
||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
val descriptor = resolvedCall.resultingDescriptor
|
||||
|
||||
// Constructor call leads to resolution of supertypes of enclosing class if it's an inner class constructor
|
||||
checkHierarchy(descriptor.dispatchReceiverParameter?.declaration, reportOn, context)
|
||||
// The constructed class' own supertypes are not resolved after constructor call,
|
||||
// so its containing declaration should not be checked.
|
||||
if (descriptor !is ConstructorDescriptor) {
|
||||
checkHierarchy(descriptor.containingDeclaration, reportOn, context)
|
||||
checkHierarchy(descriptor.extensionReceiverParameter?.declaration, reportOn, context)
|
||||
}
|
||||
}
|
||||
|
||||
private val ReceiverParameterDescriptor.declaration
|
||||
get() = value.type.constructor.declarationDescriptor
|
||||
|
||||
private fun checkHierarchy(declaration: DeclarationDescriptor?, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
if (declaration !is ClassifierDescriptor) return
|
||||
|
||||
checkSupertypes(declaration.defaultType, reportOn, context.trace, context.moduleDescriptor)
|
||||
}
|
||||
}
|
||||
|
||||
fun checkSupertypes(
|
||||
classifierType: KotlinType,
|
||||
reportOn: PsiElement,
|
||||
trace: BindingTrace,
|
||||
moduleDescriptor: ModuleDescriptor
|
||||
) {
|
||||
val classifierDescriptor = classifierType.constructor.declarationDescriptor ?: return
|
||||
|
||||
for (supertype in classifierType.supertypes()) {
|
||||
val supertypeDeclaration = supertype.constructor.declarationDescriptor
|
||||
|
||||
/*
|
||||
* TODO: expects are not checked, because findClassAcrossModuleDependencies does not work with actualization via type alias
|
||||
* Type parameters are skipped here, bounds of type parameters are checked in declaration checker separately
|
||||
* Local declarations are ignored for optimization
|
||||
*/
|
||||
if (supertypeDeclaration !is ClassDescriptor || supertypeDeclaration.isExpect) continue
|
||||
if (supertypeDeclaration.visibility == Visibilities.LOCAL) continue
|
||||
|
||||
val superTypeClassId = supertypeDeclaration.classId ?: continue
|
||||
val dependency = moduleDescriptor.findClassAcrossModuleDependencies(superTypeClassId)
|
||||
|
||||
if (dependency == null || dependency is NotFoundClasses.MockClassDescriptor) {
|
||||
trace.report(
|
||||
Errors.MISSING_DEPENDENCY_SUPERCLASS.on(
|
||||
reportOn,
|
||||
supertypeDeclaration.fqNameSafe,
|
||||
classifierDescriptor.fqNameSafe
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user