FIR checker: Add utility functions for FirModifierList.
This commit is contained in:
committed by
Ilya Kirillov
parent
315047b164
commit
7d31ea3e8f
+1
-3
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -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
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -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)) {
|
||||||
|
|||||||
+3
-3
@@ -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 &&
|
||||||
|
|||||||
+1
-1
@@ -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) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+22
-13
@@ -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()
|
||||||
|
|||||||
+1
-3
@@ -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,
|
||||||
|
|||||||
+1
-2
@@ -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)
|
||||||
|
|||||||
+1
-1
@@ -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,
|
||||||
|
|||||||
+3
-2
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user