[FIR] Fix modifying CheckerContext inside checkers

`InlineFunctionBodyContext` was not moved
outside the class to preserve as much
git blame info as possible.

^KT-56460 Fixed
This commit is contained in:
Nikolay Lunyak
2023-06-28 18:12:48 +03:00
committed by Space Team
parent ed2f0c295b
commit 8c5b2ae85b
16 changed files with 268 additions and 148 deletions
@@ -5,19 +5,15 @@
package org.jetbrains.kotlin.fir.analysis.jvm.checkers
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.FirInlineCheckerPlatformSpecificComponent
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirInlineDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalMember
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
class FirJvmInlineCheckerComponent : FirInlineCheckerPlatformSpecificComponent() {
override fun isGenerallyOk(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter): Boolean {
@@ -30,8 +26,6 @@ class FirJvmInlineCheckerComponent : FirInlineCheckerPlatformSpecificComponent()
}
}
override val inlineVisitor get() = ::JvmInlineVisitor
override fun checkSuspendFunctionalParameterWithDefaultValue(
param: FirValueParameter,
context: CheckerContext,
@@ -69,33 +63,3 @@ class FirJvmInlineCheckerComponent : FirInlineCheckerPlatformSpecificComponent()
}
}
}
class JvmInlineVisitor(
inlineFunction: FirFunction,
inlineFunEffectiveVisibility: EffectiveVisibility,
inalienableParameters: List<FirValueParameterSymbol>,
session: FirSession,
reporter: DiagnosticReporter
) : FirInlineDeclarationChecker.BasicInlineVisitor(
inlineFunction,
inlineFunEffectiveVisibility,
inalienableParameters,
session,
reporter
) {
override fun visitRegularClass(regularClass: FirRegularClass, data: CheckerContext) {
if (!regularClass.classKind.isSingleton && data.containingDeclarations.lastOrNull() === inlineFunction) {
reporter.reportOn(regularClass.source, FirErrors.NOT_YET_SUPPORTED_IN_INLINE, "Local classes", data)
} else {
super.visitRegularClass(regularClass, data)
}
}
override fun visitSimpleFunction(simpleFunction: FirSimpleFunction, data: CheckerContext) {
if (data.containingDeclarations.lastOrNull() === inlineFunction) {
reporter.reportOn(simpleFunction.source, FirErrors.NOT_YET_SUPPORTED_IN_INLINE, "Local functions", data)
} else {
super.visitSimpleFunction(simpleFunction, data)
}
}
}
@@ -31,6 +31,7 @@ object JvmDeclarationCheckers : DeclarationCheckers() {
FirJvmRecordChecker,
FirJvmInlineApplicabilityChecker,
FirJvmConflictsChecker,
FirInlineBodyRegularClassChecker,
)
override val propertyCheckers: Set<FirPropertyChecker>
@@ -49,4 +50,9 @@ object JvmDeclarationCheckers : DeclarationCheckers() {
get() = setOf(
FirUpperBoundsChecker,
)
override val simpleFunctionCheckers: Set<FirSimpleFunctionChecker>
get() = setOf(
FirInlineBodySimpleFunctionChecker,
)
}
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2023 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.jvm.checkers.declaration
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirRegularClassChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
object FirInlineBodyRegularClassChecker : FirRegularClassChecker() {
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
val inlineFunctionBodyContext = context.inlineFunctionBodyContext ?: return
if (!declaration.classKind.isSingleton && context.containingDeclarations.lastOrNull() === inlineFunctionBodyContext.inlineFunction) {
reporter.reportOn(declaration.source, FirErrors.NOT_YET_SUPPORTED_IN_INLINE, "Local classes", context)
}
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2023 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.jvm.checkers.declaration
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirSimpleFunctionChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
object FirInlineBodySimpleFunctionChecker : FirSimpleFunctionChecker() {
override fun check(declaration: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) {
val inlineFunctionBodyContext = context.inlineFunctionBodyContext ?: return
if (context.containingDeclarations.lastOrNull() === inlineFunctionBodyContext.inlineFunction) {
reporter.reportOn(declaration.source, FirErrors.NOT_YET_SUPPORTED_IN_INLINE, "Local functions", context)
}
}
}
@@ -5,6 +5,9 @@
package org.jetbrains.kotlin.fir.analysis.checkers
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirInlineBodyQualifiedAccessExpressionChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirInlineBodyResolvedQualifierChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirInlineBodyVariableAssignmentChecker
import org.jetbrains.kotlin.fir.analysis.checkers.expression.*
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirCommaInWhenConditionChecker
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirConfusingWhenBranchSyntaxChecker
@@ -49,6 +52,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
FirSuspendCallChecker,
FirLateinitIntrinsicApplicabilityChecker,
FirAbstractClassInstantiationChecker,
FirInlineBodyQualifiedAccessExpressionChecker,
)
override val callCheckers: Set<FirCallChecker>
@@ -82,6 +86,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
get() = setOf(
FirReassignmentAndInvisibleSetterChecker,
FirAssignmentTypeMismatchChecker,
FirInlineBodyVariableAssignmentChecker,
)
override val whenExpressionCheckers: Set<FirWhenExpressionChecker>
@@ -152,6 +157,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
FirOptInUsageQualifierChecker,
FirDeprecatedQualifierChecker,
FirVisibilityQualifierChecker,
FirInlineBodyResolvedQualifierChecker,
)
override val equalityOperatorCallCheckers: Set<FirEqualityOperatorCallChecker>
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirInlineDeclarationChecker
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
@@ -19,8 +18,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
abstract class FirInlineCheckerPlatformSpecificComponent : FirSessionComponent {
open fun isGenerallyOk(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter): Boolean = true
open val inlineVisitor get() = FirInlineDeclarationChecker::BasicInlineVisitor
open fun checkSuspendFunctionalParameterWithDefaultValue(
param: FirValueParameter,
context: CheckerContext,
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirInlineDeclarationChecker
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.expressions.FirGetClassCall
@@ -40,6 +41,7 @@ abstract class CheckerContext : DiagnosticContext {
abstract val annotationContainers: List<FirAnnotationContainer>
abstract val containingElements: List<FirElement>
abstract val isContractBody: Boolean
abstract val inlineFunctionBodyContext: FirInlineDeclarationChecker.InlineFunctionBodyContext?
// Suppress
abstract val suppressedDiagnostics: Set<String>
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.context
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirInlineDeclarationChecker
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.expressions.FirGetClassCall
@@ -66,6 +67,10 @@ abstract class CheckerContextForProvider(
abstract fun exitContractBody(): CheckerContextForProvider
abstract fun setInlineFunctionBodyContext(context: FirInlineDeclarationChecker.InlineFunctionBodyContext): CheckerContextForProvider
abstract fun unsetInlineFunctionBodyContext(): CheckerContextForProvider
abstract fun enterFile(file: FirFile): CheckerContextForProvider
abstract fun exitFile(file: FirFile): CheckerContextForProvider
@@ -9,8 +9,11 @@ import kotlinx.collections.immutable.PersistentSet
import kotlinx.collections.immutable.persistentSetOf
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirInlineDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.createInlineFunctionBodyContext
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.expressions.FirGetClassCall
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.resolve.PersistentImplicitReceiverStack
@@ -27,6 +30,7 @@ class MutableCheckerContext private constructor(
override val annotationContainers: MutableList<FirAnnotationContainer>,
override val containingElements: MutableList<FirElement>,
override var isContractBody: Boolean,
override var inlineFunctionBodyContext: FirInlineDeclarationChecker.InlineFunctionBodyContext?,
override var containingFile: FirFile?,
sessionHolder: SessionHolder,
returnTypeCalculator: ReturnTypeCalculator,
@@ -43,6 +47,7 @@ class MutableCheckerContext private constructor(
mutableListOf(),
mutableListOf(),
isContractBody = false,
inlineFunctionBodyContext = null,
containingFile = null,
sessionHolder,
returnTypeCalculator,
@@ -61,6 +66,7 @@ class MutableCheckerContext private constructor(
annotationContainers,
containingElements,
isContractBody,
inlineFunctionBodyContext,
containingFile,
sessionHolder,
returnTypeCalculator,
@@ -132,6 +138,7 @@ class MutableCheckerContext private constructor(
annotationContainers,
containingElements,
isContractBody,
inlineFunctionBodyContext,
containingFile,
sessionHolder,
returnTypeCalculator,
@@ -154,6 +161,16 @@ class MutableCheckerContext private constructor(
return this
}
override fun setInlineFunctionBodyContext(context: FirInlineDeclarationChecker.InlineFunctionBodyContext): CheckerContextForProvider {
inlineFunctionBodyContext = context
return this
}
override fun unsetInlineFunctionBodyContext(): CheckerContextForProvider {
inlineFunctionBodyContext = null
return this
}
override fun enterFile(file: FirFile): CheckerContextForProvider {
containingFile = file
return this
@@ -11,8 +11,11 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentSetOf
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirInlineDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.createInlineFunctionBodyContext
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.expressions.FirGetClassCall
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.resolve.PersistentImplicitReceiverStack
@@ -29,6 +32,7 @@ class PersistentCheckerContext private constructor(
override val annotationContainers: PersistentList<FirAnnotationContainer>,
override val containingElements: PersistentList<FirElement>,
override val isContractBody: Boolean,
override val inlineFunctionBodyContext: FirInlineDeclarationChecker.InlineFunctionBodyContext?,
sessionHolder: SessionHolder,
returnTypeCalculator: ReturnTypeCalculator,
override val suppressedDiagnostics: PersistentSet<String>,
@@ -45,6 +49,7 @@ class PersistentCheckerContext private constructor(
persistentListOf(),
persistentListOf(),
isContractBody = false,
inlineFunctionBodyContext = null,
sessionHolder,
returnTypeCalculator,
persistentSetOf(),
@@ -108,6 +113,7 @@ class PersistentCheckerContext private constructor(
containingElements: PersistentList<FirElement> = this.containingElements,
containingDeclarations: PersistentList<FirDeclaration> = this.containingDeclarations,
isContractBody: Boolean = this.isContractBody,
inlineFunctionBodyContext: FirInlineDeclarationChecker.InlineFunctionBodyContext? = this.inlineFunctionBodyContext,
allInfosSuppressed: Boolean = this.allInfosSuppressed,
allWarningsSuppressed: Boolean = this.allWarningsSuppressed,
allErrorsSuppressed: Boolean = this.allErrorsSuppressed,
@@ -122,6 +128,7 @@ class PersistentCheckerContext private constructor(
annotationContainers,
containingElements,
isContractBody,
inlineFunctionBodyContext,
sessionHolder,
returnTypeCalculator,
suppressedDiagnostics,
@@ -139,6 +146,11 @@ class PersistentCheckerContext private constructor(
override fun exitContractBody(): CheckerContextForProvider = toggleContractBody(newValue = false)
override fun setInlineFunctionBodyContext(context: FirInlineDeclarationChecker.InlineFunctionBodyContext) =
copy(inlineFunctionBodyContext = context)
override fun unsetInlineFunctionBodyContext(): CheckerContextForProvider = copy(inlineFunctionBodyContext = null)
override fun enterFile(file: FirFile): CheckerContextForProvider = copy(containingFile = file)
override fun exitFile(file: FirFile): CheckerContextForProvider = copy(containingFile = null)
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2023 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.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessExpressionChecker
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
object FirInlineBodyQualifiedAccessExpressionChecker : FirQualifiedAccessExpressionChecker() {
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
val inlineFunctionBodyContext = context.inlineFunctionBodyContext ?: return
val targetSymbol = expression.toResolvedCallableSymbol()
inlineFunctionBodyContext.checkQualifiedAccess(expression, targetSymbol, context, reporter)
inlineFunctionBodyContext.checkReceiversOfQualifiedAccessExpression(expression, targetSymbol, context, reporter)
if (expression is FirFunctionCall) {
inlineFunctionBodyContext.checkArgumentsOfCall(expression, targetSymbol, context, reporter)
}
}
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2023 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.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirResolvedQualifierChecker
import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
object FirInlineBodyResolvedQualifierChecker : FirResolvedQualifierChecker() {
override fun check(expression: FirResolvedQualifier, context: CheckerContext, reporter: DiagnosticReporter) {
val inlineFunctionBodyContext = context.inlineFunctionBodyContext ?: return
val accessedClass = expression.symbol ?: return
val source = expression.source ?: return
if (accessedClass.isCompanion) {
inlineFunctionBodyContext.checkAccessedDeclaration(source, accessedClass, accessedClass.visibility, context, reporter)
}
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2023 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.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirVariableAssignmentChecker
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.expressions.calleeReference
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
object FirInlineBodyVariableAssignmentChecker : FirVariableAssignmentChecker() {
override fun check(expression: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) {
val inlineFunctionBodyContext = context.inlineFunctionBodyContext ?: return
val propertySymbol = expression.calleeReference?.toResolvedCallableSymbol() as? FirPropertySymbol ?: return
val setterSymbol = propertySymbol.setterSymbol ?: return
inlineFunctionBodyContext.checkQualifiedAccess(expression, setterSymbol, context, reporter)
}
}
@@ -16,17 +16,17 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContextForProvider
import org.jetbrains.kotlin.fir.analysis.checkers.inlineCheckerExtension
import org.jetbrains.kotlin.fir.analysis.checkers.isInlineOnly
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.FirSuperReference
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.resolve.transformers.publishedApiEffectiveVisibility
import org.jetbrains.kotlin.fir.scopes.getDirectOverriddenMembers
@@ -34,7 +34,6 @@ import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor
import org.jetbrains.kotlin.fir.visitors.FirVisitor
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
@@ -47,41 +46,14 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
if (context.session.inlineCheckerExtension?.isGenerallyOk(declaration, context, reporter) == false) return
if (declaration !is FirPropertyAccessor && declaration !is FirSimpleFunction) return
val effectiveVisibility = declaration.publishedApiEffectiveVisibility ?: declaration.effectiveVisibility
checkInlineFunctionBody(declaration, effectiveVisibility, context, reporter)
checkCallableDeclaration(declaration, context, reporter)
}
private fun checkInlineFunctionBody(
function: FirFunction,
effectiveVisibility: EffectiveVisibility,
context: CheckerContext,
reporter: DiagnosticReporter
) {
val body = function.body ?: return
val inalienableParameters = function.valueParameters.filter {
if (it.isNoinline) return@filter false
val type = it.returnTypeRef.coneType
!type.isMarkedNullable && type.isNonReflectFunctionType(context.session)
}.map { it.symbol }
val createVisitor = context.session.inlineCheckerExtension?.inlineVisitor ?: ::BasicInlineVisitor
val visitor = createVisitor(
function,
effectiveVisibility,
inalienableParameters,
context.session,
reporter
)
body.checkChildrenWithCustomVisitor(context, visitor, function)
}
open class BasicInlineVisitor(
class InlineFunctionBodyContext(
val inlineFunction: FirFunction,
private val inlineFunEffectiveVisibility: EffectiveVisibility,
private val inalienableParameters: List<FirValueParameterSymbol>,
val session: FirSession,
val reporter: DiagnosticReporter
) : FirDefaultVisitor<Unit, CheckerContext>() {
private val isEffectivelyPrivateApiFunction: Boolean = inlineFunEffectiveVisibility.privateApi
@@ -90,43 +62,15 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
override fun visitElement(element: FirElement, data: CheckerContext) {}
override fun visitFunctionCall(functionCall: FirFunctionCall, data: CheckerContext) {
val targetSymbol = functionCall.toResolvedCallableSymbol()
if (targetSymbol != null) {
checkReceiversOfQualifiedAccessExpression(functionCall, targetSymbol, data)
checkArgumentsOfCall(functionCall, targetSymbol, data)
checkQualifiedAccess(functionCall, targetSymbol, data)
}
}
override fun visitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression, data: CheckerContext) {
val targetSymbol = qualifiedAccessExpression.toResolvedCallableSymbol()
checkQualifiedAccess(qualifiedAccessExpression, targetSymbol, data)
checkReceiversOfQualifiedAccessExpression(qualifiedAccessExpression, targetSymbol, data)
}
// prevent delegation to visitQualifiedAccessExpression, which causes redundant diagnostics
override fun visitSmartCastExpression(smartCastExpression: FirSmartCastExpression, data: CheckerContext) {}
override fun visitVariableAssignment(variableAssignment: FirVariableAssignment, data: CheckerContext) {
val propertySymbol = variableAssignment.calleeReference?.toResolvedCallableSymbol() as? FirPropertySymbol ?: return
val setterSymbol = propertySymbol.setterSymbol ?: return
checkQualifiedAccess(variableAssignment, setterSymbol, data)
}
override fun visitResolvedQualifier(resolvedQualifier: FirResolvedQualifier, data: CheckerContext) {
val accessedClass = resolvedQualifier.symbol ?: return
val source = resolvedQualifier.source ?: return
if (accessedClass.isCompanion) {
checkAccessedDeclaration(source, accessedClass, accessedClass.visibility, data)
}
}
private fun checkAccessedDeclaration(
internal fun checkAccessedDeclaration(
source: KtSourceElement,
accessedSymbol: FirBasedSymbol<*>,
declarationVisibility: Visibility,
context: CheckerContext
context: CheckerContext,
reporter: DiagnosticReporter,
): AccessedDeclarationVisibilityData {
val recordedEffectiveVisibility = when (accessedSymbol) {
is FirCallableSymbol<*> -> accessedSymbol.publishedApiEffectiveVisibility ?: accessedSymbol.effectiveVisibility
@@ -155,7 +99,7 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
context
)
} else {
checkPrivateClassMemberAccess(accessedSymbol, source, context)
checkPrivateClassMemberAccess(accessedSymbol, source, context, reporter)
}
return AccessedDeclarationVisibilityData(
isInlineFunPublicOrPublishedApi,
@@ -164,25 +108,27 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
)
}
private data class AccessedDeclarationVisibilityData(
internal data class AccessedDeclarationVisibilityData(
val isInlineFunPublicOrPublishedApi: Boolean,
val isCalledFunPublicOrPublishedApi: Boolean,
val calledFunEffectiveVisibility: EffectiveVisibility
)
private fun checkReceiversOfQualifiedAccessExpression(
internal fun checkReceiversOfQualifiedAccessExpression(
qualifiedAccessExpression: FirQualifiedAccessExpression,
targetSymbol: FirBasedSymbol<*>?,
context: CheckerContext
context: CheckerContext,
reporter: DiagnosticReporter,
) {
checkReceiver(qualifiedAccessExpression, qualifiedAccessExpression.dispatchReceiver, targetSymbol, context)
checkReceiver(qualifiedAccessExpression, qualifiedAccessExpression.extensionReceiver, targetSymbol, context)
checkReceiver(qualifiedAccessExpression, qualifiedAccessExpression.dispatchReceiver, targetSymbol, context, reporter)
checkReceiver(qualifiedAccessExpression, qualifiedAccessExpression.extensionReceiver, targetSymbol, context, reporter)
}
private fun checkArgumentsOfCall(
internal fun checkArgumentsOfCall(
functionCall: FirFunctionCall,
targetSymbol: FirBasedSymbol<*>?,
context: CheckerContext
context: CheckerContext,
reporter: DiagnosticReporter,
) {
if (context.isContractBody) return
val calledFunctionSymbol = targetSymbol as? FirNamedFunctionSymbol ?: return
@@ -195,7 +141,9 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
if (valueParameterOfOriginalInlineFunction != null) {
val factory = when {
calledFunctionSymbol.isInline -> when {
valueParameter.isNoinline -> FirErrors.USAGE_IS_NOT_INLINABLE
valueParameter.isNoinline -> {
FirErrors.USAGE_IS_NOT_INLINABLE
}
valueParameter.isCrossinline && !valueParameterOfOriginalInlineFunction.isCrossinline
-> FirErrors.NON_LOCAL_RETURN_NOT_ALLOWED
else -> continue
@@ -211,7 +159,8 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
qualifiedAccessExpression: FirQualifiedAccessExpression,
receiverExpression: FirExpression,
targetSymbol: FirBasedSymbol<*>?,
context: CheckerContext
context: CheckerContext,
reporter: DiagnosticReporter,
) {
val receiverSymbol = receiverExpression.toResolvedCallableSymbol() ?: return
if (receiverSymbol in inalienableParameters) {
@@ -234,10 +183,11 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
targetSymbol.dispatchReceiverType?.isSomeFunctionType(session) == true
}
private fun checkQualifiedAccess(
internal fun checkQualifiedAccess(
qualifiedAccess: FirStatement,
targetSymbol: FirBasedSymbol<*>?,
context: CheckerContext
context: CheckerContext,
reporter: DiagnosticReporter,
) {
val source = qualifiedAccess.source ?: return
if (targetSymbol !is FirCallableSymbol<*>) return
@@ -247,8 +197,8 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
reporter.reportOn(source, FirErrors.USAGE_IS_NOT_INLINABLE, targetSymbol, context)
}
}
checkVisibilityAndAccess(qualifiedAccess, targetSymbol, source, context)
checkRecursion(targetSymbol, source, context)
checkVisibilityAndAccess(qualifiedAccess, targetSymbol, source, context, reporter)
checkRecursion(targetSymbol, source, context, reporter)
}
private fun FirStatement.partOfCall(context: CheckerContext): Boolean {
@@ -265,7 +215,8 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
accessExpression: FirStatement,
calledDeclaration: FirCallableSymbol<*>?,
source: KtSourceElement,
context: CheckerContext
context: CheckerContext,
reporter: DiagnosticReporter,
) {
if (
calledDeclaration == null ||
@@ -277,11 +228,12 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
source,
calledDeclaration,
calledDeclaration.visibility,
context
context,
reporter,
)
if (isInlineFunPublicOrPublishedApi && isCalledFunPublicOrPublishedApi) {
checkSuperCalls(calledDeclaration, accessExpression, context)
checkSuperCalls(calledDeclaration, accessExpression, context, reporter)
}
val isConstructorCall = calledDeclaration is FirConstructorSymbol
@@ -302,7 +254,8 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
private fun checkPrivateClassMemberAccess(
calledDeclaration: FirBasedSymbol<*>,
source: KtSourceElement,
context: CheckerContext
context: CheckerContext,
reporter: DiagnosticReporter,
) {
if (!isEffectivelyPrivateApiFunction) {
if (calledDeclaration.isInsidePrivateClass()) {
@@ -320,7 +273,8 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
private fun checkSuperCalls(
calledDeclaration: FirCallableSymbol<*>,
callExpression: FirStatement,
context: CheckerContext
context: CheckerContext,
reporter: DiagnosticReporter,
) {
val receiver = when (callExpression) {
is FirQualifiedAccessExpression -> callExpression.dispatchReceiver
@@ -353,7 +307,8 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
private fun checkRecursion(
targetSymbol: FirBasedSymbol<*>,
source: KtSourceElement,
context: CheckerContext
context: CheckerContext,
reporter: DiagnosticReporter,
) {
if (targetSymbol == inlineFunction.symbol) {
reporter.reportOn(source, FirErrors.RECURSION_IN_INLINE, targetSymbol, context)
@@ -511,24 +466,19 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
reporter.reportOn(declaration.source, FirErrors.OVERRIDE_BY_INLINE, context)
}
}
private fun FirElement.checkChildrenWithCustomVisitor(
parentContext: CheckerContext,
visitorVoid: FirVisitor<Unit, CheckerContext>,
rootFunction: FirFunction,
) {
// TODO: Get rid of this cast and the following context modification as it looks like a leaking abstraction (see KT-56460)
require(parentContext is CheckerContextForProvider) {
"This checked violates the contract for read-only checkers"
}
parentContext.withDeclaration(rootFunction) {
val collectingVisitor = object : AbstractDiagnosticCollectorVisitor(it) {
override fun checkElement(element: FirElement) {
element.accept(visitorVoid, context)
}
}
this.accept(collectingVisitor, null)
}
}
}
fun createInlineFunctionBodyContext(function: FirFunction, session: FirSession): FirInlineDeclarationChecker.InlineFunctionBodyContext {
val inlineableParameters = function.valueParameters.filter {
if (it.isNoinline) return@filter false
val type = it.returnTypeRef.coneType
!type.isMarkedNullable && type.isNonReflectFunctionType(session)
}.map { it.symbol }
return FirInlineDeclarationChecker.InlineFunctionBodyContext(
function,
function.publishedApiEffectiveVisibility ?: function.effectiveVisibility,
inlineableParameters,
session,
)
}
@@ -9,8 +9,11 @@ import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContextForProvider
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.createInlineFunctionBodyContext
import org.jetbrains.kotlin.fir.contracts.FirContractDescription
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.buildReceiverParameter
import org.jetbrains.kotlin.fir.declarations.utils.isInline
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirContractCallBlock
import org.jetbrains.kotlin.fir.resolve.defaultType
@@ -105,7 +108,9 @@ abstract class AbstractDiagnosticCollectorVisitor(
override fun visitSimpleFunction(simpleFunction: FirSimpleFunction, data: Nothing?) {
withAnnotationContainer(simpleFunction) {
visitWithDeclarationAndReceiver(simpleFunction, simpleFunction.name, simpleFunction.receiverParameter)
withInlineFunctionBodyIfApplicable(simpleFunction, simpleFunction.isInline) {
visitWithDeclarationAndReceiver(simpleFunction, simpleFunction.name, simpleFunction.receiverParameter)
}
}
}
@@ -145,7 +150,9 @@ abstract class AbstractDiagnosticCollectorVisitor(
override fun visitPropertyAccessor(propertyAccessor: FirPropertyAccessor, data: Nothing?) {
val property = context.containingDeclarations.last() as FirProperty
withAnnotationContainer(propertyAccessor) {
visitWithDeclarationAndReceiver(propertyAccessor, property.name, property.receiverParameter)
withInlineFunctionBodyIfApplicable(propertyAccessor, propertyAccessor.isInline || property.isInline) {
visitWithDeclarationAndReceiver(propertyAccessor, property.name, property.receiverParameter)
}
}
}
@@ -189,6 +196,12 @@ abstract class AbstractDiagnosticCollectorVisitor(
}
}
override fun visitContractDescription(contractDescription: FirContractDescription, data: Nothing?) {
suppressInlineFunctionBodyContext {
visitElement(contractDescription, data)
}
}
override fun visitTypeRef(typeRef: FirTypeRef, data: Nothing?) {
if (typeRef.source?.kind?.shouldSkipErrorTypeReporting == false) {
withTypeRefAnnotationContainer(typeRef) {
@@ -286,6 +299,20 @@ abstract class AbstractDiagnosticCollectorVisitor(
}
}
@OptIn(PrivateForInline::class)
private inline fun <T> withInlineFunctionBodyIfApplicable(function: FirFunction, isInline: Boolean, block: () -> T): T {
return try {
if (isInline) {
context = context.setInlineFunctionBodyContext(createInlineFunctionBodyContext(function, context.session))
}
block()
} finally {
if (isInline) {
context = context.unsetInlineFunctionBodyContext()
}
}
}
private fun visitWithCallOrAssignment(callOrAssignment: FirStatement) {
return withCallOrAssignment(callOrAssignment) {
visitElement(callOrAssignment, null)
@@ -410,6 +437,20 @@ abstract class AbstractDiagnosticCollectorVisitor(
}
}
@OptIn(PrivateForInline::class)
private inline fun <R> suppressInlineFunctionBodyContext(block: () -> R): R {
val oldInlineFunctionBodyContext = context.inlineFunctionBodyContext?.also {
context = context.unsetInlineFunctionBodyContext()
}
return try {
block()
} finally {
oldInlineFunctionBodyContext?.let {
context = context.setInlineFunctionBodyContext(it)
}
}
}
@OptIn(PrivateForInline::class)
private inline fun <R> insideContractBody(block: () -> R): R {
context = context.enterContractBody()
@@ -6,17 +6,17 @@ inline fun inlineFun(lambda: () -> String) = lambda()
fun noInlineFun(lambda: () -> String) = lambda()
inline fun default0_1(lambda: () -> String, dlambda: () -> String = { lambda; "OK" }) {
inline fun default0_1(lambda: () -> String, dlambda: () -> String = { <!USAGE_IS_NOT_INLINABLE!>lambda<!>; "OK" }) {
lambda() + dlambda()
}
inline fun default0_2(lambda: () -> String, dlambda: () -> String = { noInlineFun (lambda) }) {
inline fun default0_2(lambda: () -> String, dlambda: () -> String = { noInlineFun (<!USAGE_IS_NOT_INLINABLE!>lambda<!>) }) {
lambda() + dlambda()
}
inline fun default0(lambda: () -> String, dlambda: () -> String = { noInlineFun (lambda) }) {
inline fun default0(lambda: () -> String, dlambda: () -> String = { noInlineFun (<!USAGE_IS_NOT_INLINABLE!>lambda<!>) }) {
lambda() + dlambda()
}