From 638eb28692b4ef554dc15471b23e4672f23c0730 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Wed, 30 Aug 2017 13:56:58 +0300 Subject: [PATCH] Unify code for lateinit properties and local variables checking --- .../kotlin/resolve/DeclarationsChecker.kt | 56 +---------- .../LateinitModifierApplicabilityChecker.kt | 93 +++++++++++++++++++ .../kotlin/resolve/LocalVariableResolver.kt | 26 +----- 3 files changed, 97 insertions(+), 78 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/LateinitModifierApplicabilityChecker.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt index ce2e55bb808..86a70ef77b5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt @@ -441,7 +441,7 @@ class DeclarationsChecker( for (parameter in declaration.valueParameters) { trace.get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter)?.let { modifiersChecker.checkModifiersForDeclaration(parameter, it) - checkPropertyLateInit(parameter, it) + LateinitModifierApplicabilityChecker.checkLateinitModifierApplicability(trace, parameter, it) } } @@ -539,7 +539,7 @@ class DeclarationsChecker( if (containingDeclaration is ClassDescriptor) { checkMemberProperty(property, propertyDescriptor, containingDeclaration) } - checkPropertyLateInit(property, propertyDescriptor) + LateinitModifierApplicabilityChecker.checkLateinitModifierApplicability(trace, property, propertyDescriptor) checkPropertyInitializer(property, propertyDescriptor) checkAccessors(property, propertyDescriptor) checkTypeParameterConstraints(property) @@ -575,56 +575,6 @@ class DeclarationsChecker( } } - private fun checkPropertyLateInit(property: KtCallableDeclaration, propertyDescriptor: PropertyDescriptor) { - val modifier = property.modifierList?.getModifier(KtTokens.LATEINIT_KEYWORD) ?: return - - if (!propertyDescriptor.isVar) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is allowed only on mutable properties")) - } - - val returnType = propertyDescriptor.type - - if (TypeUtils.isNullableType(returnType)) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on nullable properties")) - } - - if (KotlinBuiltIns.isPrimitiveType(returnType)) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on primitive type properties")) - } - - val isAbstract = propertyDescriptor.modality == Modality.ABSTRACT - if (isAbstract) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on abstract properties")) - } - - if (property is KtParameter) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on primary constructor parameters")) - } - - var hasDelegateExpressionOrInitializer = false - if (property is KtProperty && property.hasDelegateExpressionOrInitializer()) { - hasDelegateExpressionOrInitializer = true - trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, - "is not allowed on properties with initializer or on delegated properties")) - } - - val hasAccessorImplementation = propertyDescriptor.hasAccessorImplementation() - - if (!hasDelegateExpressionOrInitializer && hasAccessorImplementation) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on properties with a custom getter or setter")) - } - - val hasBackingField = trace.bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor) ?: false - - if (!isAbstract && !hasAccessorImplementation && !hasDelegateExpressionOrInitializer && !hasBackingField) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on properties without backing field")) - } - - if (propertyDescriptor.extensionReceiverParameter != null) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on extension properties")) - } - } - private fun checkMemberProperty( property: KtProperty, propertyDescriptor: PropertyDescriptor, @@ -967,7 +917,7 @@ class DeclarationsChecker( return !modifierList.hasModifier(KtTokens.OVERRIDE_KEYWORD) } - private fun PropertyDescriptor.hasAccessorImplementation(): Boolean { + fun PropertyDescriptor.hasAccessorImplementation(): Boolean { getter?.let { if (it.hasBody()) return true } setter?.let { if (it.hasBody()) return true } return false diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/LateinitModifierApplicabilityChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/LateinitModifierApplicabilityChecker.kt new file mode 100644 index 00000000000..5c1d0da97fa --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/LateinitModifierApplicabilityChecker.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2010-2017 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. + */ + +package org.jetbrains.kotlin.resolve + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtCallableDeclaration +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.resolve.DeclarationsChecker.Companion.hasAccessorImplementation +import org.jetbrains.kotlin.types.TypeUtils + +object LateinitModifierApplicabilityChecker { + fun checkLateinitModifierApplicability(trace: BindingTrace, ktDeclaration: KtCallableDeclaration, descriptor: VariableDescriptor) { + val modifier = ktDeclaration.modifierList?.getModifier(KtTokens.LATEINIT_KEYWORD) ?: return + + val variables = when (descriptor) { + is PropertyDescriptor -> "properties" + is LocalVariableDescriptor -> "local variables" + else -> throw AssertionError("Should be a property or a local variable: $descriptor") + } + + val type = descriptor.type + + if (!descriptor.isVar) { + trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is allowed only on mutable $variables")) + } + + if (TypeUtils.isNullableType(type)) { + trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on $variables of nullable types")) + } + + if (KotlinBuiltIns.isPrimitiveType(type)) { + trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on $variables of primitive types")) + } + + if (ktDeclaration is KtProperty) { + if (ktDeclaration.hasDelegateExpression()) { + trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on delegated properties")) + } + else if (ktDeclaration.hasInitializer()) { + trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on $variables with initializer")) + } + } + + if (descriptor is PropertyDescriptor) { + val isAbstract = descriptor.modality == Modality.ABSTRACT + val hasDelegateExpressionOrInitializer = ktDeclaration is KtProperty && ktDeclaration.hasDelegateExpressionOrInitializer() + val hasAccessorImplementation = descriptor.hasAccessorImplementation() + val hasBackingField = trace.bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor) ?: false + + if (ktDeclaration is KtParameter) { + trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on primary constructor parameters")) + } + + if (isAbstract) { + trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on abstract properties")) + } + + if (!hasDelegateExpressionOrInitializer) { + if (hasAccessorImplementation) { + trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on properties with a custom getter or setter")) + } + else if (!isAbstract && !hasBackingField) { + trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on properties without backing field")) + } + } + + if (descriptor.extensionReceiverParameter != null) { + trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on extension properties")) + } + } + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/LocalVariableResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/LocalVariableResolver.kt index f4575653341..3f77b3fb467 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/LocalVariableResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/LocalVariableResolver.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.resolve -import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.* @@ -34,7 +33,6 @@ import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.source.toSourceElement import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.expressions.* import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo @@ -131,29 +129,7 @@ class LocalVariableResolver( modifiersChecker.withTrace(context.trace).checkModifiersForLocalDeclaration(ktProperty, descriptor) identifierChecker.checkDeclaration(ktProperty, context.trace) - checkLocalVariableLateinit(context.trace, descriptor, ktProperty) - } - - private fun checkLocalVariableLateinit(trace: BindingTrace, descriptor: VariableDescriptor, ktProperty: KtProperty) { - val modifierList = ktProperty.modifierList ?: return - val modifier = modifierList.getModifier(KtTokens.LATEINIT_KEYWORD) ?: return - val returnType = descriptor.type - - if (!descriptor.isVar) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is allowed only on mutable local variables")) - } - - if (TypeUtils.isNullableType(returnType)) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on local variables of nullable types")) - } - - if (KotlinBuiltIns.isPrimitiveType(returnType)) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on local variables of primitive types")) - } - - if (ktProperty.hasDelegateExpressionOrInitializer()) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on variables with initializer or on local delegated properties")) - } + LateinitModifierApplicabilityChecker.checkLateinitModifierApplicability(context.trace, ktProperty, descriptor) } private fun resolveLocalVariableDescriptor(