[FIR] Add inline checker for bodies of inline functions
This checker doesn't support `@PublishedAPI` yet, so some BB tests for it were muted. #KT-46270
This commit is contained in:
committed by
TeamCityServer
parent
7e052c1eb1
commit
32c3f85679
+43
@@ -731,6 +731,49 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
|
||||
val RETURN_NOT_ALLOWED by error<KtReturnExpression>(PositioningStrategy.RETURN_WITH_LABEL)
|
||||
val RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY by error<KtReturnExpression>(PositioningStrategy.RETURN_WITH_LABEL)
|
||||
}
|
||||
|
||||
val INLINE by object : DiagnosticGroup("Inline") {
|
||||
val USAGE_IS_NOT_INLINABLE by error<KtElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
|
||||
parameter<Symbol>("parameter")
|
||||
}
|
||||
|
||||
val NON_LOCAL_RETURN_NOT_ALLOWED by error<KtElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
|
||||
parameter<Symbol>("parameter")
|
||||
}
|
||||
|
||||
val RECURSION_IN_INLINE by error<KtElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
|
||||
parameter<Symbol>("symbol")
|
||||
}
|
||||
|
||||
val NON_PUBLIC_CALL_FROM_PUBLIC_INLINE by error<KtElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
|
||||
parameter<Symbol>("inlineDeclaration")
|
||||
parameter<Symbol>("referencedDeclaration")
|
||||
}
|
||||
|
||||
val PROTECTED_CONSTRUCTOR_CALL_FROM_PUBLIC_INLINE by error<KtElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
|
||||
parameter<Symbol>("inlineDeclaration")
|
||||
parameter<Symbol>("referencedDeclaration")
|
||||
}
|
||||
|
||||
val PROTECTED_CALL_FROM_PUBLIC_INLINE_ERROR by error<KtElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
|
||||
parameter<Symbol>("inlineDeclaration")
|
||||
parameter<Symbol>("referencedDeclaration")
|
||||
}
|
||||
|
||||
val PROTECTED_CALL_FROM_PUBLIC_INLINE by warning<KtElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
|
||||
parameter<Symbol>("inlineDeclaration")
|
||||
parameter<Symbol>("referencedDeclaration")
|
||||
}
|
||||
|
||||
val PRIVATE_CLASS_MEMBER_FROM_INLINE by error<KtElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
|
||||
parameter<Symbol>("inlineDeclaration")
|
||||
parameter<Symbol>("referencedDeclaration")
|
||||
}
|
||||
|
||||
val SUPER_CALL_FROM_PUBLIC_INLINE by warning<KtElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
|
||||
parameter<Symbol>("symbol")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val exposedVisibilityDiagnosticInit: DiagnosticBuilder.() -> Unit = {
|
||||
|
||||
@@ -440,4 +440,15 @@ object FirErrors {
|
||||
val RETURN_NOT_ALLOWED by error0<KtReturnExpression>(SourceElementPositioningStrategies.RETURN_WITH_LABEL)
|
||||
val RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY by error0<KtReturnExpression>(SourceElementPositioningStrategies.RETURN_WITH_LABEL)
|
||||
|
||||
// Inline
|
||||
val USAGE_IS_NOT_INLINABLE by error1<KtElement, AbstractFirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val NON_LOCAL_RETURN_NOT_ALLOWED by error1<KtElement, AbstractFirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val RECURSION_IN_INLINE by error1<KtElement, AbstractFirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val NON_PUBLIC_CALL_FROM_PUBLIC_INLINE by error2<KtElement, AbstractFirBasedSymbol<*>, AbstractFirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val PROTECTED_CONSTRUCTOR_CALL_FROM_PUBLIC_INLINE by error2<KtElement, AbstractFirBasedSymbol<*>, AbstractFirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val PROTECTED_CALL_FROM_PUBLIC_INLINE_ERROR by error2<KtElement, AbstractFirBasedSymbol<*>, AbstractFirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val PROTECTED_CALL_FROM_PUBLIC_INLINE by warning2<KtElement, AbstractFirBasedSymbol<*>, AbstractFirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val PRIVATE_CLASS_MEMBER_FROM_INLINE by error2<KtElement, AbstractFirBasedSymbol<*>, AbstractFirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val SUPER_CALL_FROM_PUBLIC_INLINE by warning1<KtElement, AbstractFirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -186,7 +186,8 @@ internal val FirDeclaration.isEnumEntryInitializer: Boolean
|
||||
return (containingClassAttr as? ConeClassLookupTagWithFixedSymbol)?.symbol?.fir?.classKind == ClassKind.ENUM_ENTRY
|
||||
}
|
||||
|
||||
internal val FirMemberDeclaration.isLocalMember: Boolean
|
||||
// contract: returns(true) implies (this is FirMemberDeclaration)
|
||||
internal val FirDeclaration.isLocalMember: Boolean
|
||||
get() = when (this) {
|
||||
is FirProperty -> this.isLocal
|
||||
is FirRegularClass -> this.isLocal
|
||||
|
||||
+310
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.util.checkChildrenWithCustomVisitor
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.references.FirSuperReference
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.isFunctionalType
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.fir.types.isMarkedNullable
|
||||
import org.jetbrains.kotlin.fir.types.toSymbol
|
||||
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
object FirInlineDeclarationChecker : FirMemberDeclarationChecker() {
|
||||
override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (!declaration.isInline) return
|
||||
// local inline functions are prohibited
|
||||
if (declaration.isLocalMember) return
|
||||
if (declaration !is FirPropertyAccessor && declaration !is FirSimpleFunction) return
|
||||
|
||||
val effectiveVisibility = declaration.effectiveVisibility
|
||||
val function = declaration as FirFunction<*>
|
||||
checkInlineFunctionBody(function, effectiveVisibility, context, reporter)
|
||||
}
|
||||
|
||||
private fun checkInlineFunctionBody(
|
||||
function: FirFunction<*>,
|
||||
effectiveVisibility: EffectiveVisibility,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
val body = function.body ?: return
|
||||
val inlinableParameters = function.valueParameters.filter {
|
||||
if (it.isNoinline) return@filter false
|
||||
val type = it.returnTypeRef.coneType
|
||||
!type.isMarkedNullable && type.isFunctionalType(context.session) { kind -> !kind.isReflectType }
|
||||
}
|
||||
|
||||
val visitor = Visitor(
|
||||
function,
|
||||
effectiveVisibility,
|
||||
inlinableParameters,
|
||||
context.session,
|
||||
reporter
|
||||
)
|
||||
body.checkChildrenWithCustomVisitor(context, visitor)
|
||||
}
|
||||
|
||||
private class Visitor(
|
||||
val inlineFunction: FirFunction<*>,
|
||||
val inlineFunEffectiveVisibility: EffectiveVisibility,
|
||||
val inlinableParameters: List<FirValueParameter>,
|
||||
val session: FirSession,
|
||||
val reporter: DiagnosticReporter
|
||||
) : FirDefaultVisitor<Unit, CheckerContext>() {
|
||||
private val isEffectivelyPrivateApiFunction: Boolean = inlineFunEffectiveVisibility.privateApi
|
||||
|
||||
private val prohibitProtectedCallFromInline: Boolean =
|
||||
session.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitProtectedCallFromInline)
|
||||
|
||||
override fun visitElement(element: FirElement, data: CheckerContext) {}
|
||||
|
||||
override fun visitFunctionCall(functionCall: FirFunctionCall, data: CheckerContext) {
|
||||
val targetSymbol = functionCall.toResolvedCallableSymbol()
|
||||
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)
|
||||
}
|
||||
|
||||
override fun visitVariableAssignment(variableAssignment: FirVariableAssignment, data: CheckerContext) {
|
||||
val propertySymbol = variableAssignment.calleeReference.toResolvedCallableSymbol() as? FirPropertySymbol ?: return
|
||||
val setterSymbol = propertySymbol.fir.setter?.symbol ?: return
|
||||
checkQualifiedAccess(variableAssignment, setterSymbol, data)
|
||||
}
|
||||
|
||||
private fun checkReceiversOfQualifiedAccessExpression(
|
||||
qualifiedAccessExpression: FirQualifiedAccessExpression,
|
||||
targetSymbol: AbstractFirBasedSymbol<*>?,
|
||||
context: CheckerContext
|
||||
) {
|
||||
checkReceiver(qualifiedAccessExpression, qualifiedAccessExpression.dispatchReceiver, targetSymbol, context)
|
||||
checkReceiver(qualifiedAccessExpression, qualifiedAccessExpression.extensionReceiver, targetSymbol, context)
|
||||
}
|
||||
|
||||
private fun checkArgumentsOfCall(
|
||||
functionCall: FirFunctionCall,
|
||||
targetSymbol: AbstractFirBasedSymbol<*>?,
|
||||
context: CheckerContext
|
||||
) {
|
||||
val calledFunction = (targetSymbol as? FirNamedFunctionSymbol)?.fir ?: return
|
||||
val argumentMapping = functionCall.resolvedArgumentMapping ?: return
|
||||
for ((wrappedArgument, valueParameter) in argumentMapping) {
|
||||
val argument = wrappedArgument.unwrapArgument()
|
||||
val resolvedArgumentSymbol = argument.toResolvedCallableSymbol() as? FirVariableSymbol<*> ?: continue
|
||||
|
||||
val valueParameterOfOriginalInlineFunction = inlinableParameters.firstOrNull { it == resolvedArgumentSymbol.fir }
|
||||
if (valueParameterOfOriginalInlineFunction != null) {
|
||||
val factory = when {
|
||||
calledFunction.isInline -> when {
|
||||
valueParameter.isNoinline -> FirErrors.USAGE_IS_NOT_INLINABLE
|
||||
valueParameter.isCrossinline && !valueParameterOfOriginalInlineFunction.isCrossinline
|
||||
-> FirErrors.NON_LOCAL_RETURN_NOT_ALLOWED
|
||||
else -> continue
|
||||
}
|
||||
else -> FirErrors.USAGE_IS_NOT_INLINABLE
|
||||
}
|
||||
reporter.reportOn(argument.source, factory, valueParameterOfOriginalInlineFunction.symbol, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkReceiver(
|
||||
qualifiedAccessExpression: FirQualifiedAccessExpression,
|
||||
receiverExpression: FirExpression,
|
||||
targetSymbol: AbstractFirBasedSymbol<*>?,
|
||||
context: CheckerContext
|
||||
) {
|
||||
val receiverSymbol = receiverExpression.toResolvedCallableSymbol() ?: return
|
||||
if (receiverSymbol.fir in inlinableParameters) {
|
||||
val valueParameter = receiverSymbol.fir as FirValueParameter
|
||||
if (!isInvokeOrInlineExtension(targetSymbol)) {
|
||||
reporter.reportOn(
|
||||
qualifiedAccessExpression.source,
|
||||
FirErrors.USAGE_IS_NOT_INLINABLE,
|
||||
valueParameter.symbol,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isInvokeOrInlineExtension(targetSymbol: AbstractFirBasedSymbol<*>?): Boolean {
|
||||
if (targetSymbol !is FirNamedFunctionSymbol) return false
|
||||
val function = targetSymbol.fir
|
||||
if (function.isInline) return true
|
||||
return function.name == OperatorNameConventions.INVOKE &&
|
||||
function.dispatchReceiverType?.isBuiltinFunctionalType(session) == true
|
||||
}
|
||||
|
||||
private fun checkQualifiedAccess(
|
||||
qualifiedAccess: FirQualifiedAccess,
|
||||
targetSymbol: AbstractFirBasedSymbol<*>?,
|
||||
context: CheckerContext
|
||||
) {
|
||||
val source = qualifiedAccess.source ?: return
|
||||
if (targetSymbol == null) return
|
||||
val targetFir = targetSymbol.fir as? FirCallableMemberDeclaration<*>
|
||||
|
||||
if (targetSymbol.fir in inlinableParameters) {
|
||||
if (!qualifiedAccess.partOfCall(context)) {
|
||||
val valueParameter = targetSymbol.fir as FirValueParameter
|
||||
reporter.reportOn(source, FirErrors.USAGE_IS_NOT_INLINABLE, valueParameter.symbol, context)
|
||||
}
|
||||
}
|
||||
checkVisibilityAndAccess(qualifiedAccess, targetFir, source, context)
|
||||
checkRecursion(targetSymbol, source, context)
|
||||
}
|
||||
|
||||
private fun FirQualifiedAccess.partOfCall(context: CheckerContext): Boolean {
|
||||
if (this !is FirExpression) return false
|
||||
val containingQualifiedAccess = context.qualifiedAccesses.getOrNull(context.qualifiedAccesses.size - 2) ?: return false
|
||||
if (this == containingQualifiedAccess.explicitReceiver) return true
|
||||
val call = containingQualifiedAccess as? FirCall ?: return false
|
||||
return call.arguments.any { it.unwrapArgument() == this }
|
||||
}
|
||||
|
||||
private fun checkVisibilityAndAccess(
|
||||
accessExpression: FirQualifiedAccess,
|
||||
calledDeclaration: FirCallableMemberDeclaration<*>?,
|
||||
source: FirSourceElement,
|
||||
context: CheckerContext
|
||||
) {
|
||||
if (calledDeclaration == null) return
|
||||
val calledFunEffectiveVisibility = calledDeclaration.effectiveVisibility.let {
|
||||
if (it == EffectiveVisibility.Local) {
|
||||
EffectiveVisibility.Public
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
val isCalledFunPublicOrPublishedApi = calledFunEffectiveVisibility.publicApi
|
||||
val isInlineFunPublicOrPublishedApi = inlineFunEffectiveVisibility.publicApi
|
||||
if (isInlineFunPublicOrPublishedApi &&
|
||||
!isCalledFunPublicOrPublishedApi &&
|
||||
calledDeclaration.visibility !== Visibilities.Local
|
||||
) {
|
||||
reporter.reportOn(
|
||||
source,
|
||||
FirErrors.NON_PUBLIC_CALL_FROM_PUBLIC_INLINE,
|
||||
calledDeclaration.symbol,
|
||||
inlineFunction.symbol,
|
||||
context
|
||||
)
|
||||
} else {
|
||||
checkPrivateClassMemberAccess(calledDeclaration, source, context)
|
||||
if (isInlineFunPublicOrPublishedApi) {
|
||||
checkSuperCalls(calledDeclaration, accessExpression, context)
|
||||
}
|
||||
}
|
||||
|
||||
val isConstructorCall = calledDeclaration is FirConstructor
|
||||
if (
|
||||
isInlineFunPublicOrPublishedApi &&
|
||||
inlineFunEffectiveVisibility.toVisibility() !== Visibilities.Protected &&
|
||||
calledFunEffectiveVisibility.toVisibility() === Visibilities.Protected
|
||||
) {
|
||||
val factory = when {
|
||||
isConstructorCall -> FirErrors.PROTECTED_CONSTRUCTOR_CALL_FROM_PUBLIC_INLINE
|
||||
prohibitProtectedCallFromInline -> FirErrors.PROTECTED_CALL_FROM_PUBLIC_INLINE_ERROR
|
||||
else -> FirErrors.PROTECTED_CALL_FROM_PUBLIC_INLINE
|
||||
}
|
||||
reporter.reportOn(source, factory, calledDeclaration.symbol, inlineFunction.symbol, context)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkPrivateClassMemberAccess(
|
||||
calledDeclaration: FirCallableMemberDeclaration<*>,
|
||||
source: FirSourceElement,
|
||||
context: CheckerContext
|
||||
) {
|
||||
if (!isEffectivelyPrivateApiFunction) {
|
||||
if (calledDeclaration.isInsidePrivateClass()) {
|
||||
reporter.reportOn(
|
||||
source,
|
||||
FirErrors.PRIVATE_CLASS_MEMBER_FROM_INLINE,
|
||||
calledDeclaration.symbol,
|
||||
inlineFunction.symbol,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkSuperCalls(
|
||||
calledDeclaration: FirCallableMemberDeclaration<*>,
|
||||
callExpression: FirQualifiedAccess,
|
||||
context: CheckerContext
|
||||
) {
|
||||
val receiver = callExpression.dispatchReceiver as? FirQualifiedAccessExpression ?: return
|
||||
if (receiver.calleeReference is FirSuperReference) {
|
||||
val dispatchReceiverType = receiver.dispatchReceiver.typeRef.coneType
|
||||
val classSymbol = dispatchReceiverType.toSymbol(session) ?: return
|
||||
if (!classSymbol.isDefinedInInlineFunction()) {
|
||||
reporter.reportOn(
|
||||
callExpression.dispatchReceiver.source,
|
||||
FirErrors.SUPER_CALL_FROM_PUBLIC_INLINE,
|
||||
calledDeclaration.symbol,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun AbstractFirBasedSymbol<*>.isDefinedInInlineFunction(): Boolean {
|
||||
return when (val fir = this.fir) {
|
||||
is FirAnonymousFunction -> true
|
||||
is FirMemberDeclaration -> fir.isLocalMember
|
||||
is FirAnonymousObject -> true
|
||||
is FirRegularClass -> fir.classId.isLocal
|
||||
else -> error("Unknown callable declaration type: ${fir.render()}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkRecursion(
|
||||
targetSymbol: AbstractFirBasedSymbol<*>,
|
||||
source: FirSourceElement,
|
||||
context: CheckerContext
|
||||
) {
|
||||
if (targetSymbol == inlineFunction.symbol) {
|
||||
reporter.reportOn(source, FirErrors.RECURSION_IN_INLINE, targetSymbol, context)
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirCallableMemberDeclaration<*>.isInsidePrivateClass(): Boolean {
|
||||
val containingClass = this.containingClass()?.toSymbol(session)?.fir ?: return false
|
||||
|
||||
val containingClassVisibility = when (containingClass) {
|
||||
is FirAnonymousObject -> return false
|
||||
is FirRegularClass -> containingClass.visibility
|
||||
is FirTypeAlias -> containingClass.visibility
|
||||
}
|
||||
return containingClassVisibility == Visibilities.Private || containingClassVisibility == Visibilities.PrivateToThis
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.util
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.PersistentCheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor
|
||||
import org.jetbrains.kotlin.fir.visitors.FirVisitor
|
||||
|
||||
fun FirElement.checkChildrenWithCustomVisitor(
|
||||
parentContext: CheckerContext,
|
||||
visitorVoid: FirVisitor<Unit, CheckerContext>
|
||||
) {
|
||||
val collectingVisitor = object : AbstractDiagnosticCollectorVisitor(parentContext as PersistentCheckerContext) {
|
||||
override fun checkElement(element: FirElement) {
|
||||
element.accept(visitorVoid, context)
|
||||
}
|
||||
}
|
||||
this.accept(collectingVisitor, null)
|
||||
}
|
||||
+48
@@ -170,9 +170,11 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NONE_APPLICABLE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_ABSTRACT_FUNCTION_WITH_NO_BODY
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_FINAL_MEMBER_IN_FINAL_CLASS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_FINAL_MEMBER_IN_OBJECT
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_LOCAL_RETURN_NOT_ALLOWED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_MEMBER_FUNCTION_NO_BODY
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_PRIVATE_CONSTRUCTOR_IN_ENUM
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_PRIVATE_OR_PROTECTED_CONSTRUCTOR_IN_SEALED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_PUBLIC_CALL_FROM_PUBLIC_INLINE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_VARARG_SPREAD
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOTHING_TO_OVERRIDE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_AN_ANNOTATION_CLASS
|
||||
@@ -193,6 +195,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OVERLOAD_RESOLUTI
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OVERRIDING_FINAL_MEMBER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PRIVATE_CLASS_MEMBER_FROM_INLINE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PRIVATE_FUNCTION_WITH_NO_BODY
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PRIVATE_PROPERTY_IN_INTERFACE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PRIVATE_SETTER_FOR_ABSTRACT_PROPERTY
|
||||
@@ -203,8 +206,12 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_INITIALI
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_TYPE_MISMATCH_ON_OVERRIDE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_WITH_BACKING_FIELD_INSIDE_INLINE_CLASS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_WITH_NO_TYPE_NO_INITIALIZER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROTECTED_CALL_FROM_PUBLIC_INLINE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROTECTED_CALL_FROM_PUBLIC_INLINE_ERROR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROTECTED_CONSTRUCTOR_CALL_FROM_PUBLIC_INLINE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.QUALIFIED_SUPERTYPE_EXTENDED_BY_OTHER_SUPERTYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RECURSION_IN_IMPLICIT_TYPES
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RECURSION_IN_INLINE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RECURSION_IN_SUPERTYPES
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDECLARATION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_CALL_OF_CONVERSION_METHOD
|
||||
@@ -235,6 +242,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_INITIAL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_NOT_A_CLASS_OR_INTERFACE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_NOT_INITIALIZED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPER_CALL_FROM_PUBLIC_INLINE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPER_IS_NOT_AN_EXPRESSION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPER_NOT_AVAILABLE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SYNTAX
|
||||
@@ -269,6 +277,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSUPPORTED_FEATU
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNUSED_VARIABLE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UPPER_BOUND_VIOLATED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USAGE_IS_NOT_INLINABLE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_ELVIS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_ELVIS_RIGHT_IS_NULL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_VARARG_ON_PARAMETER
|
||||
@@ -905,6 +914,45 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
||||
"Returns are not allowed for functions with expression body. Use block body in '{...}'"
|
||||
)
|
||||
|
||||
// Inline
|
||||
map.put(
|
||||
USAGE_IS_NOT_INLINABLE,
|
||||
"Illegal usage of inline-parameter ''{0}''. Add ''noinline'' modifier to the parameter declaration",
|
||||
SYMBOL
|
||||
)
|
||||
map.put(
|
||||
NON_LOCAL_RETURN_NOT_ALLOWED,
|
||||
"Can''t inline ''{0}'' here: it may contain non-local returns. Add ''crossinline'' modifier to parameter declaration ''{0}''",
|
||||
SYMBOL
|
||||
)
|
||||
map.put(RECURSION_IN_INLINE, "Inline function ''{0}'' cannot be recursive", SYMBOL)
|
||||
map.put(NON_PUBLIC_CALL_FROM_PUBLIC_INLINE, "Public-API inline function cannot access non-public-API ''{1}''", SYMBOL, SYMBOL)
|
||||
map.put(
|
||||
PROTECTED_CONSTRUCTOR_CALL_FROM_PUBLIC_INLINE,
|
||||
"Protected constructor call from public-API inline function is deprecated",
|
||||
SYMBOL,
|
||||
SYMBOL
|
||||
)
|
||||
map.put(
|
||||
PROTECTED_CALL_FROM_PUBLIC_INLINE,
|
||||
"Protected function call from public-API inline function is deprecated",
|
||||
SYMBOL,
|
||||
SYMBOL
|
||||
)
|
||||
map.put(
|
||||
PROTECTED_CALL_FROM_PUBLIC_INLINE_ERROR,
|
||||
"Protected function call from public-API inline function is prohibited",
|
||||
SYMBOL,
|
||||
SYMBOL
|
||||
)
|
||||
map.put(
|
||||
PRIVATE_CLASS_MEMBER_FROM_INLINE,
|
||||
"Non-private inline function cannot access members of private classes: ''{1}''",
|
||||
SYMBOL,
|
||||
SYMBOL
|
||||
)
|
||||
map.put(SUPER_CALL_FROM_PUBLIC_INLINE, "Accessing super members from public-API inline function is deprecated", SYMBOL)
|
||||
|
||||
// Extended checkers group
|
||||
map.put(REDUNDANT_VISIBILITY_MODIFIER, "Redundant visibility modifier")
|
||||
map.put(REDUNDANT_MODALITY_MODIFIER, "Redundant modality modifier")
|
||||
|
||||
+1
@@ -31,6 +31,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
FirSealedSupertypeChecker,
|
||||
FirTypeAliasChecker,
|
||||
FirCyclicTypeBoundsChecker,
|
||||
FirInlineDeclarationChecker,
|
||||
)
|
||||
|
||||
override val functionCheckers: Set<FirFunctionChecker>
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.fir.expressions.impl.FirBlockImpl
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirPartiallyResolvedArgumentList
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedArgumentList
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock
|
||||
import org.jetbrains.kotlin.fir.references.FirReference
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
@@ -65,6 +66,10 @@ fun FirExpression.toResolvedCallableSymbol(): FirCallableSymbol<*>? {
|
||||
return toResolvedCallableReference()?.resolvedSymbol as FirCallableSymbol<*>?
|
||||
}
|
||||
|
||||
fun FirReference.toResolvedCallableSymbol(): FirCallableSymbol<*>? {
|
||||
return (this as? FirResolvedNamedReference)?.resolvedSymbol as? FirCallableSymbol<*>
|
||||
}
|
||||
|
||||
fun buildErrorLoop(source: FirSourceElement?, diagnostic: ConeDiagnostic): FirErrorLoop {
|
||||
return buildErrorLoop {
|
||||
this.source = source
|
||||
@@ -109,4 +114,4 @@ fun FirBlock.replaceFirstStatement(statement: FirStatement): FirStatement {
|
||||
return existed
|
||||
}
|
||||
|
||||
fun FirExpression.unwrapArgument(): FirExpression = (this as? FirWrappedArgumentExpression)?.expression ?: this
|
||||
fun FirExpression.unwrapArgument(): FirExpression = (this as? FirWrappedArgumentExpression)?.expression ?: this
|
||||
|
||||
Reference in New Issue
Block a user