Make infix & operator checkers implement CallChecker

Instead of SymbolUsageValidator
This commit is contained in:
Alexander Udalov
2016-06-24 16:24:39 +03:00
parent bb083a51cc
commit e94fd8f777
7 changed files with 53 additions and 76 deletions
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.checkers.OperatorCallChecker;
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem;
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemCompleter;
import org.jetbrains.kotlin.resolve.calls.inference.TypeVariableKt;
@@ -39,7 +40,6 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory;
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
import org.jetbrains.kotlin.resolve.scopes.ScopeUtils;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.kotlin.resolve.validation.OperatorValidator;
import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator;
import org.jetbrains.kotlin.types.*;
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
@@ -263,7 +263,7 @@ public class DelegatedPropertyResolver {
PsiElement byKeyword = delegate.getByKeywordNode().getPsi();
if (!resultingDescriptor.isOperator()) {
OperatorValidator.Companion.report(byKeyword, resultingDescriptor, trace);
OperatorCallChecker.Companion.report(byKeyword, resultingDescriptor, trace);
}
symbolUsageValidator.validateCall(resultingCall, resultingCall.getResultingDescriptor(), trace, byKeyword);
@@ -27,8 +27,6 @@ import org.jetbrains.kotlin.resolve.calls.checkers.*
import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator
import org.jetbrains.kotlin.resolve.scopes.SyntheticScopes
import org.jetbrains.kotlin.resolve.validation.DeprecatedSymbolValidator
import org.jetbrains.kotlin.resolve.validation.InfixValidator
import org.jetbrains.kotlin.resolve.validation.OperatorValidator
import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.DynamicTypesSettings
@@ -66,12 +64,14 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
SuspendModifierChecker,
CoroutineModifierChecker)
private val DEFAULT_CALL_CHECKERS = listOf(CapturingInClosureChecker(), InlineCheckerWrapper(), ReifiedTypeParameterSubstitutionChecker(),
SafeCallChecker(), InvokeConventionChecker(), CallReturnsArrayOfNothingChecker(),
ConstructorHeaderCallChecker, ProtectedConstructorCallChecker, CoroutineSuspendCallChecker,
BuilderFunctionsCallChecker)
private val DEFAULT_CALL_CHECKERS = listOf(
CapturingInClosureChecker(), InlineCheckerWrapper(), ReifiedTypeParameterSubstitutionChecker(), SafeCallChecker(),
InvokeConventionChecker(), CallReturnsArrayOfNothingChecker(), InfixCallChecker(), OperatorCallChecker(),
ConstructorHeaderCallChecker, ProtectedConstructorCallChecker,
CoroutineSuspendCallChecker, BuilderFunctionsCallChecker
)
private val DEFAULT_TYPE_CHECKERS = emptyList<AdditionalTypeChecker>()
private val DEFAULT_VALIDATORS = listOf(DeprecatedSymbolValidator(), OperatorValidator(), InfixValidator())
private val DEFAULT_VALIDATORS = listOf(DeprecatedSymbolValidator())
abstract class PlatformConfigurator(
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.CallTransformer
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.callUtil.getValueArgumentForExpression
import org.jetbrains.kotlin.resolve.calls.checkers.InfixCallChecker
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.EXPECTED_TYPE_POSITION
import org.jetbrains.kotlin.resolve.calls.inference.getNestedTypeVariables
@@ -41,7 +42,6 @@ import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.resolve.scopes.utils.getImplicitReceiversHierarchy
import org.jetbrains.kotlin.resolve.validation.InfixValidator
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
@@ -136,7 +136,7 @@ fun isConventionCall(call: Call): Boolean {
return calleeExpression.getNameForConventionalOperation() != null
}
fun isInfixCall(call: Call): Boolean = InfixValidator.isInfixCall(call.calleeExpression)
fun isInfixCall(call: Call): Boolean = InfixCallChecker.isInfixCall(call.calleeExpression)
fun getUnaryPlusOrMinusOperatorFunctionName(call: Call): Name? {
if (call.callElement !is KtPrefixExpression) return null
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* 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.
@@ -14,34 +14,30 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve.validation
package org.jetbrains.kotlin.resolve.calls.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.CallableDescriptor
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.BindingTrace
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
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.types.ErrorUtils
class InfixValidator : SymbolUsageValidator {
override fun validateCall(
resolvedCall: ResolvedCall<*>?,
targetDescriptor: CallableDescriptor,
trace: BindingTrace,
element: PsiElement
) {
val functionDescriptor = targetDescriptor as? FunctionDescriptor ?: return
class InfixCallChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, context: BasicCallResolutionContext, languageFeatureSettings: LanguageFeatureSettings) {
val functionDescriptor = resolvedCall.resultingDescriptor as? FunctionDescriptor ?: return
if (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
val containingDeclarationName = functionDescriptor.containingDeclaration.fqNameUnsafe.asString()
trace.report(Errors.INFIX_MODIFIER_REQUIRED.on(operationRefExpression, functionDescriptor, containingDeclarationName))
context.trace.report(Errors.INFIX_MODIFIER_REQUIRED.on(operationRefExpression, functionDescriptor, containingDeclarationName))
}
}
@@ -52,4 +48,4 @@ class InfixValidator : SymbolUsageValidator {
return binaryExpression.operationReference === operationRefExpression && !operationRefExpression.isPredefinedOperator()
}
}
}
}
@@ -14,18 +14,17 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve.validation
package org.jetbrains.kotlin.resolve.calls.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.CallableDescriptor
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.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
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,51 +35,41 @@ import org.jetbrains.kotlin.util.OperatorNameConventions.PLUS
import org.jetbrains.kotlin.util.OperatorNameConventions.UNARY_MINUS
import org.jetbrains.kotlin.util.OperatorNameConventions.UNARY_PLUS
class OperatorValidator : SymbolUsageValidator {
override fun validateCall(resolvedCall: ResolvedCall<*>?, targetDescriptor: CallableDescriptor, trace: BindingTrace, element: PsiElement) {
val functionDescriptor = targetDescriptor as? FunctionDescriptor ?: return
class OperatorCallChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, context: BasicCallResolutionContext, languageFeatureSettings: LanguageFeatureSettings) {
val functionDescriptor = resolvedCall.resultingDescriptor as? FunctionDescriptor ?: return
if (!checkNotErrorOrDynamic(functionDescriptor)) return
val jetElement = element as? KtElement ?: return
val call = resolvedCall?.call ?: trace.bindingContext[BindingContext.CALL, jetElement]
val element = resolvedCall.call.calleeExpression ?: resolvedCall.call.callElement
val call = resolvedCall.call
fun isInvokeCall(): Boolean {
return call is CallTransformer.CallForImplicitInvoke
}
fun isInvokeCall(): Boolean = call is CallTransformer.CallForImplicitInvoke
fun isMultiDeclaration(): Boolean {
return (resolvedCall != null) && (call?.callElement is KtDestructuringDeclarationEntry)
}
fun isMultiDeclaration(): Boolean = call.callElement is KtDestructuringDeclarationEntry
fun isConventionOperator(): Boolean {
if (jetElement !is KtOperationReferenceExpression) return false
return jetElement.getNameForConventionalOperation() != null
}
fun isArrayAccessExpression() = jetElement is KtArrayAccessExpression
if (resolvedCall is VariableAsFunctionResolvedCall && call is CallTransformer.CallForImplicitInvoke && call.itIsVariableAsFunctionCall) {
if (resolvedCall is VariableAsFunctionResolvedCall &&
call is CallTransformer.CallForImplicitInvoke && call.itIsVariableAsFunctionCall) {
val outerCall = call.outerCall
if (isConventionCall(outerCall)) {
throw AssertionError("Illegal resolved call to variable with invoke for $outerCall. Variable: ${resolvedCall.variableCall.resultingDescriptor}")
throw AssertionError("Illegal resolved call to variable with invoke for $outerCall. " +
"Variable: ${resolvedCall.variableCall.resultingDescriptor}")
}
}
if (isMultiDeclaration() || isInvokeCall()) {
if (!functionDescriptor.isOperator && call != null) {
report(call.callElement, functionDescriptor, trace)
if (!functionDescriptor.isOperator) {
report(call.callElement, functionDescriptor, context.trace)
}
return
}
val isConventionOperator = isConventionOperator()
if (isConventionOperator || isArrayAccessExpression()) {
val isConventionOperator = element is KtOperationReferenceExpression && element.getNameForConventionalOperation() != null
if (isConventionOperator || element is KtArrayAccessExpression) {
if (!functionDescriptor.isOperator) {
report(jetElement, functionDescriptor, trace)
report(element, functionDescriptor, context.trace)
}
if (isConventionOperator && call != null) {
checkDeprecatedUnaryConventions(call, functionDescriptor, trace)
if (isConventionOperator) {
checkDeprecatedUnaryConventions(call, functionDescriptor, context.trace)
}
}
}
@@ -108,4 +97,4 @@ class OperatorValidator : SymbolUsageValidator {
return (!functionDescriptor.isDynamic() && !ErrorUtils.isError(functionDescriptor))
}
}
}
}
@@ -869,22 +869,11 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
* @return {@code true} iff expression can be assigned to
*/
public boolean checkLValue(
@NotNull BindingTrace trace,
@NotNull ExpressionTypingContext context,
@NotNull KtExpression expression,
@Nullable KtExpression rightHandSide,
@NotNull KtOperationExpression operationExpression
) {
return checkLValue(trace, context, expression, rightHandSide, operationExpression, false);
}
private boolean checkLValue(
@NotNull BindingTrace trace,
@NotNull ExpressionTypingContext context,
@NotNull KtExpression expressionWithParenthesis,
@Nullable KtExpression rightHandSide,
@NotNull KtOperationExpression operationExpression,
boolean canBeThis
@NotNull KtOperationExpression operationExpression
) {
KtExpression expression = KtPsiUtil.deparenthesize(expressionWithParenthesis);
if (expression instanceof KtArrayAccessExpression) {
@@ -901,17 +890,20 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|| operationType == KtTokens.PLUSPLUS || operationType == KtTokens.MINUSMINUS) {
ResolvedCall<?> resolvedCall = ignoreReportsTrace.get(INDEXED_LVALUE_SET, expression);
if (resolvedCall != null) {
CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
// 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
components.symbolUsageValidator.validateCall(resolvedCall, descriptor, trace, expression);
BasicCallResolutionContext callResolutionContext = BasicCallResolutionContext.create(
context.replaceBindingTrace(trace), resolvedCall.getCall(), CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS
);
for (CallChecker checker : components.callCheckers) {
checker.check(resolvedCall, callResolutionContext, components.languageFeatureSettings);
}
}
}
return info.getType() != null;
}
if (canBeThis && expression instanceof KtThisExpression) return true;
VariableDescriptor variable = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), expression, true);
boolean result = true;
@@ -27,11 +27,11 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticSink;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.Call;
import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.resolve.calls.checkers.OperatorCallChecker;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver;
import org.jetbrains.kotlin.resolve.validation.OperatorValidator;
import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator;
import org.jetbrains.kotlin.types.DynamicTypesKt;
import org.jetbrains.kotlin.types.ErrorUtils;
@@ -99,7 +99,7 @@ public class ForLoopConventionsChecker {
if ((extensionReceiverParameter != null) && (DynamicTypesKt.isDynamic(extensionReceiverParameter.getType()))) return;
if (!descriptor.isOperator()) {
OperatorValidator.Companion.report(expression, descriptor, sink);
OperatorCallChecker.Companion.report(expression, descriptor, sink);
}
}