Allow modifier 'inner' on a class nested in enum entry class

This commit is contained in:
Dmitry Petrov
2017-09-07 18:34:25 +03:00
parent 488a825349
commit ea91a0794d
8 changed files with 122 additions and 25 deletions
@@ -112,22 +112,35 @@ object ModifierCheckerCore {
OPEN_KEYWORD to EnumSet.of(INTERFACE)
)
val possibleParentTargetMap = mapOf<KtModifierKeywordToken, Set<KotlinTarget>>(
INNER_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, ENUM_CLASS),
OVERRIDE_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL,
INTERFACE, ENUM_CLASS, ENUM_ENTRY),
PROTECTED_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, ENUM_CLASS, COMPANION_OBJECT),
INTERNAL_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL,
ENUM_CLASS, ENUM_ENTRY, FILE),
PRIVATE_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL,
INTERFACE, ENUM_CLASS, ENUM_ENTRY, FILE),
COMPANION_KEYWORD to EnumSet.of(CLASS_ONLY, ENUM_CLASS, INTERFACE),
FINAL_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL,
ENUM_CLASS, ENUM_ENTRY, ANNOTATION_CLASS, FILE),
VARARG_KEYWORD to EnumSet.of(CONSTRUCTOR, FUNCTION, CLASS)
private val possibleParentTargetPredicateMap = mapOf<KtModifierKeywordToken, TargetAllowedPredicate>(
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, ENUM_CLASS, INTERFACE),
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)
)
val deprecatedParentTargetMap = mapOf<KtModifierKeywordToken, Set<KotlinTarget>>()
private val deprecatedParentTargetMap = mapOf<KtModifierKeywordToken, Set<KotlinTarget>>()
fun isPossibleParentTarget(
modifier: KtModifierKeywordToken,
parentTarget: KotlinTarget,
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()
@@ -305,7 +318,7 @@ object ModifierCheckerCore {
// Should return false if error is reported, true otherwise
private fun checkParent(trace: BindingTrace, node: ASTNode, parentDescriptor: DeclarationDescriptor?): Boolean {
private fun checkParent(trace: BindingTrace, node: ASTNode, parentDescriptor: DeclarationDescriptor?, languageVersionSettings: LanguageVersionSettings): Boolean {
val modifier = node.elementType as KtModifierKeywordToken
val actualParents: List<KotlinTarget> = when (parentDescriptor) {
is ClassDescriptor -> KotlinTarget.classActualTargets(parentDescriptor)
@@ -319,9 +332,8 @@ object ModifierCheckerCore {
trace.report(Errors.DEPRECATED_MODIFIER_CONTAINING_DECLARATION.on(node.psi, modifier, actualParents.firstOrNull()?.description ?: "this scope"))
return true
}
val possibleParents = possibleParentTargetMap[modifier] ?: return true
if (possibleParents == KotlinTarget.ALL_TARGET_SET) return true
if (actualParents.any { it in possibleParents }) 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
}
@@ -349,7 +361,7 @@ object ModifierCheckerCore {
if (second !in incorrectNodes) {
when {
!checkTarget(trace, second, actualTargets) -> incorrectNodes += second
!checkParent(trace, second, parentDescriptor) -> incorrectNodes += second
!checkParent(trace, second, parentDescriptor, languageVersionSettings) -> incorrectNodes += second
!checkLanguageLevelSupport(trace, second, languageVersionSettings, actualTargets) -> incorrectNodes += second
}
}
@@ -375,3 +387,29 @@ object ModifierCheckerCore {
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)
}