[FIR] Cleanup and reorganize utils in :compiler:fir:checkers

This commit is contained in:
Dmitriy Novozhilov
2021-09-14 16:26:51 +03:00
committed by teamcityserver
parent 9d09b9605f
commit 0f6b6dbca3
15 changed files with 318 additions and 330 deletions
@@ -209,23 +209,7 @@ fun FirNamedFunctionSymbol.overriddenFunctions(
return overriddenFunctions
}
/**
* Returns the visibility by given KtModifierList
*/
fun KtModifierList?.getVisibility() = this?.visibilityModifierType()?.toVisibilityOrNull()
/**
* Returns Visibility by token or null
*/
fun KtModifierKeywordToken.toVisibilityOrNull(): Visibility? {
return when (this) {
KtTokens.PUBLIC_KEYWORD -> Visibilities.Public
KtTokens.PRIVATE_KEYWORD -> Visibilities.Private
KtTokens.PROTECTED_KEYWORD -> Visibilities.Protected
KtTokens.INTERNAL_KEYWORD -> Visibilities.Internal
else -> null
}
}
/**
* Returns the modality of the class
@@ -774,4 +758,4 @@ fun getActualTargetList(annotated: FirDeclaration): AnnotationTargetList {
}
}
private typealias TargetLists = AnnotationTargetLists
private typealias TargetLists = AnnotationTargetLists
@@ -41,8 +41,6 @@ private data class FirSinceKotlinValue(
val wasExperimentalMarkerClasses: List<FirRegularClassSymbol>
)
private val SINCE_KOTLIN_VERSION_NAME = Name.identifier("version")
fun FirAnnotatedDeclaration.checkSinceKotlinVersionAccessibility(context: CheckerContext): FirSinceKotlinAccessibility {
val value = getOwnSinceKotlinVersion(context.session)
val version = value?.apiVersion
@@ -67,7 +65,7 @@ private fun FirAnnotatedDeclaration.getOwnSinceKotlinVersion(session: FirSession
// TODO: use-site targeted annotations
fun FirAnnotatedDeclaration.consider() {
val sinceKotlinSingleArgument = getAnnotationByClassId(StandardClassIds.Annotations.SinceKotlin)?.findArgumentByName(SINCE_KOTLIN_VERSION_NAME)
val sinceKotlinSingleArgument = getAnnotationByClassId(StandardClassIds.Annotations.SinceKotlin)?.findArgumentByName(StandardClassIds.Annotations.ParameterNames.sinceKotlinVersion)
val apiVersion = ((sinceKotlinSingleArgument as? FirConstExpression<*>)?.value as? String)?.let(ApiVersion.Companion::parse)
if (apiVersion != null) {
// TODO: combine wasExperimentalMarkerClasses in case of several associated declarations with the same maximal API version
@@ -8,6 +8,12 @@ package org.jetbrains.kotlin.fir.analysis.checkers
import com.intellij.lang.LighterASTNode
import com.intellij.openapi.util.Ref
import com.intellij.util.diff.FlyweightCapableTreeStructure
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtModifierList
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
internal fun LighterASTNode.getChildren(tree: FlyweightCapableTreeStructure<LighterASTNode>): List<LighterASTNode?> {
val children = Ref<Array<LighterASTNode?>>()
@@ -15,3 +21,20 @@ internal fun LighterASTNode.getChildren(tree: FlyweightCapableTreeStructure<Ligh
return if (count > 0) children.get().filterNotNull() else emptyList()
}
/**
* Returns the visibility by given KtModifierList
*/
fun KtModifierList?.getVisibility() = this?.visibilityModifierType()?.toVisibilityOrNull()
/**
* Returns Visibility by token or null
*/
fun KtModifierKeywordToken.toVisibilityOrNull(): Visibility? {
return when (this) {
KtTokens.PUBLIC_KEYWORD -> Visibilities.Public
KtTokens.PRIVATE_KEYWORD -> Visibilities.Private
KtTokens.PROTECTED_KEYWORD -> Visibilities.Protected
KtTokens.INTERNAL_KEYWORD -> Visibilities.Internal
else -> null
}
}
@@ -1,256 +0,0 @@
/*
* 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.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.FirModifierList
import org.jetbrains.kotlin.fir.analysis.checkers.contains
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.hasModifier
import org.jetbrains.kotlin.fir.analysis.checkers.modality
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.containingClassForStaticMemberAttr
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeLocalVariableNoTypeOrInitializer
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
import org.jetbrains.kotlin.lexer.KtTokens
internal fun isInsideExpectClass(containingClass: FirClass, context: CheckerContext): Boolean {
return isInsideSpecificClass(containingClass, context) { klass -> klass is FirRegularClass && klass.isExpect }
}
internal fun isInsideExternalClass(containingClass: FirClass, context: CheckerContext): Boolean {
return isInsideSpecificClass(containingClass, context) { klass -> klass is FirRegularClass && klass.isExternal }
}
// Note that the class that contains the currently visiting declaration will *not* be in the context's containing declarations *yet*.
private inline fun isInsideSpecificClass(
containingClass: FirClass,
context: CheckerContext,
predicate: (FirClass) -> Boolean
): Boolean {
return predicate.invoke(containingClass) ||
context.containingDeclarations.asReversed().any { it is FirRegularClass && predicate.invoke(it) }
}
internal fun FirMemberDeclaration.isEffectivelyFinal(context: CheckerContext): Boolean {
if (this.isFinal) return true
val containingClass = context.containingDeclarations.lastOrNull() as? FirRegularClass ?: return true
if (containingClass.isEnumClass) {
// Enum class has enum entries and hence is not considered final.
return false
}
return containingClass.isFinal
}
internal fun FirMemberDeclaration.isEffectivelyExpect(
containingClass: FirClass?,
context: CheckerContext,
): Boolean {
if (this.isExpect) return true
return containingClass != null && isInsideExpectClass(containingClass, context)
}
internal fun FirMemberDeclaration.isEffectivelyExternal(
containingClass: FirClass?,
context: CheckerContext,
): Boolean {
if (this.isExternal) return true
if (this is FirPropertyAccessor) {
// Check containing property
val property = context.containingDeclarations.last() as FirProperty
return property.isEffectivelyExternal(containingClass, context)
}
if (this is FirProperty) {
// Property is effectively external if all accessors are external
if (getter?.isExternal == true && (!isVar || setter?.isExternal == true)) {
return true
}
}
return containingClass != null && isInsideExternalClass(containingClass, context)
}
// TODO: check class too
internal fun checkExpectDeclarationVisibilityAndBody(
declaration: FirMemberDeclaration,
source: FirSourceElement,
reporter: DiagnosticReporter,
context: CheckerContext
) {
if (declaration.isExpect) {
if (Visibilities.isPrivate(declaration.visibility)) {
reporter.reportOn(source, FirErrors.EXPECTED_PRIVATE_DECLARATION, context)
}
if (declaration is FirSimpleFunction && declaration.hasBody) {
reporter.reportOn(source, FirErrors.EXPECTED_DECLARATION_WITH_BODY, context)
}
}
}
// Matched FE 1.0's [DeclarationsChecker#checkPropertyInitializer].
internal fun checkPropertyInitializer(
containingClass: FirClass?,
property: FirProperty,
modifierList: FirModifierList?,
isInitialized: Boolean,
reporter: DiagnosticReporter,
context: CheckerContext,
reachable: Boolean = true
) {
val inInterface = containingClass?.isInterface == true
val hasAbstractModifier = KtTokens.ABSTRACT_KEYWORD in modifierList
val isAbstract = property.isAbstract || hasAbstractModifier
if (isAbstract) {
val returnTypeRef = property.returnTypeRef
if (property.initializer == null &&
property.delegate == null &&
returnTypeRef is FirErrorTypeRef && returnTypeRef.diagnostic is ConeLocalVariableNoTypeOrInitializer
) {
property.source?.let {
reporter.reportOn(it, FirErrors.PROPERTY_WITH_NO_TYPE_NO_INITIALIZER, context)
}
}
return
}
val backingFieldRequired = property.hasBackingField
if (inInterface && backingFieldRequired && property.hasAccessorImplementation) {
property.source?.let {
reporter.reportOn(it, FirErrors.BACKING_FIELD_IN_INTERFACE, context)
}
}
val isExpect = property.isEffectivelyExpect(containingClass, context)
when {
property.initializer != null -> {
property.initializer?.source?.let {
when {
inInterface -> {
reporter.reportOn(it, FirErrors.PROPERTY_INITIALIZER_IN_INTERFACE, context)
}
isExpect -> {
reporter.reportOn(it, FirErrors.EXPECTED_PROPERTY_INITIALIZER, context)
}
!backingFieldRequired -> {
reporter.reportOn(it, FirErrors.PROPERTY_INITIALIZER_NO_BACKING_FIELD, context)
}
property.receiverTypeRef != null -> {
reporter.reportOn(it, FirErrors.EXTENSION_PROPERTY_WITH_BACKING_FIELD, context)
}
}
}
}
property.delegate != null -> {
property.delegate?.source?.let {
when {
inInterface -> {
reporter.reportOn(it, FirErrors.DELEGATED_PROPERTY_IN_INTERFACE, context)
}
isExpect -> {
reporter.reportOn(it, FirErrors.EXPECTED_DELEGATED_PROPERTY, context)
}
}
}
}
else -> {
val propertySource = property.source ?: return
val isExternal = property.isEffectivelyExternal(containingClass, context)
if (
backingFieldRequired &&
!inInterface &&
!property.isLateInit &&
!isExpect &&
!isInitialized &&
!isExternal &&
!property.hasExplicitBackingField
) {
if (property.receiverTypeRef != null && !property.hasAccessorImplementation) {
reporter.reportOn(propertySource, FirErrors.EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT, context)
} else if (reachable) { // TODO: can be suppressed not to report diagnostics about no body
if (containingClass == null || property.hasAccessorImplementation) {
reporter.reportOn(propertySource, FirErrors.MUST_BE_INITIALIZED, context)
} else {
reporter.reportOn(propertySource, FirErrors.MUST_BE_INITIALIZED_OR_BE_ABSTRACT, context)
}
}
}
if (property.isLateInit) {
if (isExpect) {
reporter.reportOn(propertySource, FirErrors.EXPECTED_LATEINIT_PROPERTY, context)
}
// TODO: like [BindingContext.MUST_BE_LATEINIT], we should consider variable with uninitialized error.
if (backingFieldRequired && !inInterface && isInitialized) {
reporter.reportOn(propertySource, FirErrors.UNNECESSARY_LATEINIT, context)
}
}
}
}
}
private val FirProperty.hasAccessorImplementation: Boolean
get() = (getter !is FirDefaultPropertyAccessor && getter?.hasBody == true) ||
(setter !is FirDefaultPropertyAccessor && setter?.hasBody == true)
internal val FirClass.canHaveOpenMembers: Boolean get() = modality() != Modality.FINAL || classKind == ClassKind.ENUM_CLASS
internal fun FirRegularClass.isInlineOrValueClass(): Boolean {
if (this.classKind != ClassKind.CLASS) return false
return isInline || hasModifier(KtTokens.VALUE_KEYWORD)
}
internal fun FirRegularClassSymbol.isInlineOrValueClass(): Boolean {
if (this.classKind != ClassKind.CLASS) return false
return isInline
}
internal val FirDeclaration.isEnumEntryInitializer: Boolean
get() {
if (this !is FirConstructor || !this.isPrimary) return false
return (containingClassForStaticMemberAttr as? ConeClassLookupTagWithFixedSymbol)?.symbol?.classKind == ClassKind.ENUM_ENTRY
}
// contract: returns(true) implies (this is FirMemberDeclaration<*>)
internal val FirDeclaration.isLocalMember: Boolean
get() = when (this) {
is FirProperty -> this.isLocal
is FirRegularClass -> this.isLocal
is FirSimpleFunction -> this.isLocal
else -> false
}
internal val FirBasedSymbol<*>.isLocalMember: Boolean
get() = when (this) {
is FirPropertySymbol -> this.isLocal
is FirRegularClassSymbol -> this.isLocal
is FirNamedFunctionSymbol -> this.isLocal
else -> false
}
internal val FirCallableDeclaration.isExtensionMember: Boolean
get() {
return receiverTypeRef != null && dispatchReceiverType != null
}
internal val FirCallableSymbol<*>.isExtensionMember: Boolean
get() {
return resolvedReceiverTypeRef != null && dispatchReceiverType != null
}
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.isInlineOnly
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
import org.jetbrains.kotlin.fir.analysis.checkers.util.checkChildrenWithCustomVisitor
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.fir.types.isMarkedNullable
import org.jetbrains.kotlin.fir.types.isNullable
import org.jetbrains.kotlin.fir.types.toSymbol
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor
import org.jetbrains.kotlin.fir.visitors.FirVisitor
import org.jetbrains.kotlin.util.OperatorNameConventions
object FirInlineDeclarationChecker : FirFunctionChecker() {
@@ -503,4 +504,16 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
reporter.reportOn(declaration.source, FirErrors.OVERRIDE_BY_INLINE, context)
}
}
private fun FirElement.checkChildrenWithCustomVisitor(
parentContext: CheckerContext,
visitorVoid: FirVisitor<Unit, CheckerContext>
) {
val collectingVisitor = object : AbstractDiagnosticCollectorVisitor(parentContext) {
override fun checkElement(element: FirElement) {
element.accept(visitorVoid, context)
}
}
this.accept(collectingVisitor, null)
}
}
@@ -26,41 +26,41 @@ object FirOuterClassArgumentsRequiredChecker : FirRegularClassChecker() {
checkOuterClassArgumentsRequired(superTypeRef, declaration, context, reporter)
}
}
}
private fun checkOuterClassArgumentsRequired(
typeRef: FirTypeRef,
declaration: FirRegularClass?,
context: CheckerContext,
reporter: DiagnosticReporter
) {
if (typeRef !is FirResolvedTypeRef || typeRef is FirErrorTypeRef) {
return
}
private fun checkOuterClassArgumentsRequired(
typeRef: FirTypeRef,
declaration: FirRegularClass?,
context: CheckerContext,
reporter: DiagnosticReporter
) {
if (typeRef !is FirResolvedTypeRef || typeRef is FirErrorTypeRef) {
return
}
val type: ConeKotlinType = typeRef.type
val delegatedTypeRef = typeRef.delegatedTypeRef
val type: ConeKotlinType = typeRef.type
val delegatedTypeRef = typeRef.delegatedTypeRef
if (delegatedTypeRef is FirUserTypeRef && type is ConeClassLikeType) {
val symbol = type.lookupTag.toSymbol(context.session)
if (delegatedTypeRef is FirUserTypeRef && type is ConeClassLikeType) {
val symbol = type.lookupTag.toSymbol(context.session)
if (symbol is FirRegularClassSymbol) {
val typeArguments = delegatedTypeRef.qualifier.toTypeProjections()
val typeParameters = symbol.typeParameterSymbols
if (symbol is FirRegularClassSymbol) {
val typeArguments = delegatedTypeRef.qualifier.toTypeProjections()
val typeParameters = symbol.typeParameterSymbols
for (index in typeArguments.size until typeParameters.size) {
val typeParameter = typeParameters[index]
if (!isValidTypeParameterFromOuterClass(typeParameter, declaration, context.session)) {
val outerClass = typeParameter.containingDeclarationSymbol as? FirRegularClassSymbol ?: break
reporter.reportOn(typeRef.source, FirErrors.OUTER_CLASS_ARGUMENTS_REQUIRED, outerClass, context)
break
for (index in typeArguments.size until typeParameters.size) {
val typeParameter = typeParameters[index]
if (!isValidTypeParameterFromOuterClass(typeParameter, declaration, context.session)) {
val outerClass = typeParameter.containingDeclarationSymbol as? FirRegularClassSymbol ?: break
reporter.reportOn(typeRef.source, FirErrors.OUTER_CLASS_ARGUMENTS_REQUIRED, outerClass, context)
break
}
}
}
}
}
for (index in type.typeArguments.indices) {
val firTypeRefSource = extractArgumentTypeRefAndSource(typeRef, index) ?: continue
firTypeRefSource.typeRef?.let { checkOuterClassArgumentsRequired(it, declaration, context, reporter) }
for (index in type.typeArguments.indices) {
val firTypeRefSource = extractArgumentTypeRefAndSource(typeRef, index) ?: continue
firTypeRefSource.typeRef?.let { checkOuterClassArgumentsRequired(it, declaration, context, reporter) }
}
}
}
}
@@ -5,13 +5,23 @@
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.FirModifierList
import org.jetbrains.kotlin.fir.analysis.checkers.contains
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.getModifierList
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.analysis.diagnostics.withSuppressedDiagnostics
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeLocalVariableNoTypeOrInitializer
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
import org.jetbrains.kotlin.lexer.KtTokens
// See old FE's [DeclarationsChecker]
object FirTopLevelPropertiesChecker : FirFileChecker() {
@@ -43,3 +53,125 @@ object FirTopLevelPropertiesChecker : FirFileChecker() {
}
}
}
// TODO: check class too
internal fun checkExpectDeclarationVisibilityAndBody(
declaration: FirMemberDeclaration,
source: FirSourceElement,
reporter: DiagnosticReporter,
context: CheckerContext
) {
if (declaration.isExpect) {
if (Visibilities.isPrivate(declaration.visibility)) {
reporter.reportOn(source, FirErrors.EXPECTED_PRIVATE_DECLARATION, context)
}
if (declaration is FirSimpleFunction && declaration.hasBody) {
reporter.reportOn(source, FirErrors.EXPECTED_DECLARATION_WITH_BODY, context)
}
}
}
// Matched FE 1.0's [DeclarationsChecker#checkPropertyInitializer].
internal fun checkPropertyInitializer(
containingClass: FirClass?,
property: FirProperty,
modifierList: FirModifierList?,
isInitialized: Boolean,
reporter: DiagnosticReporter,
context: CheckerContext,
reachable: Boolean = true
) {
val inInterface = containingClass?.isInterface == true
val hasAbstractModifier = KtTokens.ABSTRACT_KEYWORD in modifierList
val isAbstract = property.isAbstract || hasAbstractModifier
if (isAbstract) {
val returnTypeRef = property.returnTypeRef
if (property.initializer == null &&
property.delegate == null &&
returnTypeRef is FirErrorTypeRef && returnTypeRef.diagnostic is ConeLocalVariableNoTypeOrInitializer
) {
property.source?.let {
reporter.reportOn(it, FirErrors.PROPERTY_WITH_NO_TYPE_NO_INITIALIZER, context)
}
}
return
}
val backingFieldRequired = property.hasBackingField
if (inInterface && backingFieldRequired && property.hasAccessorImplementation) {
property.source?.let {
reporter.reportOn(it, FirErrors.BACKING_FIELD_IN_INTERFACE, context)
}
}
val isExpect = property.isEffectivelyExpect(containingClass, context)
when {
property.initializer != null -> {
property.initializer?.source?.let {
when {
inInterface -> {
reporter.reportOn(it, FirErrors.PROPERTY_INITIALIZER_IN_INTERFACE, context)
}
isExpect -> {
reporter.reportOn(it, FirErrors.EXPECTED_PROPERTY_INITIALIZER, context)
}
!backingFieldRequired -> {
reporter.reportOn(it, FirErrors.PROPERTY_INITIALIZER_NO_BACKING_FIELD, context)
}
property.receiverTypeRef != null -> {
reporter.reportOn(it, FirErrors.EXTENSION_PROPERTY_WITH_BACKING_FIELD, context)
}
}
}
}
property.delegate != null -> {
property.delegate?.source?.let {
when {
inInterface -> {
reporter.reportOn(it, FirErrors.DELEGATED_PROPERTY_IN_INTERFACE, context)
}
isExpect -> {
reporter.reportOn(it, FirErrors.EXPECTED_DELEGATED_PROPERTY, context)
}
}
}
}
else -> {
val propertySource = property.source ?: return
val isExternal = property.isEffectivelyExternal(containingClass, context)
if (
backingFieldRequired &&
!inInterface &&
!property.isLateInit &&
!isExpect &&
!isInitialized &&
!isExternal &&
!property.hasExplicitBackingField
) {
if (property.receiverTypeRef != null && !property.hasAccessorImplementation) {
reporter.reportOn(propertySource, FirErrors.EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT, context)
} else if (reachable) { // TODO: can be suppressed not to report diagnostics about no body
if (containingClass == null || property.hasAccessorImplementation) {
reporter.reportOn(propertySource, FirErrors.MUST_BE_INITIALIZED, context)
} else {
reporter.reportOn(propertySource, FirErrors.MUST_BE_INITIALIZED_OR_BE_ABSTRACT, context)
}
}
}
if (property.isLateInit) {
if (isExpect) {
reporter.reportOn(propertySource, FirErrors.EXPECTED_LATEINIT_PROPERTY, context)
}
// TODO: like [BindingContext.MUST_BE_LATEINIT], we should consider variable with uninitialized error.
if (backingFieldRequired && !inInterface && isInitialized) {
reporter.reportOn(propertySource, FirErrors.UNNECESSARY_LATEINIT, context)
}
}
}
}
}
private val FirProperty.hasAccessorImplementation: Boolean
get() = (getter !is FirDefaultPropertyAccessor && getter?.hasBody == true) ||
(setter !is FirDefaultPropertyAccessor && setter?.hasBody == true)
@@ -0,0 +1,115 @@
/*
* 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.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.hasModifier
import org.jetbrains.kotlin.fir.analysis.checkers.modality
import org.jetbrains.kotlin.fir.containingClassForStaticMemberAttr
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.lexer.KtTokens
internal fun isInsideExpectClass(containingClass: FirClass, context: CheckerContext): Boolean {
return isInsideSpecificClass(containingClass, context) { klass -> klass is FirRegularClass && klass.isExpect }
}
internal fun isInsideExternalClass(containingClass: FirClass, context: CheckerContext): Boolean {
return isInsideSpecificClass(containingClass, context) { klass -> klass is FirRegularClass && klass.isExternal }
}
// Note that the class that contains the currently visiting declaration will *not* be in the context's containing declarations *yet*.
private inline fun isInsideSpecificClass(
containingClass: FirClass,
context: CheckerContext,
predicate: (FirClass) -> Boolean
): Boolean {
return predicate.invoke(containingClass) ||
context.containingDeclarations.asReversed().any { it is FirRegularClass && predicate.invoke(it) }
}
internal fun FirMemberDeclaration.isEffectivelyFinal(context: CheckerContext): Boolean {
if (this.isFinal) return true
val containingClass = context.containingDeclarations.lastOrNull() as? FirRegularClass ?: return true
if (containingClass.isEnumClass) {
// Enum class has enum entries and hence is not considered final.
return false
}
return containingClass.isFinal
}
internal fun FirMemberDeclaration.isEffectivelyExpect(
containingClass: FirClass?,
context: CheckerContext,
): Boolean {
if (this.isExpect) return true
return containingClass != null && isInsideExpectClass(containingClass, context)
}
internal fun FirMemberDeclaration.isEffectivelyExternal(
containingClass: FirClass?,
context: CheckerContext,
): Boolean {
if (this.isExternal) return true
if (this is FirPropertyAccessor) {
// Check containing property
val property = context.containingDeclarations.last() as FirProperty
return property.isEffectivelyExternal(containingClass, context)
}
if (this is FirProperty) {
// Property is effectively external if all accessors are external
if (getter?.isExternal == true && (!isVar || setter?.isExternal == true)) {
return true
}
}
return containingClass != null && isInsideExternalClass(containingClass, context)
}
internal val FirClass.canHaveOpenMembers: Boolean get() = modality() != Modality.FINAL || classKind == ClassKind.ENUM_CLASS
internal fun FirRegularClass.isInlineOrValueClass(): Boolean {
if (this.classKind != ClassKind.CLASS) return false
return isInline || hasModifier(KtTokens.VALUE_KEYWORD)
}
internal fun FirRegularClassSymbol.isInlineOrValueClass(): Boolean {
if (this.classKind != ClassKind.CLASS) return false
return isInline
}
internal val FirDeclaration.isEnumEntryInitializer: Boolean
get() {
if (this !is FirConstructor || !this.isPrimary) return false
return (containingClassForStaticMemberAttr as? ConeClassLookupTagWithFixedSymbol)?.symbol?.classKind == ClassKind.ENUM_ENTRY
}
// contract: returns(true) implies (this is FirMemberDeclaration<*>)
internal val FirDeclaration.isLocalMember: Boolean
get() = symbol.isLocalMember
internal val FirBasedSymbol<*>.isLocalMember: Boolean
get() = when (this) {
is FirPropertySymbol -> this.isLocal
is FirRegularClassSymbol -> this.isLocal
is FirNamedFunctionSymbol -> this.isLocal
else -> false
}
internal val FirCallableDeclaration.isExtensionMember: Boolean
get() = symbol.isExtensionMember
internal val FirCallableSymbol<*>.isExtensionMember: Boolean
get() = resolvedReceiverTypeRef != null && dispatchReceiverType != null
@@ -1,23 +0,0 @@
/*
* 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.fir.analysis.checkers.util
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor
import org.jetbrains.kotlin.fir.visitors.FirVisitor
fun FirElement.checkChildrenWithCustomVisitor(
parentContext: CheckerContext,
visitorVoid: FirVisitor<Unit, CheckerContext>
) {
val collectingVisitor = object : AbstractDiagnosticCollectorVisitor(parentContext) {
override fun checkElement(element: FirElement) {
element.accept(visitorVoid, context)
}
}
this.accept(collectingVisitor, null)
}
@@ -173,6 +173,8 @@ object StandardClassIds {
val targetAllowedTargets = Name.identifier("allowedTargets")
val jvmNameName = Name.identifier("name")
val sinceKotlinVersion = Name.identifier("version")
val deprecatedMessage = Name.identifier("message")
val deprecatedLevel = Name.identifier("level")