Allow contracts on final non-override members since 1.4

This commit is contained in:
Dmitriy Novozhilov
2019-12-26 11:53:27 +03:00
parent 5a9070b6da
commit f083edfac2
34 changed files with 682 additions and 45 deletions
@@ -23,7 +23,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
import org.jetbrains.kotlin.config.AnalysisFlags
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.contracts.description.ContractProviderKey
@@ -259,8 +258,9 @@ class FunctionDescriptorResolver(
if (function !is KtNamedFunction) return null
val isContractsEnabled = languageVersionSettings.supportsFeature(LanguageFeature.AllowContractsForCustomFunctions)
val isAllowedOnMembers = languageVersionSettings.supportsFeature(LanguageFeature.AllowContractsForNonOverridableMembers)
if (!isContractsEnabled || !function.mayHaveContract()) return null
if (!isContractsEnabled || !function.mayHaveContract(isAllowedOnMembers)) return null
return LazyContractProvider(storageManager) {
AstLoadingFilter.forceAllowTreeLoading(function.containingFile, ThrowableComputable {
@@ -6,11 +6,9 @@
package org.jetbrains.kotlin.resolve.calls.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.contracts.parsing.isContractCallDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
import org.jetbrains.kotlin.descriptors.isOverridable
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.psiUtil.isFirstStatement
@@ -25,6 +23,8 @@ object ContractNotAllowedCallChecker : CallChecker {
val descriptor = resolvedCall.resultingDescriptor as? FunctionDescriptor ?: return
if (!descriptor.isContractCallDescriptor()) return
val allowedOnMembers = context.languageVersionSettings.supportsFeature(LanguageFeature.AllowContractsForNonOverridableMembers)
val callElement = resolvedCall.call.callElement
var hasErrors = false
@@ -40,7 +40,24 @@ object ContractNotAllowedCallChecker : CallChecker {
contractNotAllowed("Contracts are allowed only for functions")
var inFunctionBodyBlock = true
if (scope.ownerDescriptor.containingDeclaration !is PackageFragmentDescriptor
val declarationOwner = scope.ownerDescriptor.containingDeclaration
val acceptableParent = if (allowedOnMembers) {
var owner = declarationOwner
var result = true
while (owner !is PackageFragmentDescriptor) {
if (owner !is ClassDescriptor) {
result = false
break
}
owner = owner.containingDeclaration
}
result
} else {
declarationOwner is PackageFragmentDescriptor
}
if (!acceptableParent
|| scope.kind != LexicalScopeKind.CODE_BLOCK
|| (scope.parent as? LexicalScope)?.kind != LexicalScopeKind.FUNCTION_INNER_SCOPE
) {
@@ -48,13 +65,23 @@ object ContractNotAllowedCallChecker : CallChecker {
contractNotAllowed("Contracts are allowed only in function body block")
inFunctionBodyBlock = false
} else {
contractNotAllowed("Contracts are allowed only for top-level functions")
val message = if (allowedOnMembers)
"Contracts are allowed only for functions"
else
"Contracts are allowed only for top-level functions"
contractNotAllowed(message)
}
}
if (functionDescriptor?.isOperator == true) contractNotAllowed("Contracts are not allowed for operator functions")
if (functionDescriptor?.isOverridable == true) contractNotAllowed("Contracts are not allowed for open functions")
if (!allowedOnMembers && functionDescriptor?.isOverridable == true) {
contractNotAllowed("Contracts are not allowed for open functions")
}
if (allowedOnMembers && functionDescriptor?.isOverridableOrOverrides == true) {
contractNotAllowed("Contracts are not allowed for open or override functions")
}
if (!callElement.isFirstStatement() && inFunctionBodyBlock) {
contractNotAllowed("Contract should be the first statement")