Refactor CallChecker and subclasses

Encapsulate everything that is needed in checkers into CallCheckerContext. Pass
an instance of this context instead of BasicCallResolutionContext to checkers.

Also pass an instance of the element to report errors on: this is useful
because before this, every checker had its own way of determining where should
the error be reported on. Some of them, for example, were not doing anything if
Call#calleeExpression returned null, which is wrong, see operatorCall.kt

 #KT-12875 Open
This commit is contained in:
Alexander Udalov
2016-06-27 16:36:29 +03:00
parent f6f825e0dc
commit 6ba32ed624
38 changed files with 278 additions and 290 deletions
@@ -75,7 +75,7 @@ public interface Errors {
DiagnosticFactory3.create(ERROR);
DiagnosticFactory3<PsiElement, DeclarationDescriptor, Visibility, DeclarationDescriptor> INVISIBLE_MEMBER = DiagnosticFactory3.create(ERROR, CALL_ELEMENT);
DiagnosticFactory1<KtExpression, ConstructorDescriptor> PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, ConstructorDescriptor> PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL = DiagnosticFactory1.create(ERROR);
// Exposed visibility group
DiagnosticFactory3<PsiElement, EffectiveVisibility, DescriptorWithRelation, EffectiveVisibility> EXPOSED_PROPERTY_TYPE = DiagnosticFactory3.create(ERROR);
@@ -812,7 +812,7 @@ public interface Errors {
DiagnosticFactory0<KtDeclaration> INLINE_PROPERTY_WITH_BACKING_FIELD = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory0<KtElement> NON_LOCAL_SUSPENSION_POINT = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> NON_LOCAL_SUSPENSION_POINT = DiagnosticFactory0.create(ERROR);
// Error sets
@@ -16,12 +16,13 @@
package org.jetbrains.kotlin.resolve
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.KtInstanceExpressionWithLabel
import org.jetbrains.kotlin.resolve.calls.checkers.SimpleCallChecker
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
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.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind
@@ -29,26 +30,27 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.Receiver
import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf
object ConstructorHeaderCallChecker : SimpleCallChecker {
override fun check(resolvedCall: ResolvedCall<*>, context: BasicCallResolutionContext) {
object ConstructorHeaderCallChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val dispatchReceiverClass = resolvedCall.dispatchReceiver.classDescriptorForImplicitReceiver
val extensionReceiverClass = resolvedCall.extensionReceiver.classDescriptorForImplicitReceiver
val callElement = resolvedCall.call.callElement
val labelReferenceClass =
(resolvedCall.call.callElement as? KtInstanceExpressionWithLabel)?.let {
instanceExpressionWithLabel ->
context.trace.get(BindingContext.REFERENCE_TARGET, instanceExpressionWithLabel.instanceReference) as? ClassDescriptor
if (callElement is KtInstanceExpressionWithLabel) {
context.trace.get(BindingContext.REFERENCE_TARGET, callElement.instanceReference) as? ClassDescriptor
}
else null
if (dispatchReceiverClass == null && extensionReceiverClass == null && labelReferenceClass == null) return
if (context.scope.parentsWithSelf.any() {
it is LexicalScope && it.kind == LexicalScopeKind.CONSTRUCTOR_HEADER
&& (it.ownerDescriptor as ConstructorDescriptor).containingDeclaration in
setOf(dispatchReceiverClass, extensionReceiverClass, labelReferenceClass)
val classes = setOf(dispatchReceiverClass, extensionReceiverClass, labelReferenceClass)
if (context.scope.parentsWithSelf.any { scope ->
scope is LexicalScope && scope.kind == LexicalScopeKind.CONSTRUCTOR_HEADER &&
(scope.ownerDescriptor as ConstructorDescriptor).containingDeclaration in classes
}) {
context.trace.report(
Errors.INSTANCE_ACCESS_BEFORE_SUPER_CALL.on(context.call.calleeExpression ?: return, resolvedCall.resultingDescriptor))
context.trace.report(Errors.INSTANCE_ACCESS_BEFORE_SUPER_CALL.on(reportOn, resolvedCall.resultingDescriptor))
}
}
}
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode.
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getEffectiveExpectedType
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInvokeCallOnVariable
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.context.CallCandidateResolutionContext
import org.jetbrains.kotlin.resolve.calls.context.CallPosition
@@ -83,14 +84,16 @@ class CallCompleter(
}
if (resolvedCall != null) {
for (callChecker in callCheckers) {
callChecker.check(resolvedCall, context, languageFeatureSettings)
}
val element = if (resolvedCall is VariableAsFunctionResolvedCall)
resolvedCall.variableCall.call.calleeExpression
else
resolvedCall.call.calleeExpression
val reportOn = element ?: resolvedCall.call.callElement
val callCheckerContext = CallCheckerContext(context, languageFeatureSettings)
for (callChecker in callCheckers) {
callChecker.check(resolvedCall, reportOn, callCheckerContext)
}
for (validator in symbolUsageValidators) {
validator.validateCall(resolvedCall, context.trace, element!!)
@@ -16,30 +16,30 @@
package org.jetbrains.kotlin.resolve.calls.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeatureSettings
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.types.DeferredType
import org.jetbrains.kotlin.types.KotlinType
interface CallChecker {
// TODO: Think about encapsulating these parameters into specific class like CheckerParameters when you're about to add another one
fun check(
resolvedCall: ResolvedCall<*>,
context: BasicCallResolutionContext,
languageFeatureSettings: LanguageFeatureSettings
)
fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext)
}
interface SimpleCallChecker : CallChecker {
override fun check(
resolvedCall: ResolvedCall<*>,
context: BasicCallResolutionContext,
languageFeatureSettings: LanguageFeatureSettings
) = check(resolvedCall, context)
fun check(resolvedCall: ResolvedCall<*>, context: BasicCallResolutionContext)
class CallCheckerContext(
val trace: BindingTrace,
val scope: LexicalScope,
val languageFeatureSettings: LanguageFeatureSettings,
val dataFlowInfo: DataFlowInfo,
val isAnnotationContext: Boolean
) {
constructor(c: ResolutionContext<*>, languageFeatureSettings: LanguageFeatureSettings) : this(
c.trace, c.scope, languageFeatureSettings, c.dataFlowInfo, c.isAnnotationContext
)
}
// Use this utility to avoid premature computation of deferred return type of a resolved callable descriptor.
@@ -48,6 +48,3 @@ interface SimpleCallChecker : CallChecker {
@Suppress("unused")
fun CallChecker.isComputingDeferredType(type: KotlinType) =
type is DeferredType && type.isComputing
val ResolvedCall<*>.elementToReportOn: KtElement
get() = call.calleeExpression ?: call.callElement
@@ -16,20 +16,16 @@
package org.jetbrains.kotlin.resolve.calls.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isArrayOfNothing
class CallReturnsArrayOfNothingChecker : SimpleCallChecker {
override fun check(resolvedCall: ResolvedCall<*>, context: BasicCallResolutionContext) {
val returnType = resolvedCall.resultingDescriptor.returnType
if (returnType.containsArrayOfNothing()) {
val callElement = resolvedCall.call.callElement
val diagnostic = Errors.UNSUPPORTED.on(callElement, "Array<Nothing> in return type is illegal")
context.trace.report(diagnostic)
class CallReturnsArrayOfNothingChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
if (resolvedCall.resultingDescriptor.returnType.containsArrayOfNothing()) {
context.trace.report(Errors.UNSUPPORTED.on(reportOn, "Array<Nothing> in return type is illegal"))
}
}
@@ -16,21 +16,21 @@
package org.jetbrains.kotlin.resolve.calls.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContext.CAPTURED_IN_CLOSURE
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.kotlin.resolve.inline.InlineUtil
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.types.expressions.CaptureKind
class CapturingInClosureChecker : SimpleCallChecker {
override fun check(resolvedCall: ResolvedCall<*>, context: BasicCallResolutionContext) {
class CapturingInClosureChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val variableResolvedCall = if (resolvedCall is VariableAsFunctionResolvedCall) resolvedCall.variableCall else resolvedCall
val variableDescriptor = variableResolvedCall.resultingDescriptor as? VariableDescriptor
if (variableDescriptor != null) {
@@ -68,9 +68,9 @@ class CapturingInClosureChecker : SimpleCallChecker {
if (!InlineUtil.canBeInlineArgument(scopeDeclaration)) return false
if (InlineUtil.isInlinedArgument(scopeDeclaration as KtFunction, context, false)) {
val scopeContainerParent = scopeContainer.containingDeclaration
assert(scopeContainerParent != null) { "parent is null for " + scopeContainer }
return !isCapturedVariable(variableParent, scopeContainerParent!!) || isCapturedInInline(context, scopeContainerParent, variableParent)
val scopeContainerParent = scopeContainer.containingDeclaration ?: error("parent is null for $scopeContainer")
return !isCapturedVariable(variableParent, scopeContainerParent) ||
isCapturedInInline(context, scopeContainerParent, variableParent)
}
return false
}
@@ -17,12 +17,10 @@
package org.jetbrains.kotlin.resolve.calls.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeatureSettings
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtOperationReferenceExpression
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
@@ -30,14 +28,15 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.types.ErrorUtils
class InfixCallChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, context: BasicCallResolutionContext, languageFeatureSettings: LanguageFeatureSettings) {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val functionDescriptor = resolvedCall.resultingDescriptor as? FunctionDescriptor ?: return
if (functionDescriptor.isDynamic() || ErrorUtils.isError(functionDescriptor)) return
if (functionDescriptor.isInfix || functionDescriptor.isDynamic() || ErrorUtils.isError(functionDescriptor)) return
val element = ((resolvedCall as? VariableAsFunctionResolvedCall)?.variableCall ?: resolvedCall).call.calleeExpression
if (isInfixCall(element) && !functionDescriptor.isInfix) {
val operationRefExpression = element as? KtOperationReferenceExpression ?: return
if (isInfixCall(element)) {
val containingDeclarationName = functionDescriptor.containingDeclaration.fqNameUnsafe.asString()
context.trace.report(Errors.INFIX_MODIFIER_REQUIRED.on(operationRefExpression, functionDescriptor, containingDeclarationName))
context.trace.report(Errors.INFIX_MODIFIER_REQUIRED.on(
reportOn as? KtOperationReferenceExpression ?: return, functionDescriptor, containingDeclarationName
))
}
}
@@ -20,7 +20,6 @@ import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.FunctionTypesKt;
import org.jetbrains.kotlin.config.LanguageFeatureSettings;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.lexer.KtToken;
@@ -28,7 +27,6 @@ import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext;
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
@@ -49,7 +47,7 @@ import static org.jetbrains.kotlin.diagnostics.Errors.USAGE_IS_NOT_INLINABLE;
import static org.jetbrains.kotlin.resolve.inline.InlineUtil.allowsNonLocalReturns;
import static org.jetbrains.kotlin.resolve.inline.InlineUtil.checkNonLocalReturnUsage;
class InlineChecker implements SimpleCallChecker {
class InlineChecker implements CallChecker {
private final FunctionDescriptor descriptor;
private final Set<CallableDescriptor> inlinableParameters = new LinkedHashSet<CallableDescriptor>();
private final boolean isEffectivelyPublicApiFunction;
@@ -68,17 +66,8 @@ class InlineChecker implements SimpleCallChecker {
}
@Override
public void check(
@NotNull ResolvedCall<?> resolvedCall,
@NotNull BasicCallResolutionContext context,
@NotNull LanguageFeatureSettings languageFeatureSettings
) {
SimpleCallChecker.DefaultImpls.check(this, resolvedCall, context, languageFeatureSettings);
}
@Override
public void check(@NotNull ResolvedCall<?> resolvedCall, @NotNull BasicCallResolutionContext context) {
KtExpression expression = context.call.getCalleeExpression();
public void check(@NotNull ResolvedCall<?> resolvedCall, @NotNull PsiElement reportOn, @NotNull CallCheckerContext context) {
KtExpression expression = resolvedCall.getCall().getCalleeExpression();
if (expression == null) {
return;
}
@@ -90,7 +79,7 @@ class InlineChecker implements SimpleCallChecker {
if (inlinableParameters.contains(targetDescriptor)) {
if (!isInsideCall(expression)) {
context.trace.report(USAGE_IS_NOT_INLINABLE.on(expression, expression, descriptor));
context.getTrace().report(USAGE_IS_NOT_INLINABLE.on(expression, expression, descriptor));
}
}
@@ -133,10 +122,8 @@ class InlineChecker implements SimpleCallChecker {
return parent != null;
}
private void checkValueParameter(
@NotNull BasicCallResolutionContext context,
@NotNull CallCheckerContext context,
@NotNull CallableDescriptor targetDescriptor,
@NotNull ValueArgument targetArgument,
@NotNull ValueParameterDescriptor targetParameterDescriptor
@@ -150,20 +137,20 @@ class InlineChecker implements SimpleCallChecker {
if (argumentCallee != null && inlinableParameters.contains(argumentCallee)) {
if (InlineUtil.isInline(targetDescriptor) && isInlinableParameter(targetParameterDescriptor)) {
if (allowsNonLocalReturns(argumentCallee) && !allowsNonLocalReturns(targetParameterDescriptor)) {
context.trace.report(NON_LOCAL_RETURN_NOT_ALLOWED.on(argumentExpression, argumentExpression));
context.getTrace().report(NON_LOCAL_RETURN_NOT_ALLOWED.on(argumentExpression, argumentExpression));
}
else {
checkNonLocalReturn(context, argumentCallee, argumentExpression);
}
}
else {
context.trace.report(USAGE_IS_NOT_INLINABLE.on(argumentExpression, argumentExpression, descriptor));
context.getTrace().report(USAGE_IS_NOT_INLINABLE.on(argumentExpression, argumentExpression, descriptor));
}
}
}
private void checkCallWithReceiver(
@NotNull BasicCallResolutionContext context,
@NotNull CallCheckerContext context,
@NotNull CallableDescriptor targetDescriptor,
@Nullable ReceiverValue receiver,
@Nullable KtExpression expression
@@ -194,13 +181,13 @@ class InlineChecker implements SimpleCallChecker {
@Nullable
private static CallableDescriptor getCalleeDescriptor(
@NotNull BasicCallResolutionContext context,
@NotNull CallCheckerContext context,
@NotNull KtExpression expression,
boolean unwrapVariableAsFunction
) {
if (!(expression instanceof KtSimpleNameExpression || expression instanceof KtThisExpression)) return null;
ResolvedCall<?> thisCall = CallUtilKt.getResolvedCall(expression, context.trace.getBindingContext());
ResolvedCall<?> thisCall = CallUtilKt.getResolvedCall(expression, context.getTrace().getBindingContext());
if (unwrapVariableAsFunction && thisCall instanceof VariableAsFunctionResolvedCall) {
return ((VariableAsFunctionResolvedCall) thisCall).getVariableCall().getResultingDescriptor();
}
@@ -208,27 +195,27 @@ class InlineChecker implements SimpleCallChecker {
}
private void checkLambdaInvokeOrExtensionCall(
@NotNull BasicCallResolutionContext context,
@NotNull CallCheckerContext context,
@NotNull CallableDescriptor lambdaDescriptor,
@NotNull CallableDescriptor callDescriptor,
@NotNull KtExpression receiverExpression
) {
boolean inlinableCall = isInvokeOrInlineExtension(callDescriptor);
if (!inlinableCall) {
context.trace.report(USAGE_IS_NOT_INLINABLE.on(receiverExpression, receiverExpression, descriptor));
context.getTrace().report(USAGE_IS_NOT_INLINABLE.on(receiverExpression, receiverExpression, descriptor));
}
else {
checkNonLocalReturn(context, lambdaDescriptor, receiverExpression);
}
}
public void checkRecursion(
@NotNull BasicCallResolutionContext context,
private void checkRecursion(
@NotNull CallCheckerContext context,
@NotNull CallableDescriptor targetDescriptor,
@NotNull KtElement expression
) {
if (targetDescriptor.getOriginal() == descriptor) {
context.trace.report(Errors.RECURSION_IN_INLINE.on(expression, expression, descriptor));
context.getTrace().report(Errors.RECURSION_IN_INLINE.on(expression, expression, descriptor));
}
}
@@ -250,10 +237,17 @@ class InlineChecker implements SimpleCallChecker {
return isInvoke || InlineUtil.isInline(descriptor);
}
private void checkVisibilityAndAccess(@NotNull CallableDescriptor declarationDescriptor, @NotNull KtElement expression, @NotNull BasicCallResolutionContext context){
boolean declarationDescriptorIsPublicApi = DescriptorUtilsKt.isEffectivelyPublicApi(declarationDescriptor) || isDefinedInInlineFunction(declarationDescriptor);
if (isEffectivelyPublicApiFunction && !declarationDescriptorIsPublicApi && declarationDescriptor.getVisibility() != Visibilities.LOCAL) {
context.trace.report(Errors.NON_PUBLIC_CALL_FROM_PUBLIC_INLINE.on(expression, declarationDescriptor, descriptor));
private void checkVisibilityAndAccess(
@NotNull CallableDescriptor declarationDescriptor,
@NotNull KtElement expression,
@NotNull CallCheckerContext context
) {
boolean declarationDescriptorIsPublicApi = DescriptorUtilsKt.isEffectivelyPublicApi(declarationDescriptor) ||
isDefinedInInlineFunction(declarationDescriptor);
if (isEffectivelyPublicApiFunction &&
!declarationDescriptorIsPublicApi &&
declarationDescriptor.getVisibility() != Visibilities.LOCAL) {
context.getTrace().report(Errors.NON_PUBLIC_CALL_FROM_PUBLIC_INLINE.on(expression, declarationDescriptor, descriptor));
}
else {
checkPrivateClassMemberAccess(declarationDescriptor, expression, context);
@@ -263,11 +257,11 @@ class InlineChecker implements SimpleCallChecker {
private void checkPrivateClassMemberAccess(
@NotNull DeclarationDescriptor declarationDescriptor,
@NotNull KtElement expression,
@NotNull BasicCallResolutionContext context
@NotNull CallCheckerContext context
) {
if (!isEffectivelyPrivateApiFunction) {
if (DescriptorUtilsKt.isInsidePrivateClass(declarationDescriptor)) {
context.trace.report(Errors.PRIVATE_CLASS_MEMBER_FROM_INLINE.on(expression, declarationDescriptor, descriptor));
context.getTrace().report(Errors.PRIVATE_CLASS_MEMBER_FROM_INLINE.on(expression, declarationDescriptor, descriptor));
}
}
}
@@ -285,14 +279,14 @@ class InlineChecker implements SimpleCallChecker {
}
private void checkNonLocalReturn(
@NotNull BasicCallResolutionContext context,
@NotNull CallCheckerContext context,
@NotNull CallableDescriptor inlinableParameterDescriptor,
@NotNull KtExpression parameterUsage
) {
if (!allowsNonLocalReturns(inlinableParameterDescriptor)) return;
if (!checkNonLocalReturnUsage(descriptor, parameterUsage, context.trace)) {
context.trace.report(NON_LOCAL_RETURN_NOT_ALLOWED.on(parameterUsage, parameterUsage));
if (!checkNonLocalReturnUsage(descriptor, parameterUsage, context.getTrace())) {
context.getTrace().report(NON_LOCAL_RETURN_NOT_ALLOWED.on(parameterUsage, parameterUsage));
}
}
}
@@ -16,17 +16,17 @@
package org.jetbrains.kotlin.resolve.calls.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.inline.InlineUtil
import java.lang.ref.WeakReference
class InlineCheckerWrapper : SimpleCallChecker {
private var checkersCache: WeakReference<MutableMap<DeclarationDescriptor, SimpleCallChecker>>? = null
class InlineCheckerWrapper : CallChecker {
private var checkersCache: WeakReference<MutableMap<DeclarationDescriptor, CallChecker>>? = null
override fun check(resolvedCall: ResolvedCall<*>, context: BasicCallResolutionContext) {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
if (context.isAnnotationContext) return
var parentDescriptor: DeclarationDescriptor? = context.scope.ownerDescriptor
@@ -34,14 +34,14 @@ class InlineCheckerWrapper : SimpleCallChecker {
while (parentDescriptor != null) {
if (InlineUtil.isInline(parentDescriptor)) {
val checker = getChecker(parentDescriptor as FunctionDescriptor)
checker.check(resolvedCall, context)
checker.check(resolvedCall, reportOn, context)
}
parentDescriptor = parentDescriptor.containingDeclaration
}
}
private fun getChecker(descriptor: FunctionDescriptor): SimpleCallChecker {
private fun getChecker(descriptor: FunctionDescriptor): CallChecker {
val map = checkersCache?.get() ?: hashMapOf()
checkersCache = checkersCache ?: WeakReference(map)
return map.getOrPut(descriptor) { InlineChecker(descriptor) }
@@ -16,15 +16,15 @@
package org.jetbrains.kotlin.resolve.calls.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCallImpl
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
class InvokeConventionChecker : SimpleCallChecker {
override fun check(resolvedCall: ResolvedCall<*>, context: BasicCallResolutionContext) {
class InvokeConventionChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
if (resolvedCall is VariableAsFunctionResolvedCallImpl) {
val functionCall = resolvedCall.functionCall
val variableCall = resolvedCall.variableCall
@@ -17,14 +17,12 @@
package org.jetbrains.kotlin.resolve.calls.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeatureSettings
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.calls.CallTransformer
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isConventionCall
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
@@ -36,17 +34,13 @@ import org.jetbrains.kotlin.util.OperatorNameConventions.UNARY_MINUS
import org.jetbrains.kotlin.util.OperatorNameConventions.UNARY_PLUS
class OperatorCallChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, context: BasicCallResolutionContext, languageFeatureSettings: LanguageFeatureSettings) {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val functionDescriptor = resolvedCall.resultingDescriptor as? FunctionDescriptor ?: return
if (!checkNotErrorOrDynamic(functionDescriptor)) return
val element = resolvedCall.call.calleeExpression ?: resolvedCall.call.callElement
val call = resolvedCall.call
fun isInvokeCall(): Boolean = call is CallTransformer.CallForImplicitInvoke
fun isMultiDeclaration(): Boolean = call.callElement is KtDestructuringDeclarationEntry
if (resolvedCall is VariableAsFunctionResolvedCall &&
call is CallTransformer.CallForImplicitInvoke && call.itIsVariableAsFunctionCall) {
val outerCall = call.outerCall
@@ -56,7 +50,7 @@ class OperatorCallChecker : CallChecker {
}
}
if (isMultiDeclaration() || isInvokeCall()) {
if (call.callElement is KtDestructuringDeclarationEntry || call is CallTransformer.CallForImplicitInvoke) {
if (!functionDescriptor.isOperator) {
report(call.callElement, functionDescriptor, context.trace)
}
@@ -66,7 +60,7 @@ class OperatorCallChecker : CallChecker {
val isConventionOperator = element is KtOperationReferenceExpression && element.getNameForConventionalOperation() != null
if (isConventionOperator || element is KtArrayAccessExpression) {
if (!functionDescriptor.isOperator) {
report(element, functionDescriptor, context.trace)
report(reportOn, functionDescriptor, context.trace)
}
if (isConventionOperator) {
checkDeprecatedUnaryConventions(call, functionDescriptor, context.trace)
@@ -85,16 +79,16 @@ class OperatorCallChecker : CallChecker {
}
companion object {
fun report(element: PsiElement, descriptor: FunctionDescriptor, sink: DiagnosticSink) {
fun report(reportOn: PsiElement, descriptor: FunctionDescriptor, sink: DiagnosticSink) {
if (!checkNotErrorOrDynamic(descriptor)) return
val containingDeclaration = descriptor.containingDeclaration
val containingDeclarationName = containingDeclaration.fqNameUnsafe.asString()
sink.report(Errors.OPERATOR_MODIFIER_REQUIRED.on(element, descriptor, containingDeclarationName))
sink.report(Errors.OPERATOR_MODIFIER_REQUIRED.on(reportOn, descriptor, containingDeclarationName))
}
private fun checkNotErrorOrDynamic(functionDescriptor: FunctionDescriptor): Boolean {
return (!functionDescriptor.isDynamic() && !ErrorUtils.isError(functionDescriptor))
return !functionDescriptor.isDynamic() && !ErrorUtils.isError(functionDescriptor)
}
}
}
@@ -16,18 +16,18 @@
package org.jetbrains.kotlin.resolve.calls.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.KtConstructorCalleeExpression
import org.jetbrains.kotlin.psi.KtConstructorDelegationReferenceExpression
import org.jetbrains.kotlin.psi.KtSuperTypeCallEntry
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
object ProtectedConstructorCallChecker : SimpleCallChecker {
override fun check(resolvedCall: ResolvedCall<*>, context: BasicCallResolutionContext) {
object ProtectedConstructorCallChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val descriptor = resolvedCall.resultingDescriptor as? ConstructorDescriptor ?: return
val constructorOwner = descriptor.containingDeclaration.original
val scopeOwner = context.scope.ownerDescriptor
@@ -36,7 +36,7 @@ object ProtectedConstructorCallChecker : SimpleCallChecker {
// Error already reported
if (!Visibilities.isVisibleWithAnyReceiver(descriptor, scopeOwner)) return
val calleeExpression = resolvedCall.call.calleeExpression ?: return
val calleeExpression = resolvedCall.call.calleeExpression
// Permit constructor super-calls
when (calleeExpression) {
@@ -53,7 +53,7 @@ object ProtectedConstructorCallChecker : SimpleCallChecker {
// of constructor owner
@Suppress("DEPRECATION")
if (Visibilities.findInvisibleMember(Visibilities.FALSE_IF_PROTECTED, descriptor, scopeOwner) == descriptor) {
context.trace.report(Errors.PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL.on(calleeExpression, descriptor))
context.trace.report(Errors.PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL.on(reportOn, descriptor))
}
}
}
@@ -17,35 +17,24 @@
package org.jetbrains.kotlin.resolve.calls.checkers;
import com.intellij.psi.PsiElement;
import kotlin.collections.CollectionsKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.config.LanguageFeatureSettings;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext;
import org.jetbrains.kotlin.psi.KtTypeProjection;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
import java.util.Map;
public class ReifiedTypeParameterSubstitutionChecker implements SimpleCallChecker {
public class ReifiedTypeParameterSubstitutionChecker implements CallChecker {
@Override
public void check(
@NotNull ResolvedCall<?> resolvedCall,
@NotNull BasicCallResolutionContext context,
@NotNull LanguageFeatureSettings languageFeatureSettings
) {
SimpleCallChecker.DefaultImpls.check(this, resolvedCall, context, languageFeatureSettings);
}
@Override
public void check(@NotNull ResolvedCall<?> resolvedCall, @NotNull BasicCallResolutionContext context) {
public void check(@NotNull ResolvedCall<?> resolvedCall, @NotNull PsiElement reportOn, @NotNull CallCheckerContext context) {
Map<TypeParameterDescriptor, KotlinType> typeArguments = resolvedCall.getTypeArguments();
for (Map.Entry<TypeParameterDescriptor, KotlinType> entry : typeArguments.entrySet()) {
TypeParameterDescriptor parameter = entry.getKey();
@@ -56,42 +45,34 @@ public class ReifiedTypeParameterSubstitutionChecker implements SimpleCallChecke
continue;
}
KtTypeProjection typeProjection = CollectionsKt.getOrNull(resolvedCall.getCall().getTypeArguments(), parameter.getIndex());
PsiElement reportErrorOn = typeProjection != null ? typeProjection : reportOn;
if (argumentDeclarationDescriptor instanceof TypeParameterDescriptor &&
!((TypeParameterDescriptor) argumentDeclarationDescriptor).isReified()) {
context.trace.report(
Errors.TYPE_PARAMETER_AS_REIFIED.on(getElementToReport(context, parameter.getIndex()), parameter)
);
context.getTrace().report(Errors.TYPE_PARAMETER_AS_REIFIED.on(reportErrorOn, parameter));
}
else if (TypeUtilsKt.cannotBeReified(argument)) {
context.trace.report(
Errors.REIFIED_TYPE_FORBIDDEN_SUBSTITUTION.on(getElementToReport(context, parameter.getIndex()), argument));
context.getTrace().report(Errors.REIFIED_TYPE_FORBIDDEN_SUBSTITUTION.on(reportErrorOn, argument));
}
// REIFIED_TYPE_UNSAFE_SUBSTITUTION is temporary disabled because it seems too strict now (see KT-10847)
//else if (TypeUtilsKt.unsafeAsReifiedArgument(argument) && !hasPureReifiableAnnotation(parameter)) {
// context.trace.report(
// Errors.REIFIED_TYPE_UNSAFE_SUBSTITUTION.on(getElementToReport(context, parameter.getIndex()), argument));
// context.getTrace().report(Errors.REIFIED_TYPE_UNSAFE_SUBSTITUTION.on(reportErrorOn, argument));
//}
}
}
/*
private static final FqName PURE_REIFIABLE_ANNOTATION_FQ_NAME = new FqName("kotlin.internal.PureReifiable");
private static boolean hasPureReifiableAnnotation(@NotNull TypeParameterDescriptor parameter) {
return parameter.getAnnotations().hasAnnotation(PURE_REIFIABLE_ANNOTATION_FQ_NAME) ||
isTypeParameterOfKotlinArray(parameter);
}
*/
private static boolean isTypeParameterOfKotlinArray(@NotNull TypeParameterDescriptor parameter) {
DeclarationDescriptor container = parameter.getContainingDeclaration();
return container instanceof ClassDescriptor && KotlinBuiltIns.isNonPrimitiveArray((ClassDescriptor) container);
}
@NotNull
private static PsiElement getElementToReport(@NotNull BasicCallResolutionContext context, int parameterIndex) {
if (context.call.getTypeArguments().size() > parameterIndex) {
return context.call.getTypeArguments().get(parameterIndex);
}
KtExpression callee = context.call.getCalleeExpression();
return callee != null ? callee : context.call.getCallElement();
}
}
@@ -16,17 +16,18 @@
package org.jetbrains.kotlin.resolve.calls.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
class SafeCallChecker : SimpleCallChecker {
override fun check(resolvedCall: ResolvedCall<*>, context: BasicCallResolutionContext) {
class SafeCallChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val operationNode = resolvedCall.call.callOperationNode ?: return
if (operationNode.elementType == KtTokens.SAFE_ACCESS && resolvedCall.explicitReceiverKind == ExplicitReceiverKind.NO_EXPLICIT_RECEIVER) {
if (operationNode.elementType == KtTokens.SAFE_ACCESS &&
resolvedCall.explicitReceiverKind == ExplicitReceiverKind.NO_EXPLICIT_RECEIVER) {
context.trace.report(Errors.UNEXPECTED_SAFE_CALL.on(operationNode.psi))
}
}
@@ -16,18 +16,17 @@
package org.jetbrains.kotlin.resolve.calls.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageFeatureSettings
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.coroutine.CoroutineReceiverValue
import org.jetbrains.kotlin.resolve.inline.InlineUtil
object CoroutineSuspendCallChecker : SimpleCallChecker {
override fun check(resolvedCall: ResolvedCall<*>, context: BasicCallResolutionContext) {
object CoroutineSuspendCallChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val descriptor = resolvedCall.candidateDescriptor as? FunctionDescriptor ?: return
if (!descriptor.isSuspend || descriptor.initialSignatureDescriptor == null) return
@@ -35,23 +34,17 @@ object CoroutineSuspendCallChecker : SimpleCallChecker {
val callElement = resolvedCall.call.callElement as KtExpression
if (!InlineUtil.checkNonLocalReturnUsage(dispatchReceiverOwner, callElement, context.trace)) {
context.trace.report(Errors.NON_LOCAL_SUSPENSION_POINT.on(resolvedCall.call.calleeExpression ?: callElement))
context.trace.report(Errors.NON_LOCAL_SUSPENSION_POINT.on(reportOn))
}
}
}
object BuilderFunctionsCallChecker : CallChecker {
override fun check(
resolvedCall: ResolvedCall<*>,
context: BasicCallResolutionContext,
languageFeatureSettings: LanguageFeatureSettings
) {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val descriptor = resolvedCall.candidateDescriptor as? FunctionDescriptor ?: return
if (descriptor.valueParameters.any { it.isCoroutine }
&& !languageFeatureSettings.supportsFeature(LanguageFeature.Coroutines)) {
context.trace.report(
Errors.UNSUPPORTED_FEATURE.on(
resolvedCall.call.calleeExpression ?: resolvedCall.call.callElement, LanguageFeature.Coroutines))
if (descriptor.valueParameters.any { it.isCoroutine } &&
!context.languageFeatureSettings.supportsFeature(LanguageFeature.Coroutines)) {
context.trace.report(Errors.UNSUPPORTED_FEATURE.on(reportOn, LanguageFeature.Coroutines))
}
}
}
@@ -39,8 +39,7 @@ import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver;
import org.jetbrains.kotlin.resolve.calls.CallExpressionResolver;
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker;
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext;
import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode;
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext;
import org.jetbrains.kotlin.resolve.calls.model.DataFlowInfoForArgumentsImpl;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCallImpl;
@@ -586,10 +585,9 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
trace.record(RESOLVED_CALL, call, resolvedCall);
trace.record(CALL, expression, call);
BasicCallResolutionContext resolutionContext =
BasicCallResolutionContext.create(context, call, CheckArgumentTypesMode.CHECK_CALLABLE_TYPE);
CallCheckerContext callCheckerContext = new CallCheckerContext(context, components.languageFeatureSettings);
for (CallChecker checker : components.callCheckers) {
checker.check(resolvedCall, resolutionContext, components.languageFeatureSettings);
checker.check(resolvedCall, expression, callCheckerContext);
}
for (SymbolUsageValidator validator : components.symbolUsageValidators) {
validator.validateCall(resolvedCall, trace, expression);
@@ -894,11 +892,11 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
if (resolvedCall != null) {
// Call must be validated with the actual, not temporary trace in order to report operator diagnostic
// Only unary assignment expressions (++, --) and +=/... must be checked, normal assignments have the proper trace
BasicCallResolutionContext callResolutionContext = BasicCallResolutionContext.create(
context.replaceBindingTrace(trace), resolvedCall.getCall(), CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS
CallCheckerContext callCheckerContext = new CallCheckerContext(
trace, context.scope, components.languageFeatureSettings, context.dataFlowInfo, context.isAnnotationContext
);
for (CallChecker checker : components.callCheckers) {
checker.check(resolvedCall, callResolutionContext, components.languageFeatureSettings);
checker.check(resolvedCall, expression, callCheckerContext);
}
}
}