diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/FirJvmInlineCheckerComponent.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/FirJvmInlineCheckerComponent.kt index a02d6f18e28..9707f8627bf 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/FirJvmInlineCheckerComponent.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/FirJvmInlineCheckerComponent.kt @@ -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, - 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) - } - } -} diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmDeclarationCheckers.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmDeclarationCheckers.kt index 824168aab6b..2477f228c32 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmDeclarationCheckers.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmDeclarationCheckers.kt @@ -31,6 +31,7 @@ object JvmDeclarationCheckers : DeclarationCheckers() { FirJvmRecordChecker, FirJvmInlineApplicabilityChecker, FirJvmConflictsChecker, + FirInlineBodyRegularClassChecker, ) override val propertyCheckers: Set @@ -49,4 +50,9 @@ object JvmDeclarationCheckers : DeclarationCheckers() { get() = setOf( FirUpperBoundsChecker, ) + + override val simpleFunctionCheckers: Set + get() = setOf( + FirInlineBodySimpleFunctionChecker, + ) } diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirInlineBodyRegularClassChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirInlineBodyRegularClassChecker.kt new file mode 100644 index 00000000000..4c62ab35fdc --- /dev/null +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirInlineBodyRegularClassChecker.kt @@ -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) + } + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirInlineBodySimpleFunctionChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirInlineBodySimpleFunctionChecker.kt new file mode 100644 index 00000000000..7873dbb8163 --- /dev/null +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirInlineBodySimpleFunctionChecker.kt @@ -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) + } + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt index 311d645e35b..66101ddb804 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt @@ -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 @@ -82,6 +86,7 @@ object CommonExpressionCheckers : ExpressionCheckers() { get() = setOf( FirReassignmentAndInvisibleSetterChecker, FirAssignmentTypeMismatchChecker, + FirInlineBodyVariableAssignmentChecker, ) override val whenExpressionCheckers: Set @@ -152,6 +157,7 @@ object CommonExpressionCheckers : ExpressionCheckers() { FirOptInUsageQualifierChecker, FirDeprecatedQualifierChecker, FirVisibilityQualifierChecker, + FirInlineBodyResolvedQualifierChecker, ) override val equalityOperatorCallCheckers: Set diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirInlineCheckerPlatformSpecificComponent.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirInlineCheckerPlatformSpecificComponent.kt index a3437dfc681..8d3c6fee48e 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirInlineCheckerPlatformSpecificComponent.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirInlineCheckerPlatformSpecificComponent.kt @@ -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, diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContext.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContext.kt index ba6035c92f2..bbd611e7aa7 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContext.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContext.kt @@ -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 abstract val containingElements: List abstract val isContractBody: Boolean + abstract val inlineFunctionBodyContext: FirInlineDeclarationChecker.InlineFunctionBodyContext? // Suppress abstract val suppressedDiagnostics: Set diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContextForProvider.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContextForProvider.kt index 292c42b1372..1aa6231b042 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContextForProvider.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContextForProvider.kt @@ -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 diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/MutableCheckerContext.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/MutableCheckerContext.kt index b9bdd0a3b91..e3c3f33abfa 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/MutableCheckerContext.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/MutableCheckerContext.kt @@ -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, override val containingElements: MutableList, 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 diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/PersistentCheckerContext.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/PersistentCheckerContext.kt index 5fb2a03c625..ac1582e516f 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/PersistentCheckerContext.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/PersistentCheckerContext.kt @@ -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, override val containingElements: PersistentList, override val isContractBody: Boolean, + override val inlineFunctionBodyContext: FirInlineDeclarationChecker.InlineFunctionBodyContext?, sessionHolder: SessionHolder, returnTypeCalculator: ReturnTypeCalculator, override val suppressedDiagnostics: PersistentSet, @@ -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 = this.containingElements, containingDeclarations: PersistentList = 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) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineBodyQualifiedAccessExpressionChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineBodyQualifiedAccessExpressionChecker.kt new file mode 100644 index 00000000000..7ba3d60ddcf --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineBodyQualifiedAccessExpressionChecker.kt @@ -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) + } + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineBodyResolvedQualifierChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineBodyResolvedQualifierChecker.kt new file mode 100644 index 00000000000..2481ac991a8 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineBodyResolvedQualifierChecker.kt @@ -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) + } + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineBodyVariableAssignmentChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineBodyVariableAssignmentChecker.kt new file mode 100644 index 00000000000..e38bbe10e11 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineBodyVariableAssignmentChecker.kt @@ -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) + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineDeclarationChecker.kt index 0796d4ee076..2b0c1ec459b 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineDeclarationChecker.kt @@ -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, val session: FirSession, - val reporter: DiagnosticReporter ) : FirDefaultVisitor() { 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, - 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, + ) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollectorVisitor.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollectorVisitor.kt index 8435d1b45c4..6f19b3e4e09 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollectorVisitor.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollectorVisitor.kt @@ -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 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 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 insideContractBody(block: () -> R): R { context = context.enterContractBody() diff --git a/compiler/testData/diagnostics/tests/inline/inlineLambdaInDefaultInlineParameter.fir.kt b/compiler/testData/diagnostics/tests/inline/inlineLambdaInDefaultInlineParameter.fir.kt index 3ad7a5dce69..8dbb09415e4 100644 --- a/compiler/testData/diagnostics/tests/inline/inlineLambdaInDefaultInlineParameter.fir.kt +++ b/compiler/testData/diagnostics/tests/inline/inlineLambdaInDefaultInlineParameter.fir.kt @@ -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 = { lambda; "OK" }) { lambda() + dlambda() } -inline fun default0_2(lambda: () -> String, dlambda: () -> String = { noInlineFun (lambda) }) { +inline fun default0_2(lambda: () -> String, dlambda: () -> String = { noInlineFun (lambda) }) { lambda() + dlambda() } -inline fun default0(lambda: () -> String, dlambda: () -> String = { noInlineFun (lambda) }) { +inline fun default0(lambda: () -> String, dlambda: () -> String = { noInlineFun (lambda) }) { lambda() + dlambda() }