FIR: use symbols as base for OptIn-checkers

This commit is contained in:
Mikhail Glukhikh
2021-07-21 20:07:32 +03:00
parent cb847b945d
commit 73539cf342
4 changed files with 47 additions and 71 deletions
@@ -8,17 +8,12 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind 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.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.declarations.FirAnnotatedDeclaration
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.ensureResolved
object FirOptInUsageAccessChecker : FirQualifiedAccessChecker() { object FirOptInUsageAccessChecker : FirQualifiedAccessChecker() {
@OptIn(SymbolInternals::class)
override fun check(expression: FirQualifiedAccess, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(expression: FirQualifiedAccess, context: CheckerContext, reporter: DiagnosticReporter) {
val sourceKind = expression.source?.kind val sourceKind = expression.source?.kind
if (sourceKind is FirFakeSourceElementKind.DataClassGeneratedMembers || if (sourceKind is FirFakeSourceElementKind.DataClassGeneratedMembers ||
@@ -26,16 +21,14 @@ object FirOptInUsageAccessChecker : FirQualifiedAccessChecker() {
) return ) return
val reference = expression.calleeReference as? FirResolvedNamedReference ?: return val reference = expression.calleeReference as? FirResolvedNamedReference ?: return
val resolvedSymbol = reference.resolvedSymbol val resolvedSymbol = reference.resolvedSymbol
resolvedSymbol.ensureResolved(FirResolvePhase.STATUS)
val fir = resolvedSymbol.fir as? FirAnnotatedDeclaration ?: return
with(FirOptInUsageBaseChecker) { with(FirOptInUsageBaseChecker) {
if (expression is FirVariableAssignment && fir is FirProperty) { if (expression is FirVariableAssignment && resolvedSymbol is FirPropertySymbol) {
val experimentalities = fir.loadExperimentalities(context, fromSetter = true) + val experimentalities = resolvedSymbol.loadExperimentalities(context, fromSetter = true) +
loadExperimentalitiesFromTypeArguments(context, expression.typeArguments) loadExperimentalitiesFromTypeArguments(context, expression.typeArguments)
reportNotAcceptedExperimentalities(experimentalities, expression.lValue, context, reporter) reportNotAcceptedExperimentalities(experimentalities, expression.lValue, context, reporter)
return return
} }
val experimentalities = fir.loadExperimentalities(context, fromSetter = false) + val experimentalities = resolvedSymbol.loadExperimentalities(context, fromSetter = false) +
loadExperimentalitiesFromTypeArguments(context, expression.typeArguments) loadExperimentalitiesFromTypeArguments(context, expression.typeArguments)
reportNotAcceptedExperimentalities(experimentalities, expression, context, reporter) reportNotAcceptedExperimentalities(experimentalities, expression, context, reporter)
} }
@@ -27,10 +27,13 @@ import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.ProcessorAction import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.processDirectlyOverriddenFunctions import org.jetbrains.kotlin.fir.scopes.processDirectlyOverriddenFunctions
import org.jetbrains.kotlin.fir.scopes.processDirectlyOverriddenProperties import org.jetbrains.kotlin.fir.scopes.processDirectlyOverriddenProperties
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.ensureResolved import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.resolve.checkers.OptInNames
@@ -71,58 +74,61 @@ object FirOptInUsageBaseChecker {
return result return result
} }
fun FirAnnotatedDeclaration.loadExperimentalities( fun FirBasedSymbol<*>.loadExperimentalities(
context: CheckerContext, fromSetter: Boolean context: CheckerContext, fromSetter: Boolean
): Set<Experimentality> = loadExperimentalities( ): Set<Experimentality> = loadExperimentalities(
context, knownExperimentalities = null, visited = mutableSetOf(), fromSetter context, knownExperimentalities = null, visited = mutableSetOf(), fromSetter
) )
@OptIn(SymbolInternals::class) @OptIn(SymbolInternals::class)
private fun FirAnnotatedDeclaration.loadExperimentalities( private fun FirBasedSymbol<*>.loadExperimentalities(
context: CheckerContext, context: CheckerContext,
knownExperimentalities: SmartSet<Experimentality>?, knownExperimentalities: SmartSet<Experimentality>?,
visited: MutableSet<FirAnnotatedDeclaration>, visited: MutableSet<FirAnnotatedDeclaration>,
fromSetter: Boolean, fromSetter: Boolean,
): Set<Experimentality> { ): Set<Experimentality> {
if (!visited.add(this)) return emptySet() ensureResolved(FirResolvePhase.STATUS)
val fir = this.fir as? FirAnnotatedDeclaration ?: return emptySet()
if (!visited.add(fir)) return emptySet()
val result = knownExperimentalities ?: SmartSet.create() val result = knownExperimentalities ?: SmartSet.create()
val session = context.session val session = context.session
if (this is FirCallableDeclaration) { if (fir is FirCallableDeclaration) {
val parentClass = containingClass()?.toFirRegularClass(session) val parentClassSymbol = fir.containingClass()?.toSymbol(session) as? FirRegularClassSymbol
if (this.isSubstitutionOrIntersectionOverride) { if (fir.isSubstitutionOrIntersectionOverride) {
val parentClassScope = parentClass?.unsubstitutedScope(context) parentClassSymbol?.ensureResolved(FirResolvePhase.STATUS)
if (this is FirSimpleFunction) { val parentClassScope = parentClassSymbol?.unsubstitutedScope(context)
parentClassScope?.processDirectlyOverriddenFunctions(symbol) { if (this is FirNamedFunctionSymbol) {
it.fir.loadExperimentalities(context, result, visited, fromSetter = false) parentClassScope?.processDirectlyOverriddenFunctions(this) {
it.loadExperimentalities(context, result, visited, fromSetter = false)
ProcessorAction.NEXT ProcessorAction.NEXT
} }
} else if (this is FirProperty) { } else if (this is FirPropertySymbol) {
parentClassScope?.processDirectlyOverriddenProperties(symbol) { parentClassScope?.processDirectlyOverriddenProperties(this) {
it.fir.loadExperimentalities(context, result, visited, fromSetter) it.loadExperimentalities(context, result, visited, fromSetter)
ProcessorAction.NEXT ProcessorAction.NEXT
} }
} }
} }
if (this !is FirConstructor) { if (fir !is FirConstructor) {
// Note: coneType here crashes on overridden members // Note: coneType here crashes on overridden members
returnTypeRef.coneTypeSafe<ConeKotlinType>().addExperimentalities(context, result, visited) fir.returnTypeRef.coneTypeSafe<ConeKotlinType>().addExperimentalities(context, result, visited)
receiverTypeRef?.coneTypeSafe<ConeKotlinType>().addExperimentalities(context, result, visited) fir.receiverTypeRef?.coneTypeSafe<ConeKotlinType>().addExperimentalities(context, result, visited)
if (this is FirSimpleFunction) { if (fir is FirSimpleFunction) {
valueParameters.forEach { fir.valueParameters.forEach {
it.returnTypeRef.coneTypeSafe<ConeKotlinType>().addExperimentalities(context, result, visited) it.returnTypeRef.coneTypeSafe<ConeKotlinType>().addExperimentalities(context, result, visited)
} }
} }
} }
parentClass?.loadExperimentalities(context, result, visited, fromSetter = false) parentClassSymbol?.loadExperimentalities(context, result, visited, fromSetter = false)
if (fromSetter && this is FirProperty) { if (fromSetter && this is FirPropertySymbol) {
setter?.loadExperimentalities(context, result, visited, fromSetter = false) setterSymbol?.loadExperimentalities(context, result, visited, fromSetter = false)
} }
} else if (this is FirRegularClass && !this.isLocal) { } else if (this is FirRegularClassSymbol && fir is FirRegularClass && !fir.isLocal) {
val parentClassSymbol = symbol.outerClassSymbol(context) val parentClassSymbol = outerClassSymbol(context)
parentClassSymbol?.fir?.loadExperimentalities(context, result, visited, fromSetter = false) parentClassSymbol?.loadExperimentalities(context, result, visited, fromSetter = false)
} }
for (annotation in annotations) { for (annotation in fir.annotations) {
val annotationType = annotation.annotationTypeRef.coneTypeSafe<ConeClassLikeType>() val annotationType = annotation.annotationTypeRef.coneTypeSafe<ConeClassLikeType>()
if (annotation.useSiteTarget != AnnotationUseSiteTarget.PROPERTY_SETTER || fromSetter) { if (annotation.useSiteTarget != AnnotationUseSiteTarget.PROPERTY_SETTER || fromSetter) {
result.addIfNotNull( result.addIfNotNull(
@@ -133,12 +139,12 @@ object FirOptInUsageBaseChecker {
} }
} }
if (this is FirTypeAlias) { if (fir is FirTypeAlias) {
expandedTypeRef.coneType.addExperimentalities(context, result, visited) fir.expandedTypeRef.coneType.addExperimentalities(context, result, visited)
} }
if (getAnnotationByClassId(OptInNames.WAS_EXPERIMENTAL_CLASS_ID) != null) { if (fir.getAnnotationByClassId(OptInNames.WAS_EXPERIMENTAL_CLASS_ID) != null) {
val accessibility = checkSinceKotlinVersionAccessibility(context) val accessibility = fir.checkSinceKotlinVersionAccessibility(context)
if (accessibility is FirSinceKotlinAccessibility.NotAccessibleButWasExperimental) { if (accessibility is FirSinceKotlinAccessibility.NotAccessibleButWasExperimental) {
accessibility.markerClasses.forEach { accessibility.markerClasses.forEach {
it.ensureResolved(FirResolvePhase.STATUS) it.ensureResolved(FirResolvePhase.STATUS)
@@ -151,34 +157,17 @@ object FirOptInUsageBaseChecker {
return result return result
} }
@OptIn(SymbolInternals::class)
private fun ConeKotlinType?.addExperimentalities( private fun ConeKotlinType?.addExperimentalities(
context: CheckerContext, context: CheckerContext,
result: SmartSet<Experimentality>, result: SmartSet<Experimentality>,
visited: MutableSet<FirAnnotatedDeclaration> = mutableSetOf() visited: MutableSet<FirAnnotatedDeclaration> = mutableSetOf()
) { ) {
when (this) { if (this !is ConeClassLikeType) return
!is ConeClassLikeType -> return lookupTag.toSymbol(context.session)?.loadExperimentalities(
else -> { context, result, visited, fromSetter = false
val expandedType = fullyExpandedType(context.session) )
if (this === expandedType) { fullyExpandedType(context.session).typeArguments.forEach {
expandedType.lookupTag.toFirRegularClass(context.session)?.loadExperimentalities( if (!it.isStarProjection) it.type?.addExperimentalities(context, result, visited)
context, result, visited, fromSetter = false
)
typeArguments.forEach {
if (!it.isStarProjection) it.type?.addExperimentalities(context, result, visited)
}
} else {
val symbol = lookupTag.toSymbol(context.session)
if (symbol != null) {
symbol.ensureResolved(FirResolvePhase.STATUS)
symbol.fir.loadExperimentalities(context, result, visited, fromSetter = false)
}
expandedType.typeArguments.forEach {
if (!it.isStarProjection) it.type?.addExperimentalities(context, result, visited)
}
}
}
} }
} }
@@ -7,19 +7,13 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.ensureResolved
object FirOptInUsageQualifierChecker : FirResolvedQualifierChecker() { object FirOptInUsageQualifierChecker : FirResolvedQualifierChecker() {
@OptIn(SymbolInternals::class)
override fun check(expression: FirResolvedQualifier, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(expression: FirResolvedQualifier, context: CheckerContext, reporter: DiagnosticReporter) {
val symbol = expression.symbol ?: return val symbol = expression.symbol ?: return
symbol.ensureResolved(FirResolvePhase.STATUS)
val fir = symbol.fir
with(FirOptInUsageBaseChecker) { with(FirOptInUsageBaseChecker) {
val experimentalities = fir.loadExperimentalities(context, fromSetter = false) val experimentalities = symbol.loadExperimentalities(context, fromSetter = false)
reportNotAcceptedExperimentalities(experimentalities, expression, context, reporter) reportNotAcceptedExperimentalities(experimentalities, expression, context, reporter)
} }
} }
@@ -43,7 +43,7 @@ object FirOptInUsageTypeRefChecker : FirTypeRefChecker() {
} }
with(FirOptInUsageBaseChecker) { with(FirOptInUsageBaseChecker) {
val experimentalities = symbol.fir.loadExperimentalities(context, fromSetter = false) + val experimentalities = symbol.loadExperimentalities(context, fromSetter = false) +
loadExperimentalitiesFromConeArguments(context, coneType.typeArguments.toList()) loadExperimentalitiesFromConeArguments(context, coneType.typeArguments.toList())
reportNotAcceptedExperimentalities(experimentalities, typeRef, context, reporter) reportNotAcceptedExperimentalities(experimentalities, typeRef, context, reporter)
} }