[FIR] Add NOT_YET_SUPPORTED_IN_INLINE diagnostic

This commit is contained in:
Andrey Zinovyev
2021-07-19 18:21:56 +03:00
committed by teamcityserver
parent bc75a21852
commit 28344c8530
28 changed files with 151 additions and 81 deletions
@@ -1130,6 +1130,10 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<Symbol>("parameter")
}
val NOT_YET_SUPPORTED_IN_INLINE by error<KtDeclaration>(PositioningStrategy.NOT_SUPPORTED_IN_INLINE_MOST_RELEVANT) {
parameter<String>("message")
}
val RECURSION_IN_INLINE by error<KtElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
parameter<Symbol>("symbol")
}
@@ -95,6 +95,7 @@ enum class PositioningStrategy(private val strategy: String? = null) {
DATA_MODIFIER,
SPREAD_OPERATOR,
DECLARATION_WITH_BODY,
NOT_SUPPORTED_IN_INLINE_MOST_RELEVANT,
INCOMPATIBLE_DECLARATION,
ACTUAL_DECLARATION_NAME,
UNREACHABLE_CODE,
@@ -598,6 +598,7 @@ object FirErrors {
// Inline
val USAGE_IS_NOT_INLINABLE by error1<KtElement, FirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val NON_LOCAL_RETURN_NOT_ALLOWED by error1<KtElement, FirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val NOT_YET_SUPPORTED_IN_INLINE by error1<KtDeclaration, String>(SourceElementPositioningStrategies.NOT_SUPPORTED_IN_INLINE_MOST_RELEVANT)
val RECURSION_IN_INLINE by error1<KtElement, FirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val NON_PUBLIC_CALL_FROM_PUBLIC_INLINE by error2<KtElement, FirBasedSymbol<*>, FirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val PROTECTED_CONSTRUCTOR_CALL_FROM_PUBLIC_INLINE by error2<KtElement, FirBasedSymbol<*>, FirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
@@ -10,21 +10,22 @@ 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.context.PersistentCheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
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.declarations.utils.classId
import org.jetbrains.kotlin.fir.declarations.utils.effectiveVisibility
import org.jetbrains.kotlin.fir.declarations.utils.isInline
import org.jetbrains.kotlin.fir.declarations.utils.visibility
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.resolve.inference.isBuiltinFunctionalType
import org.jetbrains.kotlin.fir.resolve.inference.isFunctionalType
import org.jetbrains.kotlin.fir.resolve.inference.isSuspendFunctionType
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.resolve.transformers.publishedApiEffectiveVisibility
import org.jetbrains.kotlin.fir.scopes.getDirectOverriddenMembers
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.coneType
@@ -37,11 +38,15 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
override fun check(declaration: FirFunction, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isInline) return
// local inline functions are prohibited
if (declaration.isLocalMember) return
if (declaration.isLocalMember) {
reporter.reportOn(declaration.source, FirErrors.NOT_YET_SUPPORTED_IN_INLINE, "Local inline functions", context)
return
}
if (declaration !is FirPropertyAccessor && declaration !is FirSimpleFunction) return
val effectiveVisibility = declaration.effectiveVisibility
checkInlineFunctionBody(declaration, effectiveVisibility, context, reporter)
checkParameters(declaration, context, reporter)
}
private fun checkInlineFunctionBody(
@@ -64,7 +69,7 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
context.session,
reporter
)
body.checkChildrenWithCustomVisitor(context, visitor)
body.checkChildrenWithCustomVisitor((context as PersistentCheckerContext).addDeclaration(function), visitor)
}
private class Visitor(
@@ -102,6 +107,22 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
checkQualifiedAccess(variableAssignment, setterSymbol, data)
}
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)
}
}
private fun checkReceiversOfQualifiedAccessExpression(
qualifiedAccessExpression: FirQualifiedAccessExpression,
targetSymbol: FirBasedSymbol<*>?,
@@ -310,4 +331,49 @@ object FirInlineDeclarationChecker : FirFunctionChecker() {
return containingClassVisibility == Visibilities.Private || containingClassVisibility == Visibilities.PrivateToThis
}
}
private fun checkParameters(function: FirFunction, context: CheckerContext, reporter: DiagnosticReporter) {
if (function !is FirSimpleFunction) return
if (function.isSuspend) {
function.valueParameters
.filter { !it.isNoinline && it.defaultValue != null && it.returnTypeRef.coneType.isSuspendFunctionType(context.session) }
.forEach { param ->
reporter.reportOn(
param.source,
FirErrors.NOT_YET_SUPPORTED_IN_INLINE,
"Suspend functional parameters with default values",
context
)
}
}
//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 ->
if (it !is FirFunctionSymbol<*>) return@flatMap emptyList<Int>()
it.valueParameterSymbols.mapIndexedNotNull { idx, param ->
idx.takeIf { param.hasDefaultValue }
}
}.toSet()
function.valueParameters.forEachIndexed { idx, param ->
if (param.defaultValue == null && paramsWithDefaults.contains(idx)) {
reporter.reportOn(
param.source,
FirErrors.NOT_YET_SUPPORTED_IN_INLINE,
"Functional parameters with inherited default values",
context
)
}
}
}
}
@@ -278,6 +278,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_NULL_ASSERTIO
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_ACTUAL_FOR_EXPECT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_YET_SUPPORTED_IN_INLINE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_COMPANION_OBJECT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_ELSE_IN_WHEN
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_GET_METHOD
@@ -1481,6 +1482,7 @@ class FirDefaultErrorMessages {
SYMBOL,
SYMBOL
)
map.put(NOT_YET_SUPPORTED_IN_INLINE, "{0} are not yet supported in inline functions", STRING)
map.put(SUPER_CALL_FROM_PUBLIC_INLINE, "Accessing super members from public-API inline function is deprecated", SYMBOL)
//imports
@@ -860,6 +860,26 @@ object LightTreePositioningStrategies {
}
}
}
val NOT_SUPPORTED_IN_INLINE_MOST_RELEVANT: LightTreePositioningStrategy = object : LightTreePositioningStrategy() {
override fun mark(
node: LighterASTNode,
startOffset: Int,
endOffset: Int,
tree: FlyweightCapableTreeStructure<LighterASTNode>
): List<TextRange> {
val nodeToMark = when (node.tokenType) {
KtNodeTypes.CLASS ->
tree.findChildByType(node, KtTokens.CLASS_KEYWORD)
KtNodeTypes.OBJECT_DECLARATION ->
tree.findChildByType(node, KtTokens.OBJECT_KEYWORD)
KtNodeTypes.FUN ->
tree.inlineModifier(node) ?: tree.findChildByType(node, KtTokens.FUN_KEYWORD)
else -> node
}
return markElement(nodeToMark ?: node, startOffset, endOffset, tree, node)
}
}
}
fun FirSourceElement.hasValOrVar(): Boolean =
@@ -982,6 +1002,9 @@ internal fun FlyweightCapableTreeStructure<LighterASTNode>.modalityModifier(decl
internal fun FlyweightCapableTreeStructure<LighterASTNode>.overrideModifier(declaration: LighterASTNode): LighterASTNode? =
modifierList(declaration)?.let { findChildByType(it, KtTokens.OVERRIDE_KEYWORD) }
internal fun FlyweightCapableTreeStructure<LighterASTNode>.inlineModifier(declaration: LighterASTNode): LighterASTNode? =
modifierList(declaration)?.let { findChildByType(it, KtTokens.INLINE_KEYWORD) }
internal fun FlyweightCapableTreeStructure<LighterASTNode>.typeParametersList(declaration: LighterASTNode): LighterASTNode? =
findChildByType(declaration, KtNodeTypes.TYPE_PARAMETER_LIST)
@@ -281,4 +281,9 @@ object SourceElementPositioningStrategies {
// TODO
val ACTUAL_DECLARATION_NAME = DEFAULT
val INCOMPATIBLE_DECLARATION = DEFAULT
val NOT_SUPPORTED_IN_INLINE_MOST_RELEVANT = SourceElementPositioningStrategy(
LightTreePositioningStrategies.NOT_SUPPORTED_IN_INLINE_MOST_RELEVANT,
PositioningStrategies.NOT_SUPPORTED_IN_INLINE_MOST_RELEVANT
)
}
@@ -70,6 +70,7 @@ abstract class AbstractFirUseSiteMemberScope(
functionSymbol: FirNamedFunctionSymbol,
processor: (FirNamedFunctionSymbol, FirTypeScope) -> ProcessorAction
): ProcessorAction =
//directOverriddenFunctions might be not filled for functionSymbol if it is not from processFunctionsByName call
doProcessDirectOverriddenCallables(
functionSymbol, processor, directOverriddenFunctions, superTypesScope,
FirTypeScope::processDirectOverriddenFunctionsWithBaseScope
@@ -17,7 +17,7 @@ fun find(): Any? {
}
fun find4(): Any? {
inline fun visit(element: Any) {
<!NOT_YET_SUPPORTED_IN_INLINE!>inline<!> fun visit(element: Any) {
<!RETURN_NOT_ALLOWED!>return@find4<!> element
}
return null
@@ -40,9 +40,9 @@ abstract class Base {
}
class Derived : Base() {
override final inline fun foo(f: () -> Unit) {
override final inline fun foo(<!NOT_YET_SUPPORTED_IN_INLINE!>f: () -> Unit<!>) {
f()
}
}
inline fun default11(s : () -> Derived = ::Derived) {}
inline fun default11(s : () -> Derived = ::Derived) {}
@@ -1,10 +0,0 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE -NOTHING_TO_INLINE
// SKIP_TXT
suspend inline fun test1(s : suspend () -> String = { "OK" }) {}
suspend inline fun test2(s : () -> String = { "OK" }) {}
suspend inline fun test3(crossinline s : suspend () -> String = { "OK" }) {}
suspend inline fun test4(crossinline s : () -> String = { "OK" }) {}
suspend inline fun test5(noinline s : suspend () -> String = { "OK" }) {}
suspend inline fun test6(noinline s : () -> String = { "OK" }) {}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE -NOTHING_TO_INLINE
// SKIP_TXT
@@ -39,9 +39,9 @@ abstract class Base {
}
class Derived : Base() {
override final inline fun foo(f: () -> Unit) {
override final inline fun foo(<!NOT_YET_SUPPORTED_IN_INLINE!>f: () -> Unit<!>) {
f()
}
}
inline fun default11(s : () -> Derived = ::Derived) {}
inline fun default11(s : () -> Derived = ::Derived) {}
@@ -1,4 +0,0 @@
fun main() {
inline fun a(){
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun main() {
<!NOT_YET_SUPPORTED_IN_INLINE!>inline<!> fun a(){
}
@@ -1,10 +1,10 @@
inline fun inlineFun() {
fun localFun() {}
class LocalClass {}
<!NOT_YET_SUPPORTED_IN_INLINE!>fun<!> localFun() {}
<!NOT_YET_SUPPORTED_IN_INLINE!>class<!> LocalClass {}
}
fun outerFun() {
inline fun localInlineFun() {}
<!NOT_YET_SUPPORTED_IN_INLINE!>inline<!> fun localInlineFun() {}
}
abstract class Base {
@@ -13,6 +13,6 @@ abstract class Base {
class Derived : Base() {
override final inline fun withDefault(
f: () -> Unit
<!NOT_YET_SUPPORTED_IN_INLINE!>f: () -> Unit<!>
) {}
}
}
@@ -1,14 +1,14 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
inline fun <R> inlineFunOnlyLocal(crossinline p: () -> R) {
fun a() {
<!NOT_YET_SUPPORTED_IN_INLINE!>fun<!> a() {
val z = p()
}
a()
}
inline fun <R> inlineFun(p: () -> R) {
fun a() {
<!NOT_YET_SUPPORTED_IN_INLINE!>fun<!> a() {
p()
}
a()
@@ -1,7 +1,7 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
inline fun <R> inlineFunOnlyLocal(crossinline p: () -> R) {
class A {
<!NOT_YET_SUPPORTED_IN_INLINE!>class<!> A {
val z = p()
@@ -12,7 +12,7 @@ inline fun <R> inlineFunOnlyLocal(crossinline p: () -> R) {
}
inline fun <R> inlineFun(p: () -> R) {
class A {
<!NOT_YET_SUPPORTED_IN_INLINE!>class<!> A {
val z = p()
@@ -1,13 +0,0 @@
public fun test() {
class Z {
public fun localFun() {
}
}
inline fun localFun2() {
Z().localFun()
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
public fun test() {
class Z {
@@ -1,11 +0,0 @@
public fun test() {
fun localFun() {
}
inline fun localFun2() {
localFun()
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
public fun test() {
fun localFun() {
@@ -1,20 +0,0 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
inline val z: Int
get() {
class A {
fun a() {
class AInner {}
}
}
<!LOCAL_OBJECT_NOT_ALLOWED!>object B<!>{
<!LOCAL_OBJECT_NOT_ALLOWED!>object BInner<!> {}
}
fun local() {
fun localInner() {}
}
return 1
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
inline val z: Int
@@ -3,7 +3,7 @@
inline fun unsupported() {
class A {
<!NOT_YET_SUPPORTED_IN_INLINE!>class<!> A {
fun a() {
class AInner {}
}
@@ -13,7 +13,7 @@ inline fun unsupported() {
<!LOCAL_OBJECT_NOT_ALLOWED!>object BInner<!> {}
}
fun local() {
<!NOT_YET_SUPPORTED_IN_INLINE!>fun<!> local() {
fun localInner() {}
}
}
@@ -27,7 +27,7 @@ open class Base {
}
class Derived: Base() {
inline final override fun foo(a: Int) {
inline final override fun foo(<!NOT_YET_SUPPORTED_IN_INLINE!>a: Int<!>) {
}
}
@@ -3092,6 +3092,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.NOT_YET_SUPPORTED_IN_INLINE) { firDiagnostic ->
NotYetSupportedInInlineImpl(
firDiagnostic.a,
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.RECURSION_IN_INLINE) { firDiagnostic ->
RecursionInInlineImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a.fir),
@@ -2155,6 +2155,11 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val parameter: KtSymbol
}
abstract class NotYetSupportedInInline : KtFirDiagnostic<KtDeclaration>() {
override val diagnosticClass get() = NotYetSupportedInInline::class
abstract val message: String
}
abstract class RecursionInInline : KtFirDiagnostic<KtElement>() {
override val diagnosticClass get() = RecursionInInline::class
abstract val symbol: KtSymbol
@@ -3482,6 +3482,14 @@ internal class NonLocalReturnNotAllowedImpl(
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class NotYetSupportedInInlineImpl(
override val message: String,
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.NotYetSupportedInInline(), KtAbstractFirDiagnostic<KtDeclaration> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class RecursionInInlineImpl(
override val symbol: KtSymbol,
firDiagnostic: FirPsiDiagnostic,