From bbf9bf91fc3abb2ac196d672fa1e3801e7f4e355 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Fri, 2 Jun 2017 12:07:45 +0300 Subject: [PATCH] Check language feature support for local and top-level lateinit vars --- .../kotlin/resolve/ModifiersChecker.kt | 48 ++++++++++--------- .../lateinit/local/capturedLocalLateinit.kt | 2 + .../lateinit/local/localLateinit.kt | 2 + .../uninitializedCapturedMemberAccess.kt | 1 + .../local/uninitializedCapturedRead.kt | 1 + .../local/uninitializedMemberAccess.kt | 1 + .../lateinit/local/uninitializedRead.kt | 1 + .../lateinit/topLevel/accessorException.kt | 1 + .../topLevel/accessorForTopLevelLateinit.kt | 2 + .../lateinit/topLevel/topLevelLateinit.kt | 2 + .../topLevel/uninitializedMemberAccess.kt | 1 + .../lateinit/topLevel/uninitializedRead.kt | 1 + .../tests/lateinit/modifierApplicability.kt | 6 +-- .../tests/properties/lateinitOnTopLevel.kt | 2 + .../inapplicableLateinitModifier.kt | 1 + .../properties/localLateinit/localLateinit.kt | 2 + .../properties/localLateinit/uninitialized.kt | 2 + .../kotlin/config/LanguageVersionSettings.kt | 2 + .../idea/completion/KeywordCompletion.kt | 37 +++++++++++--- 19 files changed, 83 insertions(+), 32 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt index 0c44ece2ff0..97cce886e34 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt @@ -91,14 +91,17 @@ object ModifierCheckerCore { ) private val featureDependencies = mapOf( - SUSPEND_KEYWORD to LanguageFeature.Coroutines, - INLINE_KEYWORD to LanguageFeature.InlineProperties, - HEADER_KEYWORD to LanguageFeature.MultiPlatformProjects, - IMPL_KEYWORD to LanguageFeature.MultiPlatformProjects + SUSPEND_KEYWORD to listOf(LanguageFeature.Coroutines), + INLINE_KEYWORD to listOf(LanguageFeature.InlineProperties), + HEADER_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects), + IMPL_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects), + LATEINIT_KEYWORD to listOf(LanguageFeature.LateinitTopLevelProperties, LanguageFeature.LateinitLocalVariables) ) private val featureDependenciesTargets = mapOf( - LanguageFeature.InlineProperties to setOf(PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER) + LanguageFeature.InlineProperties to setOf(PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER), + LanguageFeature.LateinitLocalVariables to setOf(LOCAL_VARIABLE), + LanguageFeature.LateinitTopLevelProperties to setOf(TOP_LEVEL_PROPERTY) ) // NOTE: deprecated targets must be possible! @@ -272,28 +275,29 @@ object ModifierCheckerCore { ): Boolean { val modifier = node.elementType as KtModifierKeywordToken - val dependency = featureDependencies[modifier] ?: return true + val dependencies = featureDependencies[modifier] ?: return true + for (dependency in dependencies) { + val featureSupport = languageVersionSettings.getFeatureSupport(dependency) - val featureSupport = languageVersionSettings.getFeatureSupport(dependency) + val diagnosticData = dependency to languageVersionSettings + if (featureSupport == LanguageFeature.State.ENABLED_WITH_ERROR || featureSupport == LanguageFeature.State.DISABLED) { + val restrictedTargets = featureDependenciesTargets[dependency] + if (restrictedTargets != null && actualTargets.intersect(restrictedTargets).isEmpty()) { + continue + } - val diagnosticData = dependency to languageVersionSettings - if (featureSupport == LanguageFeature.State.ENABLED_WITH_ERROR || featureSupport == LanguageFeature.State.DISABLED) { - val restrictedTargets = featureDependenciesTargets[dependency] - if (restrictedTargets != null && actualTargets.intersect(restrictedTargets).isEmpty()) { - return true + if (featureSupport == LanguageFeature.State.DISABLED) { + trace.report(Errors.UNSUPPORTED_FEATURE.on(node.psi, diagnosticData)) + } + else { + trace.report(Errors.EXPERIMENTAL_FEATURE_ERROR.on(node.psi, diagnosticData)) + } + return false } - if (featureSupport == LanguageFeature.State.DISABLED) { - trace.report(Errors.UNSUPPORTED_FEATURE.on(node.psi, diagnosticData)) + if (featureSupport == LanguageFeature.State.ENABLED_WITH_WARNING) { + trace.report(Errors.EXPERIMENTAL_FEATURE_WARNING.on(node.psi, diagnosticData)) } - else { - trace.report(Errors.EXPERIMENTAL_FEATURE_ERROR.on(node.psi, diagnosticData)) - } - return false - } - - if (featureSupport == LanguageFeature.State.ENABLED_WITH_WARNING) { - trace.report(Errors.EXPERIMENTAL_FEATURE_WARNING.on(node.psi, diagnosticData)) } return true diff --git a/compiler/testData/codegen/box/properties/lateinit/local/capturedLocalLateinit.kt b/compiler/testData/codegen/box/properties/lateinit/local/capturedLocalLateinit.kt index 54ba294cd24..03507ac8f5a 100644 --- a/compiler/testData/codegen/box/properties/lateinit/local/capturedLocalLateinit.kt +++ b/compiler/testData/codegen/box/properties/lateinit/local/capturedLocalLateinit.kt @@ -1,3 +1,5 @@ +// LANGUAGE_VERSION: 1.2 + fun runNoInline(f: () -> Unit) = f() fun box(): String { diff --git a/compiler/testData/codegen/box/properties/lateinit/local/localLateinit.kt b/compiler/testData/codegen/box/properties/lateinit/local/localLateinit.kt index 638970dc82f..bb68d6f3f3e 100644 --- a/compiler/testData/codegen/box/properties/lateinit/local/localLateinit.kt +++ b/compiler/testData/codegen/box/properties/lateinit/local/localLateinit.kt @@ -1,3 +1,5 @@ +// LANGUAGE_VERSION: 1.2 + fun box(): String { lateinit var ok: String run { diff --git a/compiler/testData/codegen/box/properties/lateinit/local/uninitializedCapturedMemberAccess.kt b/compiler/testData/codegen/box/properties/lateinit/local/uninitializedCapturedMemberAccess.kt index 485ec0bd1bf..6b7bbc05359 100644 --- a/compiler/testData/codegen/box/properties/lateinit/local/uninitializedCapturedMemberAccess.kt +++ b/compiler/testData/codegen/box/properties/lateinit/local/uninitializedCapturedMemberAccess.kt @@ -1,3 +1,4 @@ +// LANGUAGE_VERSION: 1.2 // WITH_REFLECT // IGNORE_BACKEND: JS, NATIVE diff --git a/compiler/testData/codegen/box/properties/lateinit/local/uninitializedCapturedRead.kt b/compiler/testData/codegen/box/properties/lateinit/local/uninitializedCapturedRead.kt index 96bed50b39d..17fe9e30fcf 100644 --- a/compiler/testData/codegen/box/properties/lateinit/local/uninitializedCapturedRead.kt +++ b/compiler/testData/codegen/box/properties/lateinit/local/uninitializedCapturedRead.kt @@ -1,3 +1,4 @@ +// LANGUAGE_VERSION: 1.2 // WITH_REFLECT // IGNORE_BACKEND: JS, NATIVE diff --git a/compiler/testData/codegen/box/properties/lateinit/local/uninitializedMemberAccess.kt b/compiler/testData/codegen/box/properties/lateinit/local/uninitializedMemberAccess.kt index fcd700bcd54..108f00e307d 100644 --- a/compiler/testData/codegen/box/properties/lateinit/local/uninitializedMemberAccess.kt +++ b/compiler/testData/codegen/box/properties/lateinit/local/uninitializedMemberAccess.kt @@ -1,3 +1,4 @@ +// LANGUAGE_VERSION: 1.2 // WITH_REFLECT // IGNORE_BACKEND: JS, NATIVE diff --git a/compiler/testData/codegen/box/properties/lateinit/local/uninitializedRead.kt b/compiler/testData/codegen/box/properties/lateinit/local/uninitializedRead.kt index 46cfd707616..13ec44dd760 100644 --- a/compiler/testData/codegen/box/properties/lateinit/local/uninitializedRead.kt +++ b/compiler/testData/codegen/box/properties/lateinit/local/uninitializedRead.kt @@ -1,3 +1,4 @@ +// LANGUAGE_VERSION: 1.2 // WITH_REFLECT // IGNORE_BACKEND: JS, NATIVE diff --git a/compiler/testData/codegen/box/properties/lateinit/topLevel/accessorException.kt b/compiler/testData/codegen/box/properties/lateinit/topLevel/accessorException.kt index 18c498ecbd7..3ee4f2a93c2 100644 --- a/compiler/testData/codegen/box/properties/lateinit/topLevel/accessorException.kt +++ b/compiler/testData/codegen/box/properties/lateinit/topLevel/accessorException.kt @@ -1,3 +1,4 @@ +// LANGUAGE_VERSION: 1.2 // WITH_REFLECT // IGNORE_BACKEND: JS, NATIVE // FILE: lateinit.kt diff --git a/compiler/testData/codegen/box/properties/lateinit/topLevel/accessorForTopLevelLateinit.kt b/compiler/testData/codegen/box/properties/lateinit/topLevel/accessorForTopLevelLateinit.kt index 251409c9e5c..aca5474f004 100644 --- a/compiler/testData/codegen/box/properties/lateinit/topLevel/accessorForTopLevelLateinit.kt +++ b/compiler/testData/codegen/box/properties/lateinit/topLevel/accessorForTopLevelLateinit.kt @@ -1,3 +1,5 @@ +// LANGUAGE_VERSION: 1.2 + // FILE: lateinit.kt private lateinit var s: String diff --git a/compiler/testData/codegen/box/properties/lateinit/topLevel/topLevelLateinit.kt b/compiler/testData/codegen/box/properties/lateinit/topLevel/topLevelLateinit.kt index 9a07c673ee0..2eb9c779585 100644 --- a/compiler/testData/codegen/box/properties/lateinit/topLevel/topLevelLateinit.kt +++ b/compiler/testData/codegen/box/properties/lateinit/topLevel/topLevelLateinit.kt @@ -1,3 +1,5 @@ +// LANGUAGE_VERSION: 1.2 + lateinit var ok: String fun box(): String { diff --git a/compiler/testData/codegen/box/properties/lateinit/topLevel/uninitializedMemberAccess.kt b/compiler/testData/codegen/box/properties/lateinit/topLevel/uninitializedMemberAccess.kt index 30c7ec622bf..a53e3981d63 100644 --- a/compiler/testData/codegen/box/properties/lateinit/topLevel/uninitializedMemberAccess.kt +++ b/compiler/testData/codegen/box/properties/lateinit/topLevel/uninitializedMemberAccess.kt @@ -1,3 +1,4 @@ +// LANGUAGE_VERSION: 1.2 // WITH_REFLECT // IGNORE_BACKEND: JS, NATIVE diff --git a/compiler/testData/codegen/box/properties/lateinit/topLevel/uninitializedRead.kt b/compiler/testData/codegen/box/properties/lateinit/topLevel/uninitializedRead.kt index 594dd3ccd9d..66b1eeffadd 100644 --- a/compiler/testData/codegen/box/properties/lateinit/topLevel/uninitializedRead.kt +++ b/compiler/testData/codegen/box/properties/lateinit/topLevel/uninitializedRead.kt @@ -1,3 +1,4 @@ +// LANGUAGE_VERSION: 1.2 // WITH_REFLECT // IGNORE_BACKEND: JS, NATIVE diff --git a/compiler/testData/diagnostics/tests/lateinit/modifierApplicability.kt b/compiler/testData/diagnostics/tests/lateinit/modifierApplicability.kt index b366d0e4054..b14bc48868d 100644 --- a/compiler/testData/diagnostics/tests/lateinit/modifierApplicability.kt +++ b/compiler/testData/diagnostics/tests/lateinit/modifierApplicability.kt @@ -19,7 +19,7 @@ public abstract class A(la private set fun a() { - lateinit var a: String + lateinit var a: String } lateinit var e1: V @@ -46,8 +46,8 @@ public abstract class A(la lateinit var String.e12: String } -lateinit val topLevel: String -lateinit var topLevelMutable: String +lateinit val topLevel: String +lateinit var topLevelMutable: String public interface Intf { lateinit var str: String diff --git a/compiler/testData/diagnostics/tests/properties/lateinitOnTopLevel.kt b/compiler/testData/diagnostics/tests/properties/lateinitOnTopLevel.kt index 754b4effd19..3b3153be226 100644 --- a/compiler/testData/diagnostics/tests/properties/lateinitOnTopLevel.kt +++ b/compiler/testData/diagnostics/tests/properties/lateinitOnTopLevel.kt @@ -1,3 +1,5 @@ +// !LANGUAGE: +LateinitTopLevelProperties + object Delegate { operator fun getValue(instance: Any?, property: Any) : String = "" operator fun setValue(instance: Any?, property: Any, value: String) {} diff --git a/compiler/testData/diagnostics/tests/properties/localLateinit/inapplicableLateinitModifier.kt b/compiler/testData/diagnostics/tests/properties/localLateinit/inapplicableLateinitModifier.kt index 3e767514fc8..6aa250c0593 100644 --- a/compiler/testData/diagnostics/tests/properties/localLateinit/inapplicableLateinitModifier.kt +++ b/compiler/testData/diagnostics/tests/properties/localLateinit/inapplicableLateinitModifier.kt @@ -1,4 +1,5 @@ // !DIAGNOSTICS: -UNUSED_VALUE -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE +// !LANGUAGE: +LateinitLocalVariables import kotlin.reflect.KProperty diff --git a/compiler/testData/diagnostics/tests/properties/localLateinit/localLateinit.kt b/compiler/testData/diagnostics/tests/properties/localLateinit/localLateinit.kt index ed402a1eb64..9586a34dfca 100644 --- a/compiler/testData/diagnostics/tests/properties/localLateinit/localLateinit.kt +++ b/compiler/testData/diagnostics/tests/properties/localLateinit/localLateinit.kt @@ -1,3 +1,5 @@ +// !LANGUAGE: +LateinitLocalVariables + fun test() { lateinit var s: String s = "" diff --git a/compiler/testData/diagnostics/tests/properties/localLateinit/uninitialized.kt b/compiler/testData/diagnostics/tests/properties/localLateinit/uninitialized.kt index da073e7cd81..090a7d0cce9 100644 --- a/compiler/testData/diagnostics/tests/properties/localLateinit/uninitialized.kt +++ b/compiler/testData/diagnostics/tests/properties/localLateinit/uninitialized.kt @@ -1,3 +1,5 @@ +// !LANGUAGE: +LateinitLocalVariables + fun test1() { lateinit var s: String s.length diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index b21ab4d982d..000825d64b6 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -57,6 +57,8 @@ enum class LanguageFeature( SafeCastCheckBoundSmartCasts(KOTLIN_1_2), BooleanElvisBoundSmartCasts(KOTLIN_1_2), CapturedInClosureSmartCasts(KOTLIN_1_2), + LateinitTopLevelProperties(KOTLIN_1_2), + LateinitLocalVariables(KOTLIN_1_2), // Experimental features diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KeywordCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KeywordCompletion.kt index dab9418550e..aa46a3fe0ed 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KeywordCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KeywordCompletion.kt @@ -29,6 +29,7 @@ import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.* @@ -328,6 +329,9 @@ object KeywordCompletion { fun isKeywordCorrectlyApplied(keywordTokenType: KtKeywordToken, file: KtFile): Boolean { val elementAt = file.findElementAt(prefixText.length)!! + val languageVersionSettings = ModuleUtilCore.findModuleForPsiElement(position)?.languageVersionSettings + ?: LanguageVersionSettingsImpl.DEFAULT + when { !elementAt.node!!.elementType.matchesKeyword(keywordTokenType) -> return false @@ -335,7 +339,7 @@ object KeywordCompletion { isErrorElementBefore(elementAt) -> return false - !isSupportedAtLanguageLevel(keywordTokenType, position) -> return false + !isModifierSupportedAtLanguageLevel(keywordTokenType, languageVersionSettings) -> return false keywordTokenType !is KtModifierKeywordToken -> return true @@ -358,10 +362,13 @@ object KeywordCompletion { is KtFile -> listOf(CLASS_ONLY, INTERFACE, OBJECT, ENUM_CLASS, ANNOTATION_CLASS, TOP_LEVEL_FUNCTION, TOP_LEVEL_PROPERTY, FUNCTION, PROPERTY) - else -> null + else -> listOf() } - val modifierTargets = ModifierCheckerCore.possibleTargetMap[keywordTokenType] - if (modifierTargets != null && possibleTargets != null && possibleTargets.none { it in modifierTargets }) return false + val modifierTargets = ModifierCheckerCore.possibleTargetMap[keywordTokenType]?.intersect(possibleTargets) + if (modifierTargets != null && possibleTargets.isNotEmpty() && + !modifierTargets.any { + isModifierTargetSupportedAtLanguageLevel(keywordTokenType, it, languageVersionSettings) + }) return false val ownerDeclaration = container?.getParentOfType(strict = true) val parentTarget = when (ownerDeclaration) { @@ -416,9 +423,7 @@ object KeywordCompletion { } } - private fun isSupportedAtLanguageLevel(keyword: KtKeywordToken, position: PsiElement): Boolean { - val languageVersionSettings = ModuleUtilCore.findModuleForPsiElement(position)?.languageVersionSettings - ?: LanguageVersionSettingsImpl.DEFAULT + private fun isModifierSupportedAtLanguageLevel(keyword: KtKeywordToken, languageVersionSettings: LanguageVersionSettings): Boolean { val feature = when (keyword) { KtTokens.TYPE_ALIAS_KEYWORD -> LanguageFeature.TypeAliases KtTokens.HEADER_KEYWORD, KtTokens.IMPL_KEYWORD -> LanguageFeature.MultiPlatformProjects @@ -428,6 +433,24 @@ object KeywordCompletion { return languageVersionSettings.supportsFeature(feature) } + private fun isModifierTargetSupportedAtLanguageLevel( + keyword: KtKeywordToken, + target: KotlinTarget, + languageVersionSettings: LanguageVersionSettings + ): Boolean { + if (keyword == KtTokens.LATEINIT_KEYWORD) { + val feature = when (target) { + TOP_LEVEL_PROPERTY -> LanguageFeature.LateinitTopLevelProperties + LOCAL_VARIABLE -> LanguageFeature.LateinitLocalVariables + else -> return true + } + return languageVersionSettings.supportsFeature(feature) + } + else { + return true + } + } + // builds text within scope (or from the start of the file) before position element excluding almost all declarations private fun buildReducedContextBefore(builder: StringBuilder, position: PsiElement, scope: PsiElement?) { if (position == scope) return