FE KT-47939 callable references to functional interface constructors

Allow callable references to Kotlin 'fun interface' constructors.
Prohibit callable references to Java SAM interface constructors.
This commit is contained in:
Dmitry Petrov
2021-11-29 15:10:14 +03:00
committed by TeamCityServer
parent bfb6e73728
commit 1fd0dec5e7
26 changed files with 188 additions and 43 deletions
@@ -183,5 +183,6 @@ object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") {
ProhibitSpreadOnSignaturePolymorphicCall,
PositioningStrategy.SPREAD_OPERATOR
)
val JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE by error<PsiElement>()
}
}
@@ -125,6 +125,7 @@ object FirJvmErrors {
val SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC by error0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val CONCURRENT_HASH_MAP_CONTAINS_OPERATOR by deprecationError0<PsiElement>(ProhibitConcurrentHashMapContains)
val SPREAD_ON_SIGNATURE_POLYMORPHIC_CALL by deprecationError0<PsiElement>(ProhibitSpreadOnSignaturePolymorphicCall, SourceElementPositioningStrategies.SPREAD_OPERATOR)
val JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE by error0<PsiElement>()
init {
RootDiagnosticRendererFactory.registerFactory(FirJvmErrorsDefaultMessages)
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.utils.isAbstract
import org.jetbrains.kotlin.fir.declarations.utils.isFinal
import org.jetbrains.kotlin.fir.declarations.utils.isInterface
import org.jetbrains.kotlin.fir.declarations.utils.isJavaOrEnhancement
import org.jetbrains.kotlin.fir.dispatchReceiverTypeOrNull
import org.jetbrains.kotlin.fir.originalOrSelf
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
@@ -41,7 +42,6 @@ import org.jetbrains.kotlin.name.ClassId
* Note that, in case of multi-override, if any super member requires `override`, then the `override` keyword cannot be omitted.
*/
object FirJvmOverridesBackwardCompatibilityHelper : FirOverridesBackwardCompatibilityHelper {
private val javaOrigin = setOf(FirDeclarationOrigin.Java, FirDeclarationOrigin.Enhancement)
private val platformDependentAnnotation = ClassId.fromString("kotlin/internal/PlatformDependent")
override fun overrideCanBeOmitted(
@@ -71,7 +71,7 @@ object FirJvmOverridesBackwardCompatibilityHelper : FirOverridesBackwardCompatib
return true
}
if (originalMember.origin !in javaOrigin) return false
if (!originalMember.isJavaOrEnhancement) return false
val containingClassName = originalMember.containingClass()?.classId?.asSingleFqName()?.toUnsafe() ?: return false
// If the super class is mapped to a Kotlin built-in class, then we don't require `override` keyword.
if (JavaToKotlinClassMap.mapKotlinToJava(containingClassName) != null) {
@@ -16,7 +16,8 @@ object JvmExpressionCheckers : ExpressionCheckers() {
override val qualifiedAccessExpressionCheckers: Set<FirQualifiedAccessExpressionChecker>
get() = setOf(
FirInterfaceDefaultMethodCallChecker
FirInterfaceDefaultMethodCallChecker,
FirJavaSamInterfaceConstructorReferenceChecker
)
override val functionCallCheckers: Set<FirFunctionCallChecker>
@@ -5,13 +5,13 @@
package org.jetbrains.kotlin.fir.analysis.jvm.checkers.expression
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.StandardTypes
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.utils.isJavaOrEnhancement
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.argumentMapping
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
@@ -46,11 +46,10 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
* doesn't do anything to prevent this from happening.
*/
object FirJavaGenericVarianceViolationTypeChecker : FirFunctionCallChecker() {
private val javaOrigin = setOf(FirDeclarationOrigin.Java, FirDeclarationOrigin.Enhancement)
override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) {
val calleeFunction = expression.calleeReference.toResolvedCallableSymbol() as? FirFunctionSymbol<*> ?: return
if (calleeFunction.originalOrSelf().origin !in javaOrigin) {
if (!calleeFunction.originalOrSelf().isJavaOrEnhancement) {
return
}
val argumentMapping = expression.argumentMapping ?: return
@@ -0,0 +1,40 @@
/*
* 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.jvm.checkers.expression
import org.jetbrains.kotlin.config.LanguageFeature
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.expression.FirQualifiedAccessExpressionChecker
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.utils.isFun
import org.jetbrains.kotlin.fir.declarations.utils.isJavaOrEnhancement
import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
object FirJavaSamInterfaceConstructorReferenceChecker : FirQualifiedAccessExpressionChecker() {
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
if (expression !is FirCallableReferenceAccess) return
if (!context.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitJavaSamInterfaceConstructorReference)) return
val reference = expression.calleeReference as? FirResolvedNamedReference ?: return
val referredSymbol = reference.resolvedSymbol
if (referredSymbol is FirNamedFunctionSymbol &&
referredSymbol.origin == FirDeclarationOrigin.SamConstructor
) {
val samClassSymbol = referredSymbol.resolvedReturnTypeRef.toRegularClassSymbol(context.session) ?: return
if (samClassSymbol.isFun && samClassSymbol.isJavaOrEnhancement) {
reporter.reportOn(reference.source, FirJvmErrors.JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE, context)
}
}
}
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.analysis.checkers.expression
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
@@ -12,6 +13,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_CON
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.utils.isFun
import org.jetbrains.kotlin.fir.declarations.utils.isJavaOrEnhancement
import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.resolved
@@ -20,15 +22,18 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
object FirFunInterfaceConstructorReferenceChecker : FirQualifiedAccessExpressionChecker() {
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
if (expression !is FirCallableReferenceAccess) return
if (context.languageVersionSettings.supportsFeature(LanguageFeature.AllowKotlinFunInterfaceConstructorReference)) return
val reference = expression.calleeReference.resolved ?: return
val referredSymbol = reference.resolvedSymbol
if (referredSymbol is FirNamedFunctionSymbol &&
referredSymbol.origin == FirDeclarationOrigin.SamConstructor &&
referredSymbol.resolvedReturnTypeRef.toRegularClassSymbol(context.session)?.isFun == true
referredSymbol.origin == FirDeclarationOrigin.SamConstructor
) {
reporter.reportOn(reference.source, FUN_INTERFACE_CONSTRUCTOR_REFERENCE, context)
val samClassSymbol = referredSymbol.resolvedReturnTypeRef.toRegularClassSymbol(context.session) ?: return
if (samClassSymbol.isFun && !samClassSymbol.isJavaOrEnhancement) {
reporter.reportOn(reference.source, FUN_INTERFACE_CONSTRUCTOR_REFERENCE, context)
}
}
}
}