FIR checker: Add utility functions for FirModifierList.

This commit is contained in:
Mark Punzalan
2021-03-16 09:54:07 +00:00
committed by Ilya Kirillov
parent 315047b164
commit 7d31ea3e8f
12 changed files with 40 additions and 37 deletions
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirModifierList.Companion.getModifierList
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
@@ -21,8 +20,7 @@ object FirConstPropertyChecker : FirPropertyChecker() {
if (!declaration.isConst) return if (!declaration.isConst) return
if (declaration.isVar) { if (declaration.isVar) {
val modifierList = with(FirModifierList) { declaration.source.getModifierList() } val constModifier = declaration.getModifier(KtTokens.CONST_KEYWORD)
val constModifier = modifierList?.modifiers?.firstOrNull { it.token == KtTokens.CONST_KEYWORD }
constModifier?.let { constModifier?.let {
reporter.reportOn(it.source, FirErrors.WRONG_MODIFIER_TARGET, it.token, "vars", context) reporter.reportOn(it.source, FirErrors.WRONG_MODIFIER_TARGET, it.token, "vars", context)
} }
@@ -32,7 +32,7 @@ object FirConstructorAllowedChecker : FirConstructorChecker() {
reporter.reportOn(source, FirErrors.NON_PRIVATE_CONSTRUCTOR_IN_ENUM, context) reporter.reportOn(source, FirErrors.NON_PRIVATE_CONSTRUCTOR_IN_ENUM, context)
} }
ClassKind.CLASS -> if (containingClass is FirRegularClass && containingClass.modality == Modality.SEALED) { ClassKind.CLASS -> if (containingClass is FirRegularClass && containingClass.modality == Modality.SEALED) {
val modifierList = with(FirModifierList) { source.getModifierList() } ?: return val modifierList = source.getModifierList() ?: return
val hasIllegalModifier = modifierList.modifiers.any { val hasIllegalModifier = modifierList.modifiers.any {
it.token != KtTokens.PROTECTED_KEYWORD && it.token != KtTokens.PRIVATE_KEYWORD it.token != KtTokens.PROTECTED_KEYWORD && it.token != KtTokens.PRIVATE_KEYWORD
} }
@@ -85,7 +85,7 @@ internal fun checkPropertyInitializer(
context: CheckerContext context: CheckerContext
) { ) {
val inInterface = containingClass?.isInterface == true val inInterface = containingClass?.isInterface == true
val hasAbstractModifier = modifierList?.modifiers?.any { it.token == KtTokens.ABSTRACT_KEYWORD } == true val hasAbstractModifier = KtTokens.ABSTRACT_KEYWORD in modifierList
val isAbstract = property.isAbstract || hasAbstractModifier val isAbstract = property.isAbstract || hasAbstractModifier
if (isAbstract) { if (isAbstract) {
if (property.initializer == null && property.delegate == null && property.returnTypeRef is FirImplicitTypeRef) { if (property.initializer == null && property.delegate == null && property.returnTypeRef is FirImplicitTypeRef) {
@@ -184,6 +184,5 @@ internal val FirClass<*>.canHaveOpenMembers: Boolean get() = modality() != Modal
internal fun FirRegularClass.isInlineOrValueClass(): Boolean { internal fun FirRegularClass.isInlineOrValueClass(): Boolean {
if (this.classKind != ClassKind.CLASS) return false if (this.classKind != ClassKind.CLASS) return false
val modifierList = with(FirModifierList) { source.getModifierList() } return isInline || hasModifier(KtTokens.VALUE_KEYWORD)
return isInline || modifierList?.modifiers?.any { it.token == KtTokens.VALUE_KEYWORD } == true
} }
@@ -173,8 +173,7 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() {
private fun FirValueParameter.isNotFinalReadOnly(primaryConstructorProperty: FirProperty?): Boolean { private fun FirValueParameter.isNotFinalReadOnly(primaryConstructorProperty: FirProperty?): Boolean {
if (primaryConstructorProperty == null) return true if (primaryConstructorProperty == null) return true
val modifierList = with(FirModifierList) { source.getModifierList() } val isOpen = hasModifier(KtTokens.OPEN_KEYWORD)
val isOpen = modifierList?.modifiers?.any { it.token == KtTokens.OPEN_KEYWORD } == true
return isVararg || !primaryConstructorProperty.isVal || isOpen return isVararg || !primaryConstructorProperty.isVal || isOpen
} }
@@ -34,8 +34,8 @@ object FirMemberFunctionsChecker : FirRegularClassChecker() {
if (source.kind is FirFakeSourceElementKind) return if (source.kind is FirFakeSourceElementKind) return
// If multiple (potentially conflicting) modality modifiers are specified, not all modifiers are recorded at `status`. // If multiple (potentially conflicting) modality modifiers are specified, not all modifiers are recorded at `status`.
// So, our source of truth should be the full modifier list retrieved from the source. // So, our source of truth should be the full modifier list retrieved from the source.
val modifierList = with(FirModifierList) { source.getModifierList() } val modifierList = source.getModifierList()
val hasAbstractModifier = modifierList?.modifiers?.any { it.token == KtTokens.ABSTRACT_KEYWORD } == true val hasAbstractModifier = KtTokens.ABSTRACT_KEYWORD in modifierList
val isAbstract = function.isAbstract || hasAbstractModifier val isAbstract = function.isAbstract || hasAbstractModifier
if (isAbstract) { if (isAbstract) {
if (!containingDeclaration.canHaveAbstractDeclaration) { if (!containingDeclaration.canHaveAbstractDeclaration) {
@@ -49,7 +49,7 @@ object FirMemberFunctionsChecker : FirRegularClassChecker() {
} }
} }
val isInsideExpectClass = isInsideExpectClass(containingDeclaration, context) val isInsideExpectClass = isInsideExpectClass(containingDeclaration, context)
val hasOpenModifier = modifierList?.modifiers?.any { it.token == KtTokens.OPEN_KEYWORD } == true val hasOpenModifier = KtTokens.OPEN_KEYWORD in modifierList
if (!function.hasBody) { if (!function.hasBody) {
if (containingDeclaration.isInterface) { if (containingDeclaration.isInterface) {
if (Visibilities.isPrivate(function.visibility)) { if (Visibilities.isPrivate(function.visibility)) {
@@ -146,7 +146,7 @@ object FirMemberPropertiesChecker : FirRegularClassChecker() {
if (source.kind is FirFakeSourceElementKind) return if (source.kind is FirFakeSourceElementKind) return
// If multiple (potentially conflicting) modality modifiers are specified, not all modifiers are recorded at `status`. // If multiple (potentially conflicting) modality modifiers are specified, not all modifiers are recorded at `status`.
// So, our source of truth should be the full modifier list retrieved from the source. // So, our source of truth should be the full modifier list retrieved from the source.
val modifierList = with(FirModifierList) { property.source.getModifierList() } val modifierList = property.source.getModifierList()
checkPropertyInitializer( checkPropertyInitializer(
containingDeclaration, containingDeclaration,
@@ -159,7 +159,7 @@ object FirMemberPropertiesChecker : FirRegularClassChecker() {
checkPropertyAccessors(property, reporter, context) checkPropertyAccessors(property, reporter, context)
checkExpectDeclarationVisibilityAndBody(property, source, reporter, context) checkExpectDeclarationVisibilityAndBody(property, source, reporter, context)
val hasAbstractModifier = modifierList?.modifiers?.any { it.token == KtTokens.ABSTRACT_KEYWORD } == true val hasAbstractModifier = KtTokens.ABSTRACT_KEYWORD in modifierList
val isAbstract = property.isAbstract || hasAbstractModifier val isAbstract = property.isAbstract || hasAbstractModifier
if (containingDeclaration.isInterface && if (containingDeclaration.isInterface &&
Visibilities.isPrivate(property.visibility) && Visibilities.isPrivate(property.visibility) &&
@@ -203,7 +203,7 @@ object FirMemberPropertiesChecker : FirRegularClassChecker() {
} }
} }
val hasOpenModifier = modifierList?.modifiers?.any { it.token == KtTokens.OPEN_KEYWORD } == true val hasOpenModifier = KtTokens.OPEN_KEYWORD in modifierList
if (hasOpenModifier && if (hasOpenModifier &&
containingDeclaration.isInterface && containingDeclaration.isInterface &&
!hasAbstractModifier && !hasAbstractModifier &&
@@ -169,7 +169,7 @@ object FirModifierChecker : FirBasicDeclarationChecker() {
if (!isDeclarationMappedToSourceCorrectly(declaration, source)) return if (!isDeclarationMappedToSourceCorrectly(declaration, source)) return
if (context.containingDeclarations.last() is FirDefaultPropertyAccessor) return if (context.containingDeclarations.last() is FirDefaultPropertyAccessor) return
val modifierList = with(FirModifierList) { source.getModifierList() } val modifierList = source.getModifierList()
modifierList?.let { checkModifiers(it, declaration, reporter, context) } modifierList?.let { checkModifiers(it, declaration, reporter, context) }
} }
@@ -12,6 +12,7 @@ import com.intellij.util.diff.FlyweightCapableTreeStructure
import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.analysis.checkers.getChildren import org.jetbrains.kotlin.fir.analysis.checkers.getChildren
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtModifierList import org.jetbrains.kotlin.psi.KtModifierList
@@ -44,19 +45,9 @@ internal sealed class FirModifierList {
} }
} }
companion object { operator fun get(token: KtModifierKeywordToken): FirModifier<*>? = modifiers.firstOrNull { it.token == token }
fun FirSourceElement?.getModifierList(): FirModifierList? {
return when (this) { operator fun contains(token: KtModifierKeywordToken): Boolean = modifiers.any { it.token == token }
null -> null
is FirPsiSourceElement<*> -> (psi as? KtModifierListOwner)?.modifierList?.let { FirPsiModifierList(it) }
is FirLightSourceElement -> {
val modifierListNode = lighterASTNode.getChildren(treeStructure).find { it?.tokenType == KtNodeTypes.MODIFIER_LIST }
?: return null
FirLightModifierList(modifierListNode, treeStructure)
}
}
}
}
} }
private val MODIFIER_KEYWORD_SET = TokenSet.orSet(KtTokens.SOFT_KEYWORDS, TokenSet.create(KtTokens.IN_KEYWORD, KtTokens.FUN_KEYWORD)) private val MODIFIER_KEYWORD_SET = TokenSet.orSet(KtTokens.SOFT_KEYWORDS, TokenSet.create(KtTokens.IN_KEYWORD, KtTokens.FUN_KEYWORD))
@@ -82,3 +73,21 @@ internal sealed class FirModifier<Node : Any>(val node: Node, val token: KtModif
abstract val source: FirSourceElement abstract val source: FirSourceElement
} }
internal fun FirSourceElement?.getModifierList(): FirModifierList? {
return when (this) {
null -> null
is FirPsiSourceElement<*> -> (psi as? KtModifierListOwner)?.modifierList?.let { FirModifierList.FirPsiModifierList(it) }
is FirLightSourceElement -> {
val modifierListNode = lighterASTNode.getChildren(treeStructure).find { it?.tokenType == KtNodeTypes.MODIFIER_LIST }
?: return null
FirModifierList.FirLightModifierList(modifierListNode, treeStructure)
}
}
}
internal operator fun FirModifierList?.contains(token: KtModifierKeywordToken): Boolean = this?.contains(token) == true
internal fun FirDeclaration.getModifier(token: KtModifierKeywordToken): FirModifier<*>? = source.getModifierList()?.get(token)
internal fun FirDeclaration.hasModifier(token: KtModifierKeywordToken): Boolean = token in source.getModifierList()
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirRealSourceElementKind import org.jetbrains.kotlin.fir.FirRealSourceElementKind
import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirModifierList.Companion.getModifierList
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
@@ -29,7 +28,7 @@ object FirOpenMemberChecker : FirClassChecker() {
memberDeclaration is FirConstructor memberDeclaration is FirConstructor
) continue ) continue
val source = memberDeclaration.source ?: continue val source = memberDeclaration.source ?: continue
if (memberDeclaration.isOpen || (source.hasOpenModifierInSource && source.shouldReportOpenFromSource)) { if (memberDeclaration.isOpen || memberDeclaration.hasModifier(KtTokens.OPEN_KEYWORD) && source.shouldReportOpenFromSource) {
if (declaration.classKind == ClassKind.OBJECT) { if (declaration.classKind == ClassKind.OBJECT) {
reporter.reportOn(source, FirErrors.NON_FINAL_MEMBER_IN_OBJECT, context) reporter.reportOn(source, FirErrors.NON_FINAL_MEMBER_IN_OBJECT, context)
} else { } else {
@@ -39,7 +38,6 @@ object FirOpenMemberChecker : FirClassChecker() {
} }
} }
private val FirSourceElement.hasOpenModifierInSource: Boolean get() = getModifierList()?.modifiers?.any { it.token == KtTokens.OPEN_KEYWORD } == true
private val FirSourceElement.shouldReportOpenFromSource: Boolean private val FirSourceElement.shouldReportOpenFromSource: Boolean
get() = when (kind) { get() = when (kind) {
FirRealSourceElementKind, FirRealSourceElementKind,
@@ -28,8 +28,7 @@ object FirTopLevelFunctionsChecker : FirFileChecker() {
if (source.kind is FirFakeSourceElementKind) return if (source.kind is FirFakeSourceElementKind) return
// If multiple (potentially conflicting) modality modifiers are specified, not all modifiers are recorded at `status`. // If multiple (potentially conflicting) modality modifiers are specified, not all modifiers are recorded at `status`.
// So, our source of truth should be the full modifier list retrieved from the source. // So, our source of truth should be the full modifier list retrieved from the source.
val modifierList = with(FirModifierList) { source.getModifierList() } if (function.hasModifier(KtTokens.ABSTRACT_KEYWORD)) return
if (modifierList?.modifiers?.any { it.token == KtTokens.ABSTRACT_KEYWORD } == true) return
if (function.isExternal) return if (function.isExternal) return
if (!function.hasBody && !function.isExpect) { if (!function.hasBody && !function.isExpect) {
reporter.reportOn(source, FirErrors.NON_MEMBER_FUNCTION_NO_BODY, function, context) reporter.reportOn(source, FirErrors.NON_MEMBER_FUNCTION_NO_BODY, function, context)
@@ -26,7 +26,7 @@ object FirTopLevelPropertiesChecker : FirFileChecker() {
if (source.kind is FirFakeSourceElementKind) return if (source.kind is FirFakeSourceElementKind) return
// If multiple (potentially conflicting) modality modifiers are specified, not all modifiers are recorded at `status`. // If multiple (potentially conflicting) modality modifiers are specified, not all modifiers are recorded at `status`.
// So, our source of truth should be the full modifier list retrieved from the source. // So, our source of truth should be the full modifier list retrieved from the source.
val modifierList = with(FirModifierList) { source.getModifierList() } val modifierList = source.getModifierList()
checkPropertyInitializer( checkPropertyInitializer(
containingClass = null, containingClass = null,
@@ -9,6 +9,8 @@ import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirMemberDeclarationChecker import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirMemberDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirModifierList import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirModifierList
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.getModifier
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.getModifierList
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
@@ -29,8 +31,7 @@ object FirJvmExternalDeclarationChecker : FirMemberDeclarationChecker() {
is FirRegularClass -> "class" is FirRegularClass -> "class"
else -> "non-function declaration" else -> "non-function declaration"
} }
val modifierList = with(FirModifierList) { source.getModifierList() } val externalModifier = declaration.getModifier(KtTokens.EXTERNAL_KEYWORD)
val externalModifier = modifierList?.modifiers?.firstOrNull { it.token == KtTokens.EXTERNAL_KEYWORD }
externalModifier?.let { externalModifier?.let {
reporter.reportOn(it.source, FirErrors.WRONG_MODIFIER_TARGET, it.token, target, context) reporter.reportOn(it.source, FirErrors.WRONG_MODIFIER_TARGET, it.token, target, context)
} }