Gradual migration of operator 'mod' to 'rem'

- Introduce new 'rem' operator convention
 - Prefer 'rem()' to 'mod()' when both are available, even if mod() is a
   member, and rem() -- an extension
 - Place operator 'rem' under the language feature
This commit is contained in:
Mikhail Zarechenskiy
2016-12-05 22:42:16 +03:00
parent 2df9daab1f
commit 97ca51381a
29 changed files with 528 additions and 28 deletions
@@ -1,21 +1,5 @@
/*
* 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.
*/
/*
* 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.
@@ -40,6 +24,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.types.expressions.OperatorConventions.REM_TO_MOD_OPERATION_NAMES
import org.jetbrains.kotlin.util.CheckResult
import org.jetbrains.kotlin.util.OperatorChecks
import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -57,10 +42,20 @@ object OperatorModifierChecker {
val checkResult = OperatorChecks.check(functionDescriptor)
if (checkResult.isSuccess) {
if (functionDescriptor.name in COROUTINE_OPERATOR_NAMES
&& !languageVersionSettings.supportsFeature(LanguageFeature.Coroutines)) {
diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(modifier, LanguageFeature.Coroutines))
when (functionDescriptor.name) {
in COROUTINE_OPERATOR_NAMES -> {
if (!languageVersionSettings.supportsFeature(LanguageFeature.Coroutines)) {
diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(modifier, LanguageFeature.Coroutines))
}
}
in REM_TO_MOD_OPERATION_NAMES.keys -> {
if (!languageVersionSettings.supportsFeature(LanguageFeature.OperatorRem)) {
diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(modifier, LanguageFeature.OperatorRem))
}
}
}
return
}
@@ -23,9 +23,9 @@ import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.coroutines.getExpectedTypeForCoroutineControllerHandleResult
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptorImpl
import org.jetbrains.kotlin.incremental.KotlinLookupLocation
import org.jetbrains.kotlin.lexer.KtToken
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.CallTransformer
@@ -45,6 +45,7 @@ import org.jetbrains.kotlin.resolve.scopes.utils.getImplicitReceiversHierarchy
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
import org.jetbrains.kotlin.types.typeUtil.contains
import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -127,6 +128,14 @@ fun isOrOverridesSynthesized(descriptor: CallableMemberDescriptor): Boolean {
return false
}
fun isBinaryRemOperator(call: Call): Boolean {
val callElement = call.callElement as? KtBinaryExpression ?: return false
val operator = callElement.operationToken
if (operator !is KtToken) return false
val name = OperatorConventions.getNameForOperationSymbol(operator, true, true)
return name in OperatorConventions.REM_TO_MOD_OPERATION_NAMES.keys
}
fun isConventionCall(call: Call): Boolean {
if (call is CallTransformer.CallForImplicitInvoke) return true
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
import org.jetbrains.kotlin.resolve.calls.CallTransformer
import org.jetbrains.kotlin.resolve.calls.CandidateResolver
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isBinaryRemOperator
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isConventionCall
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInfixCall
import org.jetbrains.kotlin.resolve.calls.callUtil.createLookupLocation
@@ -49,6 +50,7 @@ import org.jetbrains.kotlin.resolve.scopes.SyntheticScopes
import org.jetbrains.kotlin.resolve.scopes.receivers.*
import org.jetbrains.kotlin.types.DeferredType
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.types.isDynamic
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.check
@@ -159,7 +161,18 @@ class NewResolutionOldInference(
return allCandidatesResult(towerResolver.collectAllCandidates(scopeTower, processor))
}
val candidates = towerResolver.runResolve(scopeTower, processor, useOrder = kind != ResolutionKind.CallableReference)
var candidates = towerResolver.runResolve(scopeTower, processor, useOrder = kind != ResolutionKind.CallableReference)
// Temporary hack to resolve 'rem' as 'mod' if the first is do not present
val emptyOrInapplicableCandidates = candidates.isEmpty() ||
candidates.all {
it.candidateStatus.resultingApplicability == ResolutionCandidateApplicability.INAPPLICABLE
}
if (emptyOrInapplicableCandidates && isBinaryRemOperator(context.call)) {
val deprecatedName = OperatorConventions.REM_TO_MOD_OPERATION_NAMES[name]
val processorForDeprecatedName = kind.createTowerProcessor(this, deprecatedName!!, tracing, scopeTower, detailedReceiver, context)
candidates = towerResolver.runResolve(scopeTower, processorForDeprecatedName, useOrder = kind != ResolutionKind.CallableReference)
}
if (candidates.isEmpty()) {
if (reportAdditionalDiagnosticIfNoCandidates(context, name, kind, scopeTower, detailedReceiver)) {
@@ -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.
@@ -60,10 +60,15 @@ public class OperatorConventions {
.put(KtTokens.PLUS, PLUS)
.put(KtTokens.MINUS, MINUS)
.put(KtTokens.DIV, DIV)
.put(KtTokens.PERC, MOD)
.put(KtTokens.PERC, REM)
.put(KtTokens.RANGE, RANGE_TO)
.build();
public static final ImmutableBiMap<Name, Name> REM_TO_MOD_OPERATION_NAMES = ImmutableBiMap.<Name, Name>builder()
.put(REM, MOD)
.put(REM_ASSIGN, MOD_ASSIGN)
.build();
public static final ImmutableSet<KtSingleValueToken> NOT_OVERLOADABLE =
ImmutableSet.of(KtTokens.ANDAND, KtTokens.OROR, KtTokens.ELVIS, KtTokens.EQEQEQ, KtTokens.EXCLEQEQEQ);
@@ -85,7 +90,7 @@ public class OperatorConventions {
public static final ImmutableBiMap<KtSingleValueToken, Name> ASSIGNMENT_OPERATIONS = ImmutableBiMap.<KtSingleValueToken, Name>builder()
.put(KtTokens.MULTEQ, TIMES_ASSIGN)
.put(KtTokens.DIVEQ, DIV_ASSIGN)
.put(KtTokens.PERCEQ, MOD_ASSIGN)
.put(KtTokens.PERCEQ, REM_ASSIGN)
.put(KtTokens.PLUSEQ, PLUS_ASSIGN)
.put(KtTokens.MINUSEQ, MINUS_ASSIGN)
.build();