Check language feature support for local and top-level lateinit vars
This commit is contained in:
@@ -91,14 +91,17 @@ object ModifierCheckerCore {
|
|||||||
)
|
)
|
||||||
|
|
||||||
private val featureDependencies = mapOf(
|
private val featureDependencies = mapOf(
|
||||||
SUSPEND_KEYWORD to LanguageFeature.Coroutines,
|
SUSPEND_KEYWORD to listOf(LanguageFeature.Coroutines),
|
||||||
INLINE_KEYWORD to LanguageFeature.InlineProperties,
|
INLINE_KEYWORD to listOf(LanguageFeature.InlineProperties),
|
||||||
HEADER_KEYWORD to LanguageFeature.MultiPlatformProjects,
|
HEADER_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects),
|
||||||
IMPL_KEYWORD to LanguageFeature.MultiPlatformProjects
|
IMPL_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects),
|
||||||
|
LATEINIT_KEYWORD to listOf(LanguageFeature.LateinitTopLevelProperties, LanguageFeature.LateinitLocalVariables)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val featureDependenciesTargets = mapOf(
|
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!
|
// NOTE: deprecated targets must be possible!
|
||||||
@@ -272,28 +275,29 @@ object ModifierCheckerCore {
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
val modifier = node.elementType as KtModifierKeywordToken
|
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.DISABLED) {
|
||||||
if (featureSupport == LanguageFeature.State.ENABLED_WITH_ERROR || featureSupport == LanguageFeature.State.DISABLED) {
|
trace.report(Errors.UNSUPPORTED_FEATURE.on(node.psi, diagnosticData))
|
||||||
val restrictedTargets = featureDependenciesTargets[dependency]
|
}
|
||||||
if (restrictedTargets != null && actualTargets.intersect(restrictedTargets).isEmpty()) {
|
else {
|
||||||
return true
|
trace.report(Errors.EXPERIMENTAL_FEATURE_ERROR.on(node.psi, diagnosticData))
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (featureSupport == LanguageFeature.State.DISABLED) {
|
if (featureSupport == LanguageFeature.State.ENABLED_WITH_WARNING) {
|
||||||
trace.report(Errors.UNSUPPORTED_FEATURE.on(node.psi, diagnosticData))
|
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
|
return true
|
||||||
|
|||||||
+2
@@ -1,3 +1,5 @@
|
|||||||
|
// LANGUAGE_VERSION: 1.2
|
||||||
|
|
||||||
fun runNoInline(f: () -> Unit) = f()
|
fun runNoInline(f: () -> Unit) = f()
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// LANGUAGE_VERSION: 1.2
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
lateinit var ok: String
|
lateinit var ok: String
|
||||||
run {
|
run {
|
||||||
|
|||||||
Vendored
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// LANGUAGE_VERSION: 1.2
|
||||||
// WITH_REFLECT
|
// WITH_REFLECT
|
||||||
// IGNORE_BACKEND: JS, NATIVE
|
// IGNORE_BACKEND: JS, NATIVE
|
||||||
|
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// LANGUAGE_VERSION: 1.2
|
||||||
// WITH_REFLECT
|
// WITH_REFLECT
|
||||||
// IGNORE_BACKEND: JS, NATIVE
|
// IGNORE_BACKEND: JS, NATIVE
|
||||||
|
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// LANGUAGE_VERSION: 1.2
|
||||||
// WITH_REFLECT
|
// WITH_REFLECT
|
||||||
// IGNORE_BACKEND: JS, NATIVE
|
// IGNORE_BACKEND: JS, NATIVE
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// LANGUAGE_VERSION: 1.2
|
||||||
// WITH_REFLECT
|
// WITH_REFLECT
|
||||||
// IGNORE_BACKEND: JS, NATIVE
|
// IGNORE_BACKEND: JS, NATIVE
|
||||||
|
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// LANGUAGE_VERSION: 1.2
|
||||||
// WITH_REFLECT
|
// WITH_REFLECT
|
||||||
// IGNORE_BACKEND: JS, NATIVE
|
// IGNORE_BACKEND: JS, NATIVE
|
||||||
// FILE: lateinit.kt
|
// FILE: lateinit.kt
|
||||||
|
|||||||
+2
@@ -1,3 +1,5 @@
|
|||||||
|
// LANGUAGE_VERSION: 1.2
|
||||||
|
|
||||||
// FILE: lateinit.kt
|
// FILE: lateinit.kt
|
||||||
private lateinit var s: String
|
private lateinit var s: String
|
||||||
|
|
||||||
|
|||||||
+2
@@ -1,3 +1,5 @@
|
|||||||
|
// LANGUAGE_VERSION: 1.2
|
||||||
|
|
||||||
lateinit var ok: String
|
lateinit var ok: String
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// LANGUAGE_VERSION: 1.2
|
||||||
// WITH_REFLECT
|
// WITH_REFLECT
|
||||||
// IGNORE_BACKEND: JS, NATIVE
|
// IGNORE_BACKEND: JS, NATIVE
|
||||||
|
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// LANGUAGE_VERSION: 1.2
|
||||||
// WITH_REFLECT
|
// WITH_REFLECT
|
||||||
// IGNORE_BACKEND: JS, NATIVE
|
// IGNORE_BACKEND: JS, NATIVE
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public abstract class A<T: Any, V: String?>(<!INAPPLICABLE_LATEINIT_MODIFIER!>la
|
|||||||
private set
|
private set
|
||||||
|
|
||||||
fun a() {
|
fun a() {
|
||||||
lateinit var <!UNUSED_VARIABLE!>a<!>: String
|
<!UNSUPPORTED_FEATURE!>lateinit<!> var <!UNUSED_VARIABLE!>a<!>: String
|
||||||
}
|
}
|
||||||
|
|
||||||
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> var e1: V
|
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> var e1: V
|
||||||
@@ -46,8 +46,8 @@ public abstract class A<T: Any, V: String?>(<!INAPPLICABLE_LATEINIT_MODIFIER!>la
|
|||||||
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> var String.e12: String
|
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> var String.e12: String
|
||||||
}
|
}
|
||||||
|
|
||||||
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> val topLevel: String
|
<!INAPPLICABLE_LATEINIT_MODIFIER, UNSUPPORTED_FEATURE!>lateinit<!> val topLevel: String
|
||||||
lateinit var topLevelMutable: String
|
<!UNSUPPORTED_FEATURE!>lateinit<!> var topLevelMutable: String
|
||||||
|
|
||||||
public interface Intf {
|
public interface Intf {
|
||||||
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> var str: String
|
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> var str: String
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// !LANGUAGE: +LateinitTopLevelProperties
|
||||||
|
|
||||||
object Delegate {
|
object Delegate {
|
||||||
operator fun getValue(instance: Any?, property: Any) : String = ""
|
operator fun getValue(instance: Any?, property: Any) : String = ""
|
||||||
operator fun setValue(instance: Any?, property: Any, value: String) {}
|
operator fun setValue(instance: Any?, property: Any, value: String) {}
|
||||||
|
|||||||
Vendored
+1
@@ -1,4 +1,5 @@
|
|||||||
// !DIAGNOSTICS: -UNUSED_VALUE -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE
|
// !DIAGNOSTICS: -UNUSED_VALUE -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE
|
||||||
|
// !LANGUAGE: +LateinitLocalVariables
|
||||||
|
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// !LANGUAGE: +LateinitLocalVariables
|
||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
lateinit var s: String
|
lateinit var s: String
|
||||||
s = ""
|
s = ""
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// !LANGUAGE: +LateinitLocalVariables
|
||||||
|
|
||||||
fun test1() {
|
fun test1() {
|
||||||
lateinit var s: String
|
lateinit var s: String
|
||||||
s.length
|
s.length
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ enum class LanguageFeature(
|
|||||||
SafeCastCheckBoundSmartCasts(KOTLIN_1_2),
|
SafeCastCheckBoundSmartCasts(KOTLIN_1_2),
|
||||||
BooleanElvisBoundSmartCasts(KOTLIN_1_2),
|
BooleanElvisBoundSmartCasts(KOTLIN_1_2),
|
||||||
CapturedInClosureSmartCasts(KOTLIN_1_2),
|
CapturedInClosureSmartCasts(KOTLIN_1_2),
|
||||||
|
LateinitTopLevelProperties(KOTLIN_1_2),
|
||||||
|
LateinitLocalVariables(KOTLIN_1_2),
|
||||||
|
|
||||||
// Experimental features
|
// Experimental features
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import com.intellij.psi.tree.IElementType
|
|||||||
import com.intellij.psi.tree.TokenSet
|
import com.intellij.psi.tree.TokenSet
|
||||||
import org.jetbrains.kotlin.KtNodeTypes
|
import org.jetbrains.kotlin.KtNodeTypes
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
|
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||||
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||||
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 {
|
fun isKeywordCorrectlyApplied(keywordTokenType: KtKeywordToken, file: KtFile): Boolean {
|
||||||
val elementAt = file.findElementAt(prefixText.length)!!
|
val elementAt = file.findElementAt(prefixText.length)!!
|
||||||
|
|
||||||
|
val languageVersionSettings = ModuleUtilCore.findModuleForPsiElement(position)?.languageVersionSettings
|
||||||
|
?: LanguageVersionSettingsImpl.DEFAULT
|
||||||
|
|
||||||
when {
|
when {
|
||||||
!elementAt.node!!.elementType.matchesKeyword(keywordTokenType) -> return false
|
!elementAt.node!!.elementType.matchesKeyword(keywordTokenType) -> return false
|
||||||
|
|
||||||
@@ -335,7 +339,7 @@ object KeywordCompletion {
|
|||||||
|
|
||||||
isErrorElementBefore(elementAt) -> return false
|
isErrorElementBefore(elementAt) -> return false
|
||||||
|
|
||||||
!isSupportedAtLanguageLevel(keywordTokenType, position) -> return false
|
!isModifierSupportedAtLanguageLevel(keywordTokenType, languageVersionSettings) -> return false
|
||||||
|
|
||||||
keywordTokenType !is KtModifierKeywordToken -> return true
|
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)
|
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]
|
val modifierTargets = ModifierCheckerCore.possibleTargetMap[keywordTokenType]?.intersect(possibleTargets)
|
||||||
if (modifierTargets != null && possibleTargets != null && possibleTargets.none { it in modifierTargets }) return false
|
if (modifierTargets != null && possibleTargets.isNotEmpty() &&
|
||||||
|
!modifierTargets.any {
|
||||||
|
isModifierTargetSupportedAtLanguageLevel(keywordTokenType, it, languageVersionSettings)
|
||||||
|
}) return false
|
||||||
|
|
||||||
val ownerDeclaration = container?.getParentOfType<KtDeclaration>(strict = true)
|
val ownerDeclaration = container?.getParentOfType<KtDeclaration>(strict = true)
|
||||||
val parentTarget = when (ownerDeclaration) {
|
val parentTarget = when (ownerDeclaration) {
|
||||||
@@ -416,9 +423,7 @@ object KeywordCompletion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSupportedAtLanguageLevel(keyword: KtKeywordToken, position: PsiElement): Boolean {
|
private fun isModifierSupportedAtLanguageLevel(keyword: KtKeywordToken, languageVersionSettings: LanguageVersionSettings): Boolean {
|
||||||
val languageVersionSettings = ModuleUtilCore.findModuleForPsiElement(position)?.languageVersionSettings
|
|
||||||
?: LanguageVersionSettingsImpl.DEFAULT
|
|
||||||
val feature = when (keyword) {
|
val feature = when (keyword) {
|
||||||
KtTokens.TYPE_ALIAS_KEYWORD -> LanguageFeature.TypeAliases
|
KtTokens.TYPE_ALIAS_KEYWORD -> LanguageFeature.TypeAliases
|
||||||
KtTokens.HEADER_KEYWORD, KtTokens.IMPL_KEYWORD -> LanguageFeature.MultiPlatformProjects
|
KtTokens.HEADER_KEYWORD, KtTokens.IMPL_KEYWORD -> LanguageFeature.MultiPlatformProjects
|
||||||
@@ -428,6 +433,24 @@ object KeywordCompletion {
|
|||||||
return languageVersionSettings.supportsFeature(feature)
|
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
|
// 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?) {
|
private fun buildReducedContextBefore(builder: StringBuilder, position: PsiElement, scope: PsiElement?) {
|
||||||
if (position == scope) return
|
if (position == scope) return
|
||||||
|
|||||||
Reference in New Issue
Block a user