[FIR] Add OVERRIDE_BY_INLINE diagnostic

This commit is contained in:
Andrey Zinovyev
2021-07-21 11:51:53 +03:00
committed by teamcityserver
parent d8b7b7b2dc
commit 29dbaa4ae1
13 changed files with 91 additions and 67 deletions
@@ -1175,6 +1175,8 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
}
val DECLARATION_CANT_BE_INLINED by error<KtDeclaration>(PositioningStrategy.DECLARATION_SIGNATURE)
val OVERRIDE_BY_INLINE by warning<KtDeclaration>(PositioningStrategy.DECLARATION_SIGNATURE)
}
val IMPORTS by object : DiagnosticGroup("Imports") {
@@ -609,6 +609,7 @@ object FirErrors {
val PRIVATE_CLASS_MEMBER_FROM_INLINE by error2<KtElement, FirBasedSymbol<*>, FirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val SUPER_CALL_FROM_PUBLIC_INLINE by warning1<KtElement, FirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val DECLARATION_CANT_BE_INLINED by error0<KtDeclaration>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
val OVERRIDE_BY_INLINE by warning0<KtDeclaration>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
// Imports
val CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON by error1<KtImportDirective, Name>(SourceElementPositioningStrategies.IMPORT_LAST_NAME)
@@ -11,7 +11,6 @@ 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.context.PersistentCheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.context.findClosest
import org.jetbrains.kotlin.fir.analysis.checkers.isInlineOnly
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
import org.jetbrains.kotlin.fir.analysis.checkers.util.checkChildrenWithCustomVisitor
@@ -49,9 +48,7 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
val effectiveVisibility = declaration.effectiveVisibility
checkInlineFunctionBody(declaration, effectiveVisibility, context, reporter)
checkParameters(declaration, context, reporter)
checkNothingToInline(declaration, context, reporter)
checkCanBeInlined(declaration, effectiveVisibility, context, reporter)
checkCallableDeclaration(declaration, context, reporter)
}
private fun checkInlineFunctionBody(
@@ -337,9 +334,12 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
}
}
private fun checkParameters(function: FirFunction, context: CheckerContext, reporter: DiagnosticReporter) {
if (function !is FirSimpleFunction) return
private fun checkParameters(
function: FirSimpleFunction,
overriddenSymbols: List<FirCallableSymbol<out FirCallableDeclaration>>,
context: CheckerContext,
reporter: DiagnosticReporter
) {
for (param in function.valueParameters) {
if (param.isNoinline) continue
@@ -365,16 +365,7 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
}
//check for inherited default values
val classSymbol = function.containingClass()?.toSymbol(context.session) as? FirClassSymbol<*> ?: return
if (!function.isOverride) return
if (!function.valueParameters.any { it.defaultValue == null }) return
val scope = classSymbol.unsubstitutedScope(context)
//this call is needed because AbstractFirUseSiteMemberScope collect overrides in it only,
//and not in processDirectOverriddenFunctionsWithBaseScope
scope.processFunctionsByName(function.name) { }
val overriddenMembers = scope.getDirectOverriddenMembers(function.symbol, true)
val paramsWithDefaults = overriddenMembers.flatMap { it ->
val paramsWithDefaults = overriddenSymbols.flatMap {
if (it !is FirFunctionSymbol<*>) return@flatMap emptyList<Int>()
it.valueParameterSymbols.mapIndexedNotNull { idx, param ->
idx.takeIf { param.hasDefaultValue }
@@ -392,8 +383,17 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
}
}
private fun checkNothingToInline(function: FirFunction, context: CheckerContext, reporter: DiagnosticReporter) {
if (function !is FirSimpleFunction) return
private fun FirCallableDeclaration.getOverriddenSymbols(context: CheckerContext): List<FirCallableSymbol<out FirCallableDeclaration>> {
if (!this.isOverride) return emptyList()
val classSymbol = this.containingClass()?.toSymbol(context.session) as? FirClassSymbol<*> ?: return emptyList()
val scope = classSymbol.unsubstitutedScope(context)
//this call is needed because AbstractFirUseSiteMemberScope collect overrides in it only,
//and not in processDirectOverriddenFunctionsWithBaseScope
scope.processFunctionsByName(this.symbol.name) { }
return scope.getDirectOverriddenMembers(this.symbol, true)
}
private fun checkNothingToInline(function: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) {
if (function.isExpect || function.isSuspend) return
if (function.typeParameters.any { it.symbol.isReified }) return
val hasInlinableParameters =
@@ -409,20 +409,32 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
}
private fun checkCanBeInlined(
declaration: FirFunction,
declaration: FirCallableDeclaration,
effectiveVisibility: EffectiveVisibility,
context: CheckerContext,
reporter: DiagnosticReporter
) {
if (declaration !is FirSimpleFunction) return
if (declaration.containingClass() == null) return
if (effectiveVisibility == EffectiveVisibility.PrivateInClass) return
val isFinal = when (declaration) {
is FirPropertyAccessor -> context.findClosest<FirProperty>()?.isFinal ?: declaration.isFinal
else -> declaration.isFinal
}
if (!isFinal) {
): Boolean {
if (declaration.containingClass() == null) return true
if (effectiveVisibility == EffectiveVisibility.PrivateInClass) return true
if (!declaration.isFinal) {
reporter.reportOn(declaration.source, FirErrors.DECLARATION_CANT_BE_INLINED, context)
return false
}
return true
}
internal fun checkCallableDeclaration(declaration: FirCallableDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration is FirPropertyAccessor) return
val overriddenSymbols = declaration.getOverriddenSymbols(context)
if (declaration is FirSimpleFunction) {
checkParameters(declaration, overriddenSymbols, context, reporter)
checkNothingToInline(declaration, context, reporter)
}
val canBeInlined = checkCanBeInlined(declaration, declaration.effectiveVisibility, context, reporter)
if (canBeInlined && overriddenSymbols.isNotEmpty()) {
reporter.reportOn(declaration.source, FirErrors.OVERRIDE_BY_INLINE, context)
}
}
}
@@ -5,26 +5,16 @@
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
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.containingClass
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.utils.effectiveVisibility
import org.jetbrains.kotlin.fir.declarations.utils.isFinal
import org.jetbrains.kotlin.fir.declarations.utils.isInline
object FirInlinePropertyChecker : FirPropertyChecker() {
override fun check(declaration: FirProperty, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.containingClass() == null) return
if (declaration.effectiveVisibility == EffectiveVisibility.PrivateInClass) return
if (!declaration.isFinal && (declaration.getter?.isInline == true || declaration.setter?.isInline == true)) {
reporter.reportOn(declaration.source, FirErrors.DECLARATION_CANT_BE_INLINED, context)
if (declaration.getter?.isInline == true || declaration.setter?.isInline == true) {
FirInlineDeclarationChecker.checkCallableDeclaration(declaration, context, reporter)
}
}
}
@@ -298,6 +298,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OPERATOR_RENAMED_
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OTHER_ERROR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OUTER_CLASS_ARGUMENTS_REQUIRED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OVERLOAD_RESOLUTION_AMBIGUITY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OVERRIDE_BY_INLINE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OVERRIDING_FINAL_MEMBER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PACKAGE_CANNOT_BE_IMPORTED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED
@@ -930,6 +931,7 @@ class FirDefaultErrorMessages {
map.put(INLINE_CLASS_CANNOT_EXTEND_CLASSES, "Inline class cannot extend classes")
map.put(INLINE_CLASS_CANNOT_BE_RECURSIVE, "Inline class cannot be recursive")
map.put(DECLARATION_CANT_BE_INLINED, "'inline' modifier is not allowed on virtual members. Only private or final members can be inlined")
map.put(OVERRIDE_BY_INLINE, "Override by an inline function")
map.put(RESERVED_MEMBER_INSIDE_INLINE_CLASS, "Member with the name ''{0}'' is reserved for future releases", TO_STRING)
map.put(
SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_INLINE_CLASS,
@@ -40,7 +40,7 @@ abstract class Base {
}
class Derived : Base() {
override final inline fun foo(<!NOT_YET_SUPPORTED_IN_INLINE!>f: () -> Unit<!>) {
<!OVERRIDE_BY_INLINE!>override final inline fun foo(<!NOT_YET_SUPPORTED_IN_INLINE!>f: () -> Unit<!>)<!> {
f()
}
}
@@ -39,7 +39,7 @@ abstract class Base {
}
class Derived : Base() {
override final inline fun foo(<!NOT_YET_SUPPORTED_IN_INLINE!>f: () -> Unit<!>) {
<!OVERRIDE_BY_INLINE!>override final inline fun foo(<!NOT_YET_SUPPORTED_IN_INLINE!>f: () -> Unit<!>)<!> {
f()
}
}
@@ -12,7 +12,7 @@ abstract class Base {
}
class Derived : Base() {
override final inline fun withDefault(
<!OVERRIDE_BY_INLINE!>override final inline fun withDefault(
<!NOT_YET_SUPPORTED_IN_INLINE!>f: () -> Unit<!>
) {}
)<!> {}
}
@@ -7,53 +7,53 @@ interface IBase {
}
class CDerived : IBase {
override inline final fun foo() {}
override inline fun bar() {}
override inline final fun <reified T> qux(x: T) {}
<!OVERRIDE_BY_INLINE!>override inline final fun foo()<!> {}
<!OVERRIDE_BY_INLINE!>override inline fun bar()<!> {}
<!OVERRIDE_BY_INLINE!>override inline final fun <reified T> qux(x: T)<!> {}
class CNested : IBase {
override inline final fun foo() {}
override inline fun bar() {}
override inline final fun <reified T> qux(x: T) {}
<!OVERRIDE_BY_INLINE!>override inline final fun foo()<!> {}
<!OVERRIDE_BY_INLINE!>override inline fun bar()<!> {}
<!OVERRIDE_BY_INLINE!>override inline final fun <reified T> qux(x: T)<!> {}
}
val anObject = object : IBase {
override inline final fun foo() {}
override inline fun bar() {}
override inline final fun <reified T> qux(x: T) {}
<!OVERRIDE_BY_INLINE!>override inline final fun foo()<!> {}
<!OVERRIDE_BY_INLINE!>override inline fun bar()<!> {}
<!OVERRIDE_BY_INLINE!>override inline final fun <reified T> qux(x: T)<!> {}
}
fun aMethod() {
class CLocal : IBase {
override inline final fun foo() {}
override inline fun bar() {}
override inline final fun <reified T> qux(x: T) {}
<!OVERRIDE_BY_INLINE!>override inline final fun foo()<!> {}
<!OVERRIDE_BY_INLINE!>override inline fun bar()<!> {}
<!OVERRIDE_BY_INLINE!>override inline final fun <reified T> qux(x: T)<!> {}
}
}
}
open class COpen : IBase {
override inline final fun foo() {}
<!OVERRIDE_BY_INLINE!>override inline final fun foo()<!> {}
<!DECLARATION_CANT_BE_INLINED!>override inline fun bar()<!> {}
override inline final fun <reified T> qux(x: T) {}
<!OVERRIDE_BY_INLINE!>override inline final fun <reified T> qux(x: T)<!> {}
open class COpenNested : IBase {
override inline final fun foo() {}
<!OVERRIDE_BY_INLINE!>override inline final fun foo()<!> {}
<!DECLARATION_CANT_BE_INLINED!>override inline fun bar()<!> {}
override inline final fun <reified T> qux(x: T) {}
<!OVERRIDE_BY_INLINE!>override inline final fun <reified T> qux(x: T)<!> {}
}
val anObject = object : IBase {
override inline final fun foo() {}
override inline fun bar() {}
override inline final fun <reified T> qux(x: T) {}
<!OVERRIDE_BY_INLINE!>override inline final fun foo()<!> {}
<!OVERRIDE_BY_INLINE!>override inline fun bar()<!> {}
<!OVERRIDE_BY_INLINE!>override inline final fun <reified T> qux(x: T)<!> {}
}
fun aMethod() {
open class COpenLocal : IBase {
override inline final fun foo() {}
<!OVERRIDE_BY_INLINE!>override inline final fun foo()<!> {}
<!DECLARATION_CANT_BE_INLINED!>override inline fun bar()<!> {}
override inline final fun <reified T> qux(x: T) {}
<!OVERRIDE_BY_INLINE!>override inline final fun <reified T> qux(x: T)<!> {}
}
}
}
@@ -27,7 +27,7 @@ open class Base {
}
class Derived: Base() {
inline final override fun foo(<!NOT_YET_SUPPORTED_IN_INLINE!>a: Int<!>) {
<!OVERRIDE_BY_INLINE!>inline final override fun foo(<!NOT_YET_SUPPORTED_IN_INLINE!>a: Int<!>)<!> {
}
}
@@ -3173,6 +3173,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.OVERRIDE_BY_INLINE) { firDiagnostic ->
OverrideByInlineImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON) { firDiagnostic ->
CannotAllUnderImportFromSingletonImpl(
firDiagnostic.a,
@@ -2214,6 +2214,10 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = DeclarationCantBeInlined::class
}
abstract class OverrideByInline : KtFirDiagnostic<KtDeclaration>() {
override val diagnosticClass get() = OverrideByInline::class
}
abstract class CannotAllUnderImportFromSingleton : KtFirDiagnostic<KtImportDirective>() {
override val diagnosticClass get() = CannotAllUnderImportFromSingleton::class
abstract val objectName: Name
@@ -3574,6 +3574,13 @@ internal class DeclarationCantBeInlinedImpl(
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class OverrideByInlineImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.OverrideByInline(), KtAbstractFirDiagnostic<KtDeclaration> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class CannotAllUnderImportFromSingletonImpl(
override val objectName: Name,
firDiagnostic: FirPsiDiagnostic,