diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 638c70b6019..5a4d2446ce6 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -301,23 +301,23 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val MODIFIERS by object : DiagnosticGroup("Modifiers") { val INAPPLICABLE_INFIX_MODIFIER by error() val REPEATED_MODIFIER by error { - parameter("modifier") + parameter("modifier") } val REDUNDANT_MODIFIER by error { - parameter("redundantModifier") - parameter("conflictingModifier") + parameter("redundantModifier") + parameter("conflictingModifier") } val DEPRECATED_MODIFIER_PAIR by error { - parameter("deprecatedModifier") - parameter("conflictingModifier") + parameter("deprecatedModifier") + parameter("conflictingModifier") } val INCOMPATIBLE_MODIFIERS by error { - parameter("modifier1") - parameter("modifier2") + parameter("modifier1") + parameter("modifier2") } val REDUNDANT_OPEN_IN_INTERFACE by warning(PositioningStrategy.OPEN_MODIFIER) val WRONG_MODIFIER_TARGET by error { - parameter("modifier") + parameter("modifier") parameter("target") } val OPERATOR_MODIFIER_REQUIRED by error { diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmExternalDeclarationChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmExternalDeclarationChecker.kt index c006e56e6af..8a218e3675e 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmExternalDeclarationChecker.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmExternalDeclarationChecker.kt @@ -32,7 +32,7 @@ object FirJvmExternalDeclarationChecker : FirBasicDeclarationChecker() { } val externalModifier = declaration.getModifier(KtTokens.EXTERNAL_KEYWORD) externalModifier?.let { - reporter.reportOn(it.source, FirErrors.WRONG_MODIFIER_TARGET, it.token, target, context) + reporter.reportOn(it.source, FirErrors.WRONG_MODIFIER_TARGET, it.token.toString(), target, context) } } diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 303b1676ecc..0fd6c85a6a7 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -254,12 +254,12 @@ object FirErrors { // Modifiers val INAPPLICABLE_INFIX_MODIFIER by error0() - val REPEATED_MODIFIER by error1() - val REDUNDANT_MODIFIER by error2() - val DEPRECATED_MODIFIER_PAIR by error2() - val INCOMPATIBLE_MODIFIERS by error2() + val REPEATED_MODIFIER by error1() + val REDUNDANT_MODIFIER by error2() + val DEPRECATED_MODIFIER_PAIR by error2() + val INCOMPATIBLE_MODIFIERS by error2() val REDUNDANT_OPEN_IN_INTERFACE by warning0(SourceElementPositioningStrategies.OPEN_MODIFIER) - val WRONG_MODIFIER_TARGET by error2() + val WRONG_MODIFIER_TARGET by error2() val OPERATOR_MODIFIER_REQUIRED by error2() val INFIX_MODIFIER_REQUIRED by error1() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirModifierHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirModifierHelpers.kt new file mode 100644 index 00000000000..fbc1c73cb1b --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirModifierHelpers.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the licensedot/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.analysis.checkers + +import org.jetbrains.kotlin.lexer.KtKeywordToken +import org.jetbrains.kotlin.lexer.KtTokens.* +import org.jetbrains.kotlin.resolve.KeywordType + +fun getKeywordType(modifier: FirModifier<*>): KeywordType { + return ktKeywordToKeywordTypeMap[modifier.token]!! +} + +private val ktKeywordToKeywordTypeMap: Map = mapOf( + INNER_KEYWORD to KeywordType.Inner, + OVERRIDE_KEYWORD to KeywordType.Override, + PUBLIC_KEYWORD to KeywordType.Public, + PROTECTED_KEYWORD to KeywordType.Protected, + INTERNAL_KEYWORD to KeywordType.Internal, + PRIVATE_KEYWORD to KeywordType.Private, + COMPANION_KEYWORD to KeywordType.Companion, + FINAL_KEYWORD to KeywordType.Final, + VARARG_KEYWORD to KeywordType.Vararg, + ENUM_KEYWORD to KeywordType.Enum, + ABSTRACT_KEYWORD to KeywordType.Abstract, + OPEN_KEYWORD to KeywordType.Open, + SEALED_KEYWORD to KeywordType.Sealed, + IN_KEYWORD to KeywordType.In, + OUT_KEYWORD to KeywordType.Out, + REIFIED_KEYWORD to KeywordType.Reified, + LATEINIT_KEYWORD to KeywordType.Lateinit, + DATA_KEYWORD to KeywordType.Data, + INLINE_KEYWORD to KeywordType.Inline, + NOINLINE_KEYWORD to KeywordType.Noinline, + TAILREC_KEYWORD to KeywordType.Tailrec, + SUSPEND_KEYWORD to KeywordType.Suspend, + EXTERNAL_KEYWORD to KeywordType.External, + ANNOTATION_KEYWORD to KeywordType.Annotation, + CROSSINLINE_KEYWORD to KeywordType.Crossinline, + CONST_KEYWORD to KeywordType.Const, + OPERATOR_KEYWORD to KeywordType.Operator, + INFIX_KEYWORD to KeywordType.Infix, + HEADER_KEYWORD to KeywordType.Header, + IMPL_KEYWORD to KeywordType.Impl, + EXPECT_KEYWORD to KeywordType.Expect, + ACTUAL_KEYWORD to KeywordType.Actual, + FUN_KEYWORD to KeywordType.Fun, + VALUE_KEYWORD to KeywordType.Value +) \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConstPropertyChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConstPropertyChecker.kt index 2fac0035b45..b6c62af3ce6 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConstPropertyChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConstPropertyChecker.kt @@ -29,7 +29,7 @@ object FirConstPropertyChecker : FirPropertyChecker() { if (declaration.isVar) { val constModifier = declaration.getModifier(KtTokens.CONST_KEYWORD) constModifier?.let { - reporter.reportOn(it.source, FirErrors.WRONG_MODIFIER_TARGET, it.token, "vars", context) + reporter.reportOn(it.source, FirErrors.WRONG_MODIFIER_TARGET, it.token.toString(), "vars", context) } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirModifierChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirModifierChecker.kt index eef80ff698f..65aec7c1968 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirModifierChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirModifierChecker.kt @@ -11,129 +11,37 @@ import org.jetbrains.kotlin.fir.analysis.checkers.FirModifier import org.jetbrains.kotlin.fir.analysis.checkers.FirModifierList import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.getModifierList +import org.jetbrains.kotlin.fir.analysis.checkers.getKeywordType import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor -import org.jetbrains.kotlin.lexer.KtModifierKeywordToken -import org.jetbrains.kotlin.lexer.KtTokens.* +import org.jetbrains.kotlin.resolve.Compatibility +import org.jetbrains.kotlin.resolve.KeywordType +import org.jetbrains.kotlin.resolve.compatibility object FirModifierChecker : FirBasicDeclarationChecker() { + override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + if (declaration is FirFile) return - private enum class CompatibilityType { - COMPATIBLE, - COMPATIBLE_FOR_CLASSES, // for functions and properties: error - REDUNDANT_1_TO_2, // first is redundant to second: warning - REDUNDANT_2_TO_1, // second is redundant to first: warning - DEPRECATED, // pair is deprecated and will soon become incompatible: warning - REPEATED, // first and second are the same: error - INCOMPATIBLE, // pair is incompatible: error + val source = declaration.source ?: return + if (!isDeclarationMappedToSourceCorrectly(declaration, source)) return + if (context.containingDeclarations.last() is FirDefaultPropertyAccessor) return + + val modifierList = source.getModifierList() + modifierList?.let { checkModifiers(it, declaration, reporter, context) } } - // first modifier in pair should also be first in spelling order and declaration's modifier list - private val compatibilityTypeMap = hashMapOf, CompatibilityType>() - - private fun recordCompatibilityType(compatibilityType: CompatibilityType, vararg list: KtModifierKeywordToken) { - for (firstKeyword in list) { - for (secondKeyword in list) { - if (firstKeyword != secondKeyword) { - compatibilityTypeMap[Pair(firstKeyword, secondKeyword)] = compatibilityType - } - } + private fun isDeclarationMappedToSourceCorrectly(declaration: FirDeclaration, source: FirSourceElement): Boolean = + when (source.elementType) { + KtNodeTypes.CLASS -> declaration is FirClass + KtNodeTypes.OBJECT_DECLARATION -> declaration is FirClass + KtNodeTypes.PROPERTY -> declaration is FirProperty + KtNodeTypes.VALUE_PARAMETER -> declaration is FirValueParameter + // TODO more FIR-PSI relations possibly have to be added + else -> true } - } - - private fun recordPairsCompatibleForClasses(vararg list: KtModifierKeywordToken) { - recordCompatibilityType(CompatibilityType.COMPATIBLE_FOR_CLASSES, *list) - } - - private fun recordDeprecatedPairs(vararg list: KtModifierKeywordToken) { - recordCompatibilityType(CompatibilityType.DEPRECATED, *list) - } - - private fun recordIncompatiblePairs(vararg list: KtModifierKeywordToken) { - recordCompatibilityType(CompatibilityType.INCOMPATIBLE, *list) - } - - // note that order matters: the first argument is redundant to the second, not the other way around - private fun recordRedundantPairs(redundantKeyword: KtModifierKeywordToken, sufficientKeyword: KtModifierKeywordToken) { - compatibilityTypeMap[Pair(redundantKeyword, sufficientKeyword)] = CompatibilityType.REDUNDANT_1_TO_2 - compatibilityTypeMap[Pair(sufficientKeyword, redundantKeyword)] = CompatibilityType.REDUNDANT_2_TO_1 - } - - // building the compatibility type mapping - init { - recordIncompatiblePairs(IN_KEYWORD, OUT_KEYWORD) // Variance - recordIncompatiblePairs(PRIVATE_KEYWORD, PROTECTED_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD) // Visibilities - recordIncompatiblePairs(HEADER_KEYWORD, EXPECT_KEYWORD, IMPL_KEYWORD, ACTUAL_KEYWORD) - recordIncompatiblePairs(FINAL_KEYWORD, ABSTRACT_KEYWORD) - recordIncompatiblePairs(FINAL_KEYWORD, OPEN_KEYWORD, SEALED_KEYWORD) - recordIncompatiblePairs(CROSSINLINE_KEYWORD, NOINLINE_KEYWORD) - - recordIncompatiblePairs(DATA_KEYWORD, OPEN_KEYWORD) - recordIncompatiblePairs(DATA_KEYWORD, INNER_KEYWORD) - recordIncompatiblePairs(DATA_KEYWORD, ABSTRACT_KEYWORD) - recordIncompatiblePairs(DATA_KEYWORD, SEALED_KEYWORD) - recordIncompatiblePairs(DATA_KEYWORD, INLINE_KEYWORD) - - recordIncompatiblePairs(CONST_KEYWORD, ABSTRACT_KEYWORD) - recordIncompatiblePairs(CONST_KEYWORD, OPEN_KEYWORD) - recordIncompatiblePairs(CONST_KEYWORD, OVERRIDE_KEYWORD) - - recordIncompatiblePairs(PRIVATE_KEYWORD, OVERRIDE_KEYWORD) - recordPairsCompatibleForClasses(PRIVATE_KEYWORD, OPEN_KEYWORD) - recordPairsCompatibleForClasses(PRIVATE_KEYWORD, ABSTRACT_KEYWORD) - - // 1. subclasses contained inside a sealed class can not be instantiated, because their constructors needs - // an instance of an outer sealed (effectively abstract) class - // 2. subclasses of a non-top-level sealed class must be declared inside the class - // (see the KEEP https://github.com/Kotlin/KEEP/blob/master/proposals/sealed-class-inheritance.md) - recordIncompatiblePairs(SEALED_KEYWORD, INNER_KEYWORD) - - recordRedundantPairs(OPEN_KEYWORD, ABSTRACT_KEYWORD) - recordRedundantPairs(ABSTRACT_KEYWORD, SEALED_KEYWORD) - } - - private fun deduceCompatibilityType(firstKeyword: KtModifierKeywordToken, secondKeyword: KtModifierKeywordToken): CompatibilityType = - if (firstKeyword == secondKeyword) { - CompatibilityType.REPEATED - } else { - compatibilityTypeMap[Pair(firstKeyword, secondKeyword)] ?: CompatibilityType.COMPATIBLE - } - - private fun checkCompatibilityType( - firstModifier: FirModifier<*>, - secondModifier: FirModifier<*>, - reporter: DiagnosticReporter, - reportedNodes: MutableSet>, - owner: FirDeclaration?, - context: CheckerContext - ) { - val firstToken = firstModifier.token - val secondToken = secondModifier.token - when (val compatibilityType = deduceCompatibilityType(firstToken, secondToken)) { - CompatibilityType.COMPATIBLE -> { - } - CompatibilityType.REPEATED -> - if (reportedNodes.add(secondModifier)) reporter.reportRepeatedModifier(secondModifier, secondToken, context) - CompatibilityType.REDUNDANT_2_TO_1 -> - reporter.reportRedundantModifier(secondModifier, secondToken, firstToken, context) - CompatibilityType.REDUNDANT_1_TO_2 -> - reporter.reportRedundantModifier(firstModifier, firstToken, secondToken, context) - CompatibilityType.DEPRECATED -> { - reporter.reportDeprecatedModifierPair(firstModifier, firstToken, secondToken, context) - reporter.reportDeprecatedModifierPair(secondModifier, secondToken, firstToken, context) - } - CompatibilityType.INCOMPATIBLE, CompatibilityType.COMPATIBLE_FOR_CLASSES -> { - if (compatibilityType == CompatibilityType.COMPATIBLE_FOR_CLASSES && owner is FirClass) { - return - } - if (reportedNodes.add(firstModifier)) reporter.reportIncompatibleModifiers(firstModifier, firstToken, secondToken, context) - if (reportedNodes.add(secondModifier)) reporter.reportIncompatibleModifiers(secondModifier, secondToken, firstToken, context) - } - } - } private fun checkModifiers( list: FirModifierList, @@ -156,48 +64,84 @@ object FirModifierChecker : FirBasicDeclarationChecker() { } } - private fun isDeclarationMappedToSourceCorrectly(declaration: FirDeclaration, source: FirSourceElement): Boolean = - when (source.elementType) { - KtNodeTypes.CLASS -> declaration is FirClass - KtNodeTypes.OBJECT_DECLARATION -> declaration is FirClass - KtNodeTypes.PROPERTY -> declaration is FirProperty - KtNodeTypes.VALUE_PARAMETER -> declaration is FirValueParameter - // TODO more FIR-PSI relations possibly have to be added - else -> true + private fun checkCompatibilityType( + firstModifier: FirModifier<*>, + secondModifier: FirModifier<*>, + reporter: DiagnosticReporter, + reportedNodes: MutableSet>, + owner: FirDeclaration?, + context: CheckerContext + ) { + val firstModifierType = getKeywordType(firstModifier) + val secondModifierType = getKeywordType(secondModifier) + when (val compatibilityType = compatibility(firstModifierType, secondModifierType)) { + Compatibility.COMPATIBLE -> { + } + Compatibility.REPEATED -> + if (reportedNodes.add(secondModifier)) { + reporter.reportOn(secondModifier.source, FirErrors.REPEATED_MODIFIER, secondModifierType.render(), context) + } + Compatibility.REDUNDANT -> { + reporter.reportOn( + secondModifier.source, + FirErrors.REDUNDANT_MODIFIER, + secondModifierType.render(), + firstModifierType.render(), + context + ) + } + Compatibility.REVERSE_REDUNDANT -> { + reporter.reportOn( + firstModifier.source, + FirErrors.REDUNDANT_MODIFIER, + firstModifierType.render(), + secondModifierType.render(), + context + ) + } + Compatibility.DEPRECATED -> { + reporter.reportOn( + firstModifier.source, + FirErrors.DEPRECATED_MODIFIER_PAIR, + firstModifierType.render(), + secondModifierType.render(), + context + ) + reporter.reportOn( + secondModifier.source, + FirErrors.DEPRECATED_MODIFIER_PAIR, + secondModifierType.render(), + firstModifierType.render(), + context + ) + } + Compatibility.INCOMPATIBLE, Compatibility.COMPATIBLE_FOR_CLASSES_ONLY -> { + if (compatibilityType == Compatibility.COMPATIBLE_FOR_CLASSES_ONLY && owner is FirClass) { + return + } + if (reportedNodes.add(firstModifier)) { + reporter.reportOn( + firstModifier.source, + FirErrors.INCOMPATIBLE_MODIFIERS, + firstModifierType.render(), + secondModifierType.render(), + context + ) + } + if (reportedNodes.add(secondModifier)) { + reporter.reportOn( + secondModifier.source, + FirErrors.INCOMPATIBLE_MODIFIERS, + secondModifierType.render(), + firstModifierType.render(), + context + ) + } + } } - - override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { - if (declaration is FirFile) return - - val source = declaration.source ?: return - if (!isDeclarationMappedToSourceCorrectly(declaration, source)) return - if (context.containingDeclarations.last() is FirDefaultPropertyAccessor) return - - val modifierList = source.getModifierList() - modifierList?.let { checkModifiers(it, declaration, reporter, context) } } - private fun DiagnosticReporter.reportRepeatedModifier( - modifier: FirModifier<*>, keyword: KtModifierKeywordToken, context: CheckerContext - ) { - reportOn(modifier.source, FirErrors.REPEATED_MODIFIER, keyword, context) - } - - private fun DiagnosticReporter.reportRedundantModifier( - modifier: FirModifier<*>, firstKeyword: KtModifierKeywordToken, secondKeyword: KtModifierKeywordToken, context: CheckerContext - ) { - reportOn(modifier.source, FirErrors.REDUNDANT_MODIFIER, firstKeyword, secondKeyword, context) - } - - private fun DiagnosticReporter.reportDeprecatedModifierPair( - modifier: FirModifier<*>, firstKeyword: KtModifierKeywordToken, secondKeyword: KtModifierKeywordToken, context: CheckerContext - ) { - reportOn(modifier.source, FirErrors.DEPRECATED_MODIFIER_PAIR, firstKeyword, secondKeyword, context) - } - - private fun DiagnosticReporter.reportIncompatibleModifiers( - modifier: FirModifier<*>, firstKeyword: KtModifierKeywordToken, secondKeyword: KtModifierKeywordToken, context: CheckerContext - ) { - reportOn(modifier.source, FirErrors.INCOMPATIBLE_MODIFIERS, firstKeyword, secondKeyword, context) + private fun KeywordType.render(): String { + return this.toString().lowercase() } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirSuspendModifierChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirSuspendModifierChecker.kt index 7e87dca2416..055f7db3480 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirSuspendModifierChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirSuspendModifierChecker.kt @@ -38,7 +38,7 @@ object FirSuspendModifierChecker : FirTypeRefChecker() { reporter.reportOn( suspendModifier.source, FirErrors.WRONG_MODIFIER_TARGET, - suspendModifier.token, + suspendModifier.token.toString(), "non-functional type", context ) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 1b9a995bcc4..5479e22d859 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -725,12 +725,12 @@ class FirDefaultErrorMessages { // Modifiers map.put(INAPPLICABLE_INFIX_MODIFIER, "''infix'' modifier is inapplicable on this function") - map.put(REPEATED_MODIFIER, "Repeated ''{0}''", TO_STRING) - map.put(REDUNDANT_MODIFIER, "Modifier ''{0}'' is redundant because ''{1}'' is present", TO_STRING, TO_STRING) - map.put(DEPRECATED_MODIFIER_PAIR, "Modifier ''{0}'' is deprecated in presence of ''{1}''", TO_STRING, TO_STRING) - map.put(INCOMPATIBLE_MODIFIERS, "Modifier ''{0}'' is incompatible with ''{1}''", TO_STRING, TO_STRING) + map.put(REPEATED_MODIFIER, "Repeated ''{0}''", STRING) + map.put(REDUNDANT_MODIFIER, "Modifier ''{0}'' is redundant because ''{1}'' is present", STRING, STRING) + map.put(DEPRECATED_MODIFIER_PAIR, "Modifier ''{0}'' is deprecated in presence of ''{1}''", STRING, STRING) + map.put(INCOMPATIBLE_MODIFIERS, "Modifier ''{0}'' is incompatible with ''{1}''", STRING, STRING) map.put(REDUNDANT_OPEN_IN_INTERFACE, "Modifier 'open' is redundant for abstract interface members") - map.put(WRONG_MODIFIER_TARGET, "Modifier ''{0}'' is not applicable to ''{1}''", TO_STRING, TO_STRING) + map.put(WRONG_MODIFIER_TARGET, "Modifier ''{0}'' is not applicable to ''{1}''", STRING, STRING) map.put(INFIX_MODIFIER_REQUIRED, "''infix'' modifier is required on ''{0}''", TO_STRING) // Classes and interfaces diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 59ad9856263..9216e7ef73a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -224,7 +224,7 @@ public interface Errors { DiagnosticFactory2 REDUNDANT_MODIFIER = DiagnosticFactory2.create(WARNING); DiagnosticFactory2 WRONG_MODIFIER_TARGET = DiagnosticFactory2.create(ERROR); DiagnosticFactory2 DEPRECATED_MODIFIER_FOR_TARGET = DiagnosticFactory2.create(WARNING); - DiagnosticFactory2 DEPRECATED_MODIFIER = DiagnosticFactory2.create(WARNING); + DiagnosticFactory2 DEPRECATED_MODIFIER = DiagnosticFactory2.create(WARNING); DiagnosticFactory2 REDUNDANT_MODIFIER_FOR_TARGET = DiagnosticFactory2.create(WARNING); DiagnosticFactory0 NO_EXPLICIT_VISIBILITY_IN_API_MODE = DiagnosticFactory0.create(ERROR, DECLARATION_START_TO_NAME); DiagnosticFactory0 NO_EXPLICIT_RETURN_TYPE_IN_API_MODE = DiagnosticFactory0.create(ERROR, DECLARATION_NAME); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt index 60510625de1..21775ab8fee 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt @@ -14,252 +14,106 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.* import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.lexer.KtKeywordToken import org.jetbrains.kotlin.lexer.KtModifierKeywordToken import org.jetbrains.kotlin.lexer.KtTokens.* import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtDeclarationWithBody import org.jetbrains.kotlin.psi.KtModifierList import org.jetbrains.kotlin.psi.KtModifierListOwner +import org.jetbrains.kotlin.resolve.KeywordType.* +import org.jetbrains.kotlin.resolve.KeywordType.Annotation import org.jetbrains.kotlin.resolve.calls.checkers.checkCoroutinesFeature -import java.util.* object ModifierCheckerCore { - private enum class Compatibility { - // modifier pair is compatible: ok (default) - COMPATIBLE, - // second is redundant to first: warning - REDUNDANT, - // first is redundant to second: warning - REVERSE_REDUNDANT, - // error - REPEATED, - // pair is deprecated, will become incompatible: warning - DEPRECATED, - // pair is incompatible: error - INCOMPATIBLE, - // same but only for functions / properties: error - COMPATIBLE_FOR_CLASSES_ONLY - } - - private val defaultVisibilityTargets = EnumSet.of( - CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS, - MEMBER_FUNCTION, TOP_LEVEL_FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, - MEMBER_PROPERTY, TOP_LEVEL_PROPERTY, CONSTRUCTOR, TYPEALIAS + private val ktKeywordToKeywordTypeMap: Map = mapOf( + INNER_KEYWORD to Inner, + OVERRIDE_KEYWORD to Override, + PUBLIC_KEYWORD to Public, + PROTECTED_KEYWORD to Protected, + INTERNAL_KEYWORD to Internal, + PRIVATE_KEYWORD to Private, + COMPANION_KEYWORD to KeywordType.Companion, + FINAL_KEYWORD to Final, + VARARG_KEYWORD to Vararg, + ENUM_KEYWORD to KeywordType.Enum, + ABSTRACT_KEYWORD to Abstract, + OPEN_KEYWORD to Open, + SEALED_KEYWORD to Sealed, + IN_KEYWORD to In, + OUT_KEYWORD to Out, + REIFIED_KEYWORD to Reified, + LATEINIT_KEYWORD to Lateinit, + DATA_KEYWORD to Data, + INLINE_KEYWORD to Inline, + NOINLINE_KEYWORD to Noinline, + TAILREC_KEYWORD to Tailrec, + SUSPEND_KEYWORD to Suspend, + EXTERNAL_KEYWORD to External, + ANNOTATION_KEYWORD to Annotation, + CROSSINLINE_KEYWORD to Crossinline, + CONST_KEYWORD to Const, + OPERATOR_KEYWORD to Operator, + INFIX_KEYWORD to Infix, + HEADER_KEYWORD to Header, + IMPL_KEYWORD to Impl, + EXPECT_KEYWORD to Expect, + ACTUAL_KEYWORD to Actual, + FUN_KEYWORD to Fun, + VALUE_KEYWORD to Value ) - val possibleTargetMap = mapOf>( - ENUM_KEYWORD to EnumSet.of(ENUM_CLASS), - ABSTRACT_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, INTERFACE, MEMBER_PROPERTY, MEMBER_FUNCTION), - OPEN_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, INTERFACE, MEMBER_PROPERTY, MEMBER_FUNCTION), - FINAL_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, ENUM_CLASS, OBJECT, MEMBER_PROPERTY, MEMBER_FUNCTION), - SEALED_KEYWORD to EnumSet.of(CLASS_ONLY, INTERFACE), - INNER_KEYWORD to EnumSet.of(CLASS_ONLY), - OVERRIDE_KEYWORD to EnumSet.of(MEMBER_PROPERTY, MEMBER_FUNCTION), - PRIVATE_KEYWORD to defaultVisibilityTargets, - PUBLIC_KEYWORD to defaultVisibilityTargets, - INTERNAL_KEYWORD to defaultVisibilityTargets, - PROTECTED_KEYWORD to EnumSet.of( - CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS, - MEMBER_FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, MEMBER_PROPERTY, CONSTRUCTOR, TYPEALIAS - ), - IN_KEYWORD to EnumSet.of(TYPE_PARAMETER, TYPE_PROJECTION), - OUT_KEYWORD to EnumSet.of(TYPE_PARAMETER, TYPE_PROJECTION), - REIFIED_KEYWORD to EnumSet.of(TYPE_PARAMETER), - VARARG_KEYWORD to EnumSet.of(VALUE_PARAMETER, PROPERTY_PARAMETER), - COMPANION_KEYWORD to EnumSet.of(OBJECT), - LATEINIT_KEYWORD to EnumSet.of(MEMBER_PROPERTY, TOP_LEVEL_PROPERTY, LOCAL_VARIABLE), - DATA_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS), - INLINE_KEYWORD to EnumSet.of(FUNCTION, PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER, CLASS_ONLY), - NOINLINE_KEYWORD to EnumSet.of(VALUE_PARAMETER), - TAILREC_KEYWORD to EnumSet.of(FUNCTION), - SUSPEND_KEYWORD to EnumSet.of(MEMBER_FUNCTION, TOP_LEVEL_FUNCTION, LOCAL_FUNCTION), - EXTERNAL_KEYWORD to EnumSet.of(FUNCTION, PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER, CLASS), - ANNOTATION_KEYWORD to EnumSet.of(ANNOTATION_CLASS), - CROSSINLINE_KEYWORD to EnumSet.of(VALUE_PARAMETER), - CONST_KEYWORD to EnumSet.of(MEMBER_PROPERTY, TOP_LEVEL_PROPERTY), - OPERATOR_KEYWORD to EnumSet.of(FUNCTION), - INFIX_KEYWORD to EnumSet.of(FUNCTION), - HEADER_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, TOP_LEVEL_PROPERTY, CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS), - IMPL_KEYWORD to EnumSet.of( - TOP_LEVEL_FUNCTION, - MEMBER_FUNCTION, - TOP_LEVEL_PROPERTY, - MEMBER_PROPERTY, - CONSTRUCTOR, - CLASS_ONLY, - OBJECT, - INTERFACE, - ENUM_CLASS, - ANNOTATION_CLASS, - TYPEALIAS - ), - EXPECT_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, TOP_LEVEL_PROPERTY, CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS), - ACTUAL_KEYWORD to EnumSet.of( - TOP_LEVEL_FUNCTION, - MEMBER_FUNCTION, - TOP_LEVEL_PROPERTY, - MEMBER_PROPERTY, - CONSTRUCTOR, - CLASS_ONLY, - OBJECT, - INTERFACE, - ENUM_CLASS, - ANNOTATION_CLASS, - TYPEALIAS - ), - FUN_KEYWORD to EnumSet.of(INTERFACE), - VALUE_KEYWORD to EnumSet.of(CLASS_ONLY) - ) - - private val featureDependencies = mapOf( - SUSPEND_KEYWORD to listOf(LanguageFeature.Coroutines), - INLINE_KEYWORD to listOf(LanguageFeature.InlineProperties, LanguageFeature.InlineClasses), - HEADER_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects), - IMPL_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects), - EXPECT_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects), - ACTUAL_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects), - LATEINIT_KEYWORD to listOf(LanguageFeature.LateinitTopLevelProperties, LanguageFeature.LateinitLocalVariables), - FUN_KEYWORD to listOf(LanguageFeature.FunctionalInterfaceConversion) - ) - - private val featureDependenciesTargets = mapOf( - LanguageFeature.InlineProperties to setOf(PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER), - LanguageFeature.LateinitLocalVariables to setOf(LOCAL_VARIABLE), - LanguageFeature.LateinitTopLevelProperties to setOf(TOP_LEVEL_PROPERTY), - LanguageFeature.InlineClasses to setOf(CLASS_ONLY), - LanguageFeature.JvmInlineValueClasses to setOf(CLASS_ONLY), - LanguageFeature.FunctionalInterfaceConversion to setOf(INTERFACE) - ) - - // NOTE: deprecated targets must be possible! - private val deprecatedTargetMap = mapOf>() - - private val deprecatedModifierMap = mapOf( - HEADER_KEYWORD to EXPECT_KEYWORD, - IMPL_KEYWORD to ACTUAL_KEYWORD - ) - - // NOTE: redundant targets must be possible! - private val redundantTargetMap = mapOf>( - OPEN_KEYWORD to EnumSet.of(INTERFACE) - ) - - private val possibleParentTargetPredicateMap = mapOf( - INNER_KEYWORD to or( - always(CLASS_ONLY, LOCAL_CLASS, ENUM_CLASS), - ifSupported(LanguageFeature.InnerClassInEnumEntryClass, ENUM_ENTRY) - ), - OVERRIDE_KEYWORD to always(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL, INTERFACE, ENUM_CLASS, ENUM_ENTRY), - PROTECTED_KEYWORD to always(CLASS_ONLY, LOCAL_CLASS, ENUM_CLASS, COMPANION_OBJECT), - INTERNAL_KEYWORD to always(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL, ENUM_CLASS, ENUM_ENTRY, FILE), - PRIVATE_KEYWORD to always(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL, INTERFACE, ENUM_CLASS, ENUM_ENTRY, FILE), - COMPANION_KEYWORD to always(CLASS_ONLY, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS), - FINAL_KEYWORD to always(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL, ENUM_CLASS, ENUM_ENTRY, ANNOTATION_CLASS, FILE), - VARARG_KEYWORD to always(CONSTRUCTOR, FUNCTION, CLASS) - ) - - private val deprecatedParentTargetMap = mapOf>() - - fun isPossibleParentTarget( - modifier: KtModifierKeywordToken, - parentTarget: KotlinTarget, + fun check( + listOwner: KtModifierListOwner, + trace: BindingTrace, + descriptor: DeclarationDescriptor?, languageVersionSettings: LanguageVersionSettings - ): Boolean { - deprecatedParentTargetMap[modifier]?.let { - if (parentTarget in it) return false - } - - possibleParentTargetPredicateMap[modifier]?.let { - return it.isAllowed(parentTarget, languageVersionSettings) - } - - return true - } - - // First modifier in pair should be also first in declaration - private val mutualCompatibility = buildCompatibilityMap() - - private fun buildCompatibilityMap(): Map, Compatibility> { - val result = hashMapOf, Compatibility>() - // Variance: in + out are incompatible - result += incompatibilityRegister(IN_KEYWORD, OUT_KEYWORD) - // Visibilities: incompatible - result += incompatibilityRegister(PRIVATE_KEYWORD, PROTECTED_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD) - // Abstract + open + final + sealed: incompatible - result += incompatibilityRegister(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD, SEALED_KEYWORD) - // data + open, data + inner, data + abstract, data + sealed, data + inline, data + value - result += incompatibilityRegister(DATA_KEYWORD, OPEN_KEYWORD) - result += incompatibilityRegister(DATA_KEYWORD, INNER_KEYWORD) - result += incompatibilityRegister(DATA_KEYWORD, ABSTRACT_KEYWORD) - result += incompatibilityRegister(DATA_KEYWORD, SEALED_KEYWORD) - result += incompatibilityRegister(DATA_KEYWORD, INLINE_KEYWORD) - result += incompatibilityRegister(DATA_KEYWORD, VALUE_KEYWORD) - // open is redundant to abstract & override - result += redundantRegister(ABSTRACT_KEYWORD, OPEN_KEYWORD) - // abstract is redundant to sealed - result += redundantRegister(SEALED_KEYWORD, ABSTRACT_KEYWORD) - - // const is incompatible with abstract, open, override - result += incompatibilityRegister(CONST_KEYWORD, ABSTRACT_KEYWORD) - result += incompatibilityRegister(CONST_KEYWORD, OPEN_KEYWORD) - result += incompatibilityRegister(CONST_KEYWORD, OVERRIDE_KEYWORD) - - // private is incompatible with override - result += incompatibilityRegister(PRIVATE_KEYWORD, OVERRIDE_KEYWORD) - // private is compatible with open / abstract only for classes - result += compatibilityForClassesRegister(PRIVATE_KEYWORD, OPEN_KEYWORD) - result += compatibilityForClassesRegister(PRIVATE_KEYWORD, ABSTRACT_KEYWORD) - - result += incompatibilityRegister(CROSSINLINE_KEYWORD, NOINLINE_KEYWORD) - - // 1. subclasses contained inside a sealed class can not be instantiated, because their constructors needs - // an instance of an outer sealed (effectively abstract) class - // 2. subclasses of a non-top-level sealed class must be declared inside the class - // (see the KEEP https://github.com/Kotlin/KEEP/blob/master/proposals/sealed-class-inheritance.md) - result += incompatibilityRegister(SEALED_KEYWORD, INNER_KEYWORD) - - // header / expect / impl / actual are all incompatible - result += incompatibilityRegister(HEADER_KEYWORD, EXPECT_KEYWORD, IMPL_KEYWORD, ACTUAL_KEYWORD) - - return result - } - - private fun redundantRegister( - sufficient: KtModifierKeywordToken, - redundant: KtModifierKeywordToken - ): Map, Compatibility> { - return mapOf( - Pair(sufficient, redundant) to Compatibility.REDUNDANT, - Pair(redundant, sufficient) to Compatibility.REVERSE_REDUNDANT - ) - } - - private fun compatibilityRegister( - compatibility: Compatibility, vararg list: KtModifierKeywordToken - ): Map, Compatibility> { - val result = hashMapOf, Compatibility>() - for (first in list) { - for (second in list) { - if (first != second) { - result[Pair(first, second)] = compatibility + ) { + if (listOwner is KtDeclarationWithBody) { + // JetFunction or JetPropertyAccessor + for (parameter in listOwner.valueParameters) { + if (!parameter.hasValOrVar()) { + check(parameter, trace, trace[BindingContext.VALUE_PARAMETER, parameter], languageVersionSettings) } } } - return result + val actualTargets = AnnotationChecker.getDeclarationSiteActualTargetList( + listOwner, descriptor as? ClassDescriptor, trace.bindingContext + ) + val list = listOwner.modifierList ?: return + checkModifierList(list, trace, descriptor?.containingDeclaration, actualTargets, languageVersionSettings) } - private fun compatibilityForClassesRegister(vararg list: KtModifierKeywordToken) = - compatibilityRegister(Compatibility.COMPATIBLE_FOR_CLASSES_ONLY, *list) + private val MODIFIER_KEYWORD_SET = TokenSet.orSet(SOFT_KEYWORDS, TokenSet.create(IN_KEYWORD, FUN_KEYWORD)) - private fun incompatibilityRegister(vararg list: KtModifierKeywordToken) = compatibilityRegister(Compatibility.INCOMPATIBLE, *list) + private fun checkModifierList( + list: KtModifierList, + trace: BindingTrace, + parentDescriptor: DeclarationDescriptor?, + actualTargets: List, + languageVersionSettings: LanguageVersionSettings + ) { + if (list.stub != null) return - private fun deprecationRegister(vararg list: KtModifierKeywordToken) = compatibilityRegister(Compatibility.DEPRECATED, *list) + // It's a list of all nodes with error already reported + // General strategy: report no more than one error but any number of warnings + val incorrectNodes = hashSetOf() - private fun compatibility(first: KtModifierKeywordToken, second: KtModifierKeywordToken): Compatibility { - return if (first == second) { - Compatibility.REPEATED - } else { - mutualCompatibility[Pair(first, second)] ?: Compatibility.COMPATIBLE + val children = list.node.getChildren(MODIFIER_KEYWORD_SET) + for (second in children) { + for (first in children) { + if (first == second) { + break + } + checkCompatibility(trace, first, second, list.owner, incorrectNodes) + } + if (second !in incorrectNodes) { + when { + !checkTarget(trace, second, actualTargets) -> incorrectNodes += second + !checkParent(trace, second, parentDescriptor, languageVersionSettings) -> incorrectNodes += second + !checkLanguageLevelSupport(trace, second, languageVersionSettings, actualTargets) -> incorrectNodes += second + } + } } } @@ -270,32 +124,32 @@ object ModifierCheckerCore { owner: PsiElement, incorrectNodes: MutableSet ) { - val first = firstNode.elementType as KtModifierKeywordToken - val second = secondNode.elementType as KtModifierKeywordToken - val compatibility = compatibility(first, second) - when (compatibility) { + val (firstModifier, firstModifierType) = getModifierAndModifierType(firstNode) + val (secondModifier, secondModifierType) = getModifierAndModifierType(secondNode) + + when (val compatibility = compatibility(firstModifierType, secondModifierType)) { Compatibility.COMPATIBLE -> { } Compatibility.REPEATED -> if (incorrectNodes.add(secondNode)) { - trace.report(Errors.REPEATED_MODIFIER.on(secondNode.psi, first)) + trace.report(Errors.REPEATED_MODIFIER.on(secondNode.psi, firstModifier)) } Compatibility.REDUNDANT -> - trace.report(Errors.REDUNDANT_MODIFIER.on(secondNode.psi, second, first)) + trace.report(Errors.REDUNDANT_MODIFIER.on(secondNode.psi, secondModifier, firstModifier)) Compatibility.REVERSE_REDUNDANT -> - trace.report(Errors.REDUNDANT_MODIFIER.on(firstNode.psi, first, second)) + trace.report(Errors.REDUNDANT_MODIFIER.on(firstNode.psi, firstModifier, secondModifier)) Compatibility.DEPRECATED -> { - trace.report(Errors.DEPRECATED_MODIFIER_PAIR.on(firstNode.psi, first, second)) - trace.report(Errors.DEPRECATED_MODIFIER_PAIR.on(secondNode.psi, second, first)) + trace.report(Errors.DEPRECATED_MODIFIER_PAIR.on(firstNode.psi, firstModifier, secondModifier)) + trace.report(Errors.DEPRECATED_MODIFIER_PAIR.on(secondNode.psi, secondModifier, firstModifier)) } Compatibility.COMPATIBLE_FOR_CLASSES_ONLY, Compatibility.INCOMPATIBLE -> { if (compatibility == Compatibility.COMPATIBLE_FOR_CLASSES_ONLY) { if (owner is KtClassOrObject) return } if (incorrectNodes.add(firstNode)) { - trace.report(Errors.INCOMPATIBLE_MODIFIERS.on(firstNode.psi, first, second)) + trace.report(Errors.INCOMPATIBLE_MODIFIERS.on(firstNode.psi, firstModifier, secondModifier)) } if (incorrectNodes.add(secondNode)) { - trace.report(Errors.INCOMPATIBLE_MODIFIERS.on(secondNode.psi, second, first)) + trace.report(Errors.INCOMPATIBLE_MODIFIERS.on(secondNode.psi, secondModifier, firstModifier)) } } } @@ -303,18 +157,19 @@ object ModifierCheckerCore { // Should return false if error is reported, true otherwise private fun checkTarget(trace: BindingTrace, node: ASTNode, actualTargets: List): Boolean { - val modifier = node.elementType as KtModifierKeywordToken - val possibleTargets = possibleTargetMap[modifier] ?: emptySet() + val (modifier, modifierType) = getModifierAndModifierType(node) + + val possibleTargets = possibleTargetMap[modifierType] ?: emptySet() if (!actualTargets.any { it in possibleTargets }) { trace.report(Errors.WRONG_MODIFIER_TARGET.on(node.psi, modifier, actualTargets.firstOrNull()?.description ?: "this")) return false } - val deprecatedModifierReplacement = deprecatedModifierMap[modifier] - val deprecatedTargets = deprecatedTargetMap[modifier] ?: emptySet() - val redundantTargets = redundantTargetMap[modifier] ?: emptySet() + val deprecatedModifierReplacement = deprecatedModifierMap[modifierType] + val deprecatedTargets = deprecatedTargetMap[modifierType] ?: emptySet() + val redundantTargets = redundantTargetMap[modifierType] ?: emptySet() when { deprecatedModifierReplacement != null -> - trace.report(Errors.DEPRECATED_MODIFIER.on(node.psi, modifier, deprecatedModifierReplacement)) + trace.report(Errors.DEPRECATED_MODIFIER.on(node.psi, modifier, deprecatedModifierReplacement.render())) actualTargets.any { it in deprecatedTargets } -> trace.report( Errors.DEPRECATED_MODIFIER_FOR_TARGET.on( @@ -335,15 +190,59 @@ object ModifierCheckerCore { return true } + // Should return false if error is reported, true otherwise + private fun checkParent( + trace: BindingTrace, + node: ASTNode, + parentDescriptor: DeclarationDescriptor?, + languageVersionSettings: LanguageVersionSettings + ): Boolean { + val (modifier, modifierType) = getModifierAndModifierType(node) + + val actualParents: List = when (parentDescriptor) { + is ClassDescriptor -> KotlinTarget.classActualTargets( + parentDescriptor.kind, + isInnerClass = parentDescriptor.isInner, + isCompanionObject = parentDescriptor.isCompanionObject, + isLocalClass = DescriptorUtils.isLocal(parentDescriptor) + ) + is PropertySetterDescriptor -> listOf(PROPERTY_SETTER) + is PropertyGetterDescriptor -> listOf(PROPERTY_GETTER) + is FunctionDescriptor -> listOf(FUNCTION) + else -> listOf(FILE) + } + val deprecatedParents = deprecatedParentTargetMap[modifierType] + if (deprecatedParents != null && actualParents.any { it in deprecatedParents }) { + trace.report( + Errors.DEPRECATED_MODIFIER_CONTAINING_DECLARATION.on( + node.psi, + modifier, + actualParents.firstOrNull()?.description ?: "this scope" + ) + ) + return true + } + val possibleParentPredicate = possibleParentTargetPredicateMap[modifierType] ?: return true + if (actualParents.any { possibleParentPredicate.isAllowed(it, languageVersionSettings) }) return true + trace.report( + Errors.WRONG_MODIFIER_CONTAINING_DECLARATION.on( + node.psi, + modifier, + actualParents.firstOrNull()?.description ?: "this scope" + ) + ) + return false + } + private fun checkLanguageLevelSupport( trace: BindingTrace, node: ASTNode, languageVersionSettings: LanguageVersionSettings, actualTargets: List ): Boolean { - val modifier = node.elementType as KtModifierKeywordToken + val (_, modifierType) = getModifierAndModifierType(node) - val dependencies = featureDependencies[modifier] ?: return true + val dependencies = featureDependencies[modifierType] ?: return true for (dependency in dependencies) { val restrictedTargets = featureDependenciesTargets[dependency] if (restrictedTargets != null && actualTargets.intersect(restrictedTargets).isEmpty()) { @@ -385,128 +284,8 @@ object ModifierCheckerCore { return true } - - // Should return false if error is reported, true otherwise - private fun checkParent( - trace: BindingTrace, - node: ASTNode, - parentDescriptor: DeclarationDescriptor?, - languageVersionSettings: LanguageVersionSettings - ): Boolean { + private fun getModifierAndModifierType(node: ASTNode): Pair { val modifier = node.elementType as KtModifierKeywordToken - val actualParents: List = when (parentDescriptor) { - is ClassDescriptor -> KotlinTarget.classActualTargets( - parentDescriptor.kind, - isInnerClass = parentDescriptor.isInner, - isCompanionObject = parentDescriptor.isCompanionObject, - isLocalClass = DescriptorUtils.isLocal(parentDescriptor) - ) - is PropertySetterDescriptor -> listOf(PROPERTY_SETTER) - is PropertyGetterDescriptor -> listOf(PROPERTY_GETTER) - is FunctionDescriptor -> listOf(FUNCTION) - else -> listOf(FILE) - } - val deprecatedParents = deprecatedParentTargetMap[modifier] - if (deprecatedParents != null && actualParents.any { it in deprecatedParents }) { - trace.report( - Errors.DEPRECATED_MODIFIER_CONTAINING_DECLARATION.on( - node.psi, - modifier, - actualParents.firstOrNull()?.description ?: "this scope" - ) - ) - return true - } - val possibleParentPredicate = possibleParentTargetPredicateMap[modifier] ?: return true - if (actualParents.any { possibleParentPredicate.isAllowed(it, languageVersionSettings) }) return true - trace.report( - Errors.WRONG_MODIFIER_CONTAINING_DECLARATION.on( - node.psi, - modifier, - actualParents.firstOrNull()?.description ?: "this scope" - ) - ) - return false + return Pair(modifier, ktKeywordToKeywordTypeMap[modifier]!!) } - - private val MODIFIER_KEYWORD_SET = TokenSet.orSet(SOFT_KEYWORDS, TokenSet.create(IN_KEYWORD, FUN_KEYWORD)) - - private fun checkModifierList( - list: KtModifierList, - trace: BindingTrace, - parentDescriptor: DeclarationDescriptor?, - actualTargets: List, - languageVersionSettings: LanguageVersionSettings - ) { - if (list.stub != null) return - - // It's a list of all nodes with error already reported - // General strategy: report no more than one error but any number of warnings - val incorrectNodes = hashSetOf() - - val children = list.node.getChildren(MODIFIER_KEYWORD_SET) - for (second in children) { - for (first in children) { - if (first == second) { - break - } - checkCompatibility(trace, first, second, list.owner, incorrectNodes) - } - if (second !in incorrectNodes) { - when { - !checkTarget(trace, second, actualTargets) -> incorrectNodes += second - !checkParent(trace, second, parentDescriptor, languageVersionSettings) -> incorrectNodes += second - !checkLanguageLevelSupport(trace, second, languageVersionSettings, actualTargets) -> incorrectNodes += second - } - } - } - } - - fun check( - listOwner: KtModifierListOwner, - trace: BindingTrace, - descriptor: DeclarationDescriptor?, - languageVersionSettings: LanguageVersionSettings - ) { - if (listOwner is KtDeclarationWithBody) { - // JetFunction or JetPropertyAccessor - for (parameter in listOwner.valueParameters) { - if (!parameter.hasValOrVar()) { - check(parameter, trace, trace[BindingContext.VALUE_PARAMETER, parameter], languageVersionSettings) - } - } - } - val actualTargets = AnnotationChecker.getDeclarationSiteActualTargetList( - listOwner, descriptor as? ClassDescriptor, trace.bindingContext - ) - val list = listOwner.modifierList ?: return - checkModifierList(list, trace, descriptor?.containingDeclaration, actualTargets, languageVersionSettings) - } -} - - -private interface TargetAllowedPredicate { - fun isAllowed(target: KotlinTarget, languageVersionSettings: LanguageVersionSettings): Boolean -} - - -private fun always(target: KotlinTarget, vararg targets: KotlinTarget) = object : TargetAllowedPredicate { - private val targetSet = EnumSet.of(target, *targets) - - override fun isAllowed(target: KotlinTarget, languageVersionSettings: LanguageVersionSettings) = - target in targetSet -} - -private fun ifSupported(languageFeature: LanguageFeature, target: KotlinTarget, vararg targets: KotlinTarget) = - object : TargetAllowedPredicate { - private val targetSet = EnumSet.of(target, *targets) - - override fun isAllowed(target: KotlinTarget, languageVersionSettings: LanguageVersionSettings) = - languageVersionSettings.supportsFeature(languageFeature) && target in targetSet - } - -private fun or(p1: TargetAllowedPredicate, p2: TargetAllowedPredicate) = object : TargetAllowedPredicate { - override fun isAllowed(target: KotlinTarget, languageVersionSettings: LanguageVersionSettings) = - p1.isAllowed(target, languageVersionSettings) || - p2.isAllowed(target, languageVersionSettings) -} +} \ No newline at end of file diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/ModifierCheckerHelpers.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/ModifierCheckerHelpers.kt new file mode 100644 index 00000000000..2ffb6521647 --- /dev/null +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/ModifierCheckerHelpers.kt @@ -0,0 +1,400 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve; + +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget +import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.* +import java.util.* +import org.jetbrains.kotlin.resolve.KeywordType.* + +enum class KeywordType { + Inner, + Override, + Public, + Protected, + Internal, + Private, + Companion, + Final, + Vararg, + Enum, + Abstract, + Open, + Sealed, + In, + Out, + Reified, + Lateinit, + Data, + Inline, + Noinline, + Tailrec, + Suspend, + External, + Annotation, + Crossinline, + Const, + Operator, + Infix, + Header, + Impl, + Expect, + Actual, + Fun, + Value +} + +fun KeywordType.render(): String { + return toString().lowercase() +} + +enum class Compatibility { + // modifier pair is compatible: ok (default) + COMPATIBLE, + + // second is redundant to first: warning + REDUNDANT, + + // first is redundant to second: warning + REVERSE_REDUNDANT, + + // error + REPEATED, + + // pair is deprecated, will become incompatible: warning + DEPRECATED, + + // pair is incompatible: error + INCOMPATIBLE, + + // same but only for functions / properties: error + COMPATIBLE_FOR_CLASSES_ONLY +} + +val compatibilityTypeMap = hashMapOf, Compatibility>() + +fun compatibility(first: KeywordType, second: KeywordType): Compatibility { + return if (first == second) { + Compatibility.REPEATED + } else { + mutualCompatibility[Pair(first, second)] ?: Compatibility.COMPATIBLE + } +} + +// First modifier in pair should be also first in declaration +private val mutualCompatibility = buildCompatibilityMap() + +private fun buildCompatibilityMap(): Map, Compatibility> { + val result = hashMapOf, Compatibility>() + // Variance: in + out are incompatible + result += incompatibilityRegister(In, Out) + // Visibilities: incompatible + result += incompatibilityRegister(Private, Protected, Public, Internal) + // Abstract + open + final + sealed: incompatible + result += incompatibilityRegister(Abstract, Open, Final, Sealed) + // data + open, data + inner, data + abstract, data + sealed, data + inline, data + value + result += incompatibilityRegister(Data, Open) + result += incompatibilityRegister(Data, Inner) + result += incompatibilityRegister(Data, Abstract) + result += incompatibilityRegister(Data, Sealed) + result += incompatibilityRegister(Data, Inline) + result += incompatibilityRegister(Data, Value) + // open is redundant to abstract & override + result += redundantRegister(Abstract, Open) + // abstract is redundant to sealed + result += redundantRegister(Sealed, Abstract) + + // const is incompatible with abstract, open, override + result += incompatibilityRegister(Const, Abstract) + result += incompatibilityRegister(Const, Open) + result += incompatibilityRegister(Const, Override) + + // private is incompatible with override + result += incompatibilityRegister(Private, Override) + // private is compatible with open / abstract only for classes + result += compatibilityForClassesRegister(Private, Open) + result += compatibilityForClassesRegister(Private, Abstract) + + result += incompatibilityRegister(Crossinline, Noinline) + + // 1. subclasses contained inside a sealed class can not be instantiated, because their constructors needs + // an instance of an outer sealed (effectively abstract) class + // 2. subclasses of a non-top-level sealed class must be declared inside the class + // (see the KEEP https://github.com/Kotlin/KEEP/blob/master/proposals/sealed-class-inheritance.md) + result += incompatibilityRegister(Sealed, Inner) + + // header / expect / impl / actual are all incompatible + result += incompatibilityRegister(Header, Expect, Impl, Actual) + + return result +} + +private fun incompatibilityRegister(vararg list: KeywordType): Map, Compatibility> { + return compatibilityRegister(Compatibility.INCOMPATIBLE, *list) +} + +private fun redundantRegister( + sufficient: KeywordType, + redundant: KeywordType +): Map, Compatibility> { + return mapOf( + Pair(sufficient, redundant) to Compatibility.REDUNDANT, + Pair(redundant, sufficient) to Compatibility.REVERSE_REDUNDANT + ) +} + +private fun compatibilityForClassesRegister(vararg list: KeywordType) = + compatibilityRegister(Compatibility.COMPATIBLE_FOR_CLASSES_ONLY, *list) + +private fun compatibilityRegister( + compatibility: Compatibility, vararg list: KeywordType +): Map, Compatibility> { + val result = hashMapOf, Compatibility>() + for (first in list) { + for (second in list) { + if (first != second) { + result[Pair(first, second)] = compatibility + } + } + } + return result +} + +val featureDependencies = mapOf( + Suspend to listOf(LanguageFeature.Coroutines), + Inline to listOf(LanguageFeature.InlineProperties, LanguageFeature.InlineClasses), + Header to listOf(LanguageFeature.MultiPlatformProjects), + Impl to listOf(LanguageFeature.MultiPlatformProjects), + Expect to listOf(LanguageFeature.MultiPlatformProjects), + Actual to listOf(LanguageFeature.MultiPlatformProjects), + Lateinit to listOf(LanguageFeature.LateinitTopLevelProperties, LanguageFeature.LateinitLocalVariables), + Fun to listOf(LanguageFeature.FunctionalInterfaceConversion) +) + +val featureDependenciesTargets = mapOf( + LanguageFeature.InlineProperties to setOf(PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER), + LanguageFeature.LateinitLocalVariables to setOf(LOCAL_VARIABLE), + LanguageFeature.LateinitTopLevelProperties to setOf(TOP_LEVEL_PROPERTY), + LanguageFeature.InlineClasses to setOf(CLASS_ONLY), + LanguageFeature.JvmInlineValueClasses to setOf(CLASS_ONLY), + LanguageFeature.FunctionalInterfaceConversion to setOf(INTERFACE) +) + +val defaultVisibilityTargets: EnumSet = EnumSet.of( + CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS, + MEMBER_FUNCTION, TOP_LEVEL_FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, + MEMBER_PROPERTY, TOP_LEVEL_PROPERTY, CONSTRUCTOR, TYPEALIAS +) + +val possibleTargetMap = mapOf( + KeywordType.Enum to EnumSet.of(ENUM_CLASS), + Abstract to EnumSet.of( + CLASS_ONLY, + LOCAL_CLASS, + INTERFACE, + MEMBER_PROPERTY, + MEMBER_FUNCTION + ), + Open to EnumSet.of( + CLASS_ONLY, + LOCAL_CLASS, + INTERFACE, + MEMBER_PROPERTY, + MEMBER_FUNCTION + ), + Final to EnumSet.of( + CLASS_ONLY, + LOCAL_CLASS, + ENUM_CLASS, + OBJECT, + MEMBER_PROPERTY, + MEMBER_FUNCTION + ), + Sealed to EnumSet.of(CLASS_ONLY, INTERFACE), + Inner to EnumSet.of(CLASS_ONLY), + Override to EnumSet.of(MEMBER_PROPERTY, MEMBER_FUNCTION), + Private to defaultVisibilityTargets, + Public to defaultVisibilityTargets, + Internal to defaultVisibilityTargets, + Protected to EnumSet.of( + CLASS_ONLY, + OBJECT, + INTERFACE, + ENUM_CLASS, + ANNOTATION_CLASS, + MEMBER_FUNCTION, + PROPERTY_GETTER, + PROPERTY_SETTER, + MEMBER_PROPERTY, + CONSTRUCTOR, + TYPEALIAS + ), + In to EnumSet.of(TYPE_PARAMETER, TYPE_PROJECTION), + Out to EnumSet.of(TYPE_PARAMETER, TYPE_PROJECTION), + Reified to EnumSet.of(TYPE_PARAMETER), + Vararg to EnumSet.of(VALUE_PARAMETER, PROPERTY_PARAMETER), + KeywordType.Companion to EnumSet.of(OBJECT), + Lateinit to EnumSet.of(MEMBER_PROPERTY, TOP_LEVEL_PROPERTY, LOCAL_VARIABLE), + Data to EnumSet.of(CLASS_ONLY, LOCAL_CLASS), + Inline to EnumSet.of( + FUNCTION, + PROPERTY, + PROPERTY_GETTER, + PROPERTY_SETTER, + CLASS_ONLY + ), + Noinline to EnumSet.of(VALUE_PARAMETER), + Tailrec to EnumSet.of(FUNCTION), + Suspend to EnumSet.of(MEMBER_FUNCTION, TOP_LEVEL_FUNCTION, LOCAL_FUNCTION), + External to EnumSet.of( + FUNCTION, + PROPERTY, + PROPERTY_GETTER, + PROPERTY_SETTER, + CLASS + ), + KeywordType.Annotation to EnumSet.of(ANNOTATION_CLASS), // TODO: Workaround for FIR, https://youtrack.jetbrains.com/issue/KT-48157 + Crossinline to EnumSet.of(VALUE_PARAMETER), + Const to EnumSet.of(MEMBER_PROPERTY, TOP_LEVEL_PROPERTY), + Operator to EnumSet.of(FUNCTION), + Infix to EnumSet.of(FUNCTION), + Header to EnumSet.of( + TOP_LEVEL_FUNCTION, + TOP_LEVEL_PROPERTY, + CLASS_ONLY, + OBJECT, + INTERFACE, + ENUM_CLASS, + ANNOTATION_CLASS + ), + Impl to EnumSet.of( + TOP_LEVEL_FUNCTION, + MEMBER_FUNCTION, + TOP_LEVEL_PROPERTY, + MEMBER_PROPERTY, + CONSTRUCTOR, + CLASS_ONLY, + OBJECT, + INTERFACE, + ENUM_CLASS, + ANNOTATION_CLASS, + TYPEALIAS + ), + Expect to EnumSet.of( + TOP_LEVEL_FUNCTION, + TOP_LEVEL_PROPERTY, + CLASS_ONLY, + OBJECT, + INTERFACE, + ENUM_CLASS, + ANNOTATION_CLASS + ), + Actual to EnumSet.of( + TOP_LEVEL_FUNCTION, + MEMBER_FUNCTION, + TOP_LEVEL_PROPERTY, + MEMBER_PROPERTY, + CONSTRUCTOR, + CLASS_ONLY, + OBJECT, + INTERFACE, + ENUM_CLASS, + ANNOTATION_CLASS, + TYPEALIAS + ), + Fun to EnumSet.of(INTERFACE), + Value to EnumSet.of(CLASS_ONLY) +) + +// NOTE: deprecated targets must be possible! +val deprecatedTargetMap = mapOf>() + +val deprecatedParentTargetMap = mapOf>() + +val deprecatedModifierMap = mapOf( + Header to Expect, + Impl to Actual +) + +// NOTE: redundant targets must be possible! +val redundantTargetMap = mapOf>( + Open to EnumSet.of(INTERFACE) +) + +interface TargetAllowedPredicate { + fun isAllowed(target: KotlinTarget, languageVersionSettings: LanguageVersionSettings): Boolean +} + +fun always(target: KotlinTarget, vararg targets: KotlinTarget) = object : TargetAllowedPredicate { + private val targetSet = EnumSet.of(target, *targets) + + override fun isAllowed(target: KotlinTarget, languageVersionSettings: LanguageVersionSettings) = + target in targetSet +} + +fun ifSupported(languageFeature: LanguageFeature, target: KotlinTarget, vararg targets: KotlinTarget) = + object : TargetAllowedPredicate { + private val targetSet = EnumSet.of(target, *targets) + + override fun isAllowed(target: KotlinTarget, languageVersionSettings: LanguageVersionSettings) = + languageVersionSettings.supportsFeature(languageFeature) && target in targetSet + } + +fun or(p1: TargetAllowedPredicate, p2: TargetAllowedPredicate) = object : TargetAllowedPredicate { + override fun isAllowed(target: KotlinTarget, languageVersionSettings: LanguageVersionSettings) = + p1.isAllowed(target, languageVersionSettings) || + p2.isAllowed(target, languageVersionSettings) +} + +val possibleParentTargetPredicateMap = mapOf( + Inner to or( + always(CLASS_ONLY, LOCAL_CLASS, ENUM_CLASS), + ifSupported(LanguageFeature.InnerClassInEnumEntryClass, ENUM_ENTRY) + ), + Override to always( + CLASS_ONLY, + LOCAL_CLASS, + OBJECT, + OBJECT_LITERAL, + INTERFACE, + ENUM_CLASS, + ENUM_ENTRY + ), + Protected to always(CLASS_ONLY, LOCAL_CLASS, ENUM_CLASS, COMPANION_OBJECT), + Internal to always( + CLASS_ONLY, + LOCAL_CLASS, + OBJECT, + OBJECT_LITERAL, + ENUM_CLASS, + ENUM_ENTRY, + FILE + ), + Private to always( + CLASS_ONLY, + LOCAL_CLASS, + OBJECT, + OBJECT_LITERAL, + INTERFACE, + ENUM_CLASS, + ENUM_ENTRY, + FILE + ), + KeywordType.Companion to always(CLASS_ONLY, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS), + Final to always( + CLASS_ONLY, + LOCAL_CLASS, + OBJECT, + OBJECT_LITERAL, + ENUM_CLASS, + ENUM_ENTRY, + ANNOTATION_CLASS, + FILE + ), + Vararg to always(CONSTRUCTOR, FUNCTION, CLASS) +) diff --git a/compiler/testData/diagnostics/tests/valueClasses/valueClassDeclarationCheck.fir.kt b/compiler/testData/diagnostics/tests/valueClasses/valueClassDeclarationCheck.fir.kt index f6dd33b95f1..5b20950d9f5 100644 --- a/compiler/testData/diagnostics/tests/valueClasses/valueClassDeclarationCheck.fir.kt +++ b/compiler/testData/diagnostics/tests/valueClasses/valueClassDeclarationCheck.fir.kt @@ -49,4 +49,4 @@ final value class D0(val x: Int) abstract value class D2(val x: Int) sealed value class D3(val x: Int) -value data class D4(val x: String) +value data class D4(val x: String) diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 718345087c8..11d0beb8502 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -724,19 +724,19 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract class RepeatedModifier : KtFirDiagnostic() { override val diagnosticClass get() = RepeatedModifier::class - abstract val modifier: KtModifierKeywordToken + abstract val modifier: String } abstract class RedundantModifier : KtFirDiagnostic() { override val diagnosticClass get() = RedundantModifier::class - abstract val redundantModifier: KtModifierKeywordToken - abstract val conflictingModifier: KtModifierKeywordToken + abstract val redundantModifier: String + abstract val conflictingModifier: String } abstract class DeprecatedModifierPair : KtFirDiagnostic() { override val diagnosticClass get() = DeprecatedModifierPair::class - abstract val deprecatedModifier: KtModifierKeywordToken - abstract val conflictingModifier: KtModifierKeywordToken + abstract val deprecatedModifier: String + abstract val conflictingModifier: String } abstract class IncompatibleModifiers : KtFirDiagnostic() { diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 6630837d7a9..73b6a0986a1 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -1150,7 +1150,7 @@ internal class InapplicableInfixModifierImpl( } internal class RepeatedModifierImpl( - override val modifier: KtModifierKeywordToken, + override val modifier: String, firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken, ) : KtFirDiagnostic.RepeatedModifier(), KtAbstractFirDiagnostic { @@ -1158,8 +1158,8 @@ internal class RepeatedModifierImpl( } internal class RedundantModifierImpl( - override val redundantModifier: KtModifierKeywordToken, - override val conflictingModifier: KtModifierKeywordToken, + override val redundantModifier: String, + override val conflictingModifier: String, firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken, ) : KtFirDiagnostic.RedundantModifier(), KtAbstractFirDiagnostic { @@ -1167,8 +1167,8 @@ internal class RedundantModifierImpl( } internal class DeprecatedModifierPairImpl( - override val deprecatedModifier: KtModifierKeywordToken, - override val conflictingModifier: KtModifierKeywordToken, + override val deprecatedModifier: String, + override val conflictingModifier: String, firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken, ) : KtFirDiagnostic.DeprecatedModifierPair(), KtAbstractFirDiagnostic { @@ -1176,8 +1176,8 @@ internal class DeprecatedModifierPairImpl( } internal class IncompatibleModifiersImpl( - override val modifier1: KtModifierKeywordToken, - override val modifier2: KtModifierKeywordToken, + override val modifier1: String, + override val modifier2: String, firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken, ) : KtFirDiagnostic.IncompatibleModifiers(), KtAbstractFirDiagnostic {