Forbid ExtensionFunctionType on functional types without parameters
#KT-43527 Fixed
This commit is contained in:
+6
@@ -945,6 +945,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
|||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
add(FirErrors.WRONG_EXTENSION_FUNCTION_TYPE) { firDiagnostic ->
|
||||||
|
WrongExtensionFunctionTypeImpl(
|
||||||
|
firDiagnostic as KtPsiDiagnostic,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
add(FirErrors.OPT_IN_USAGE) { firDiagnostic ->
|
add(FirErrors.OPT_IN_USAGE) { firDiagnostic ->
|
||||||
OptInUsageImpl(
|
OptInUsageImpl(
|
||||||
firDiagnostic.a,
|
firDiagnostic.a,
|
||||||
|
|||||||
+4
@@ -680,6 +680,10 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
|||||||
override val diagnosticClass get() = NotAClass::class
|
override val diagnosticClass get() = NotAClass::class
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class WrongExtensionFunctionType : KtFirDiagnostic<KtAnnotationEntry>() {
|
||||||
|
override val diagnosticClass get() = WrongExtensionFunctionType::class
|
||||||
|
}
|
||||||
|
|
||||||
abstract class OptInUsage : KtFirDiagnostic<PsiElement>() {
|
abstract class OptInUsage : KtFirDiagnostic<PsiElement>() {
|
||||||
override val diagnosticClass get() = OptInUsage::class
|
override val diagnosticClass get() = OptInUsage::class
|
||||||
abstract val optInMarkerFqName: FqName
|
abstract val optInMarkerFqName: FqName
|
||||||
|
|||||||
+5
@@ -816,6 +816,11 @@ internal class NotAClassImpl(
|
|||||||
override val token: ValidityToken,
|
override val token: ValidityToken,
|
||||||
) : KtFirDiagnostic.NotAClass(), KtAbstractFirDiagnostic<PsiElement>
|
) : KtFirDiagnostic.NotAClass(), KtAbstractFirDiagnostic<PsiElement>
|
||||||
|
|
||||||
|
internal class WrongExtensionFunctionTypeImpl(
|
||||||
|
override val firDiagnostic: KtPsiDiagnostic,
|
||||||
|
override val token: ValidityToken,
|
||||||
|
) : KtFirDiagnostic.WrongExtensionFunctionType(), KtAbstractFirDiagnostic<KtAnnotationEntry>
|
||||||
|
|
||||||
internal class OptInUsageImpl(
|
internal class OptInUsageImpl(
|
||||||
override val optInMarkerFqName: FqName,
|
override val optInMarkerFqName: FqName,
|
||||||
override val message: String,
|
override val message: String,
|
||||||
|
|||||||
+1
@@ -287,6 +287,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
|||||||
val REPEATED_ANNOTATION by error<KtAnnotationEntry>()
|
val REPEATED_ANNOTATION by error<KtAnnotationEntry>()
|
||||||
val REPEATED_ANNOTATION_WARNING by warning<KtAnnotationEntry>()
|
val REPEATED_ANNOTATION_WARNING by warning<KtAnnotationEntry>()
|
||||||
val NOT_A_CLASS by error<PsiElement>()
|
val NOT_A_CLASS by error<PsiElement>()
|
||||||
|
val WRONG_EXTENSION_FUNCTION_TYPE by error<KtAnnotationEntry>()
|
||||||
}
|
}
|
||||||
|
|
||||||
val OPT_IN by object : DiagnosticGroup("OptIn") {
|
val OPT_IN by object : DiagnosticGroup("OptIn") {
|
||||||
|
|||||||
@@ -253,6 +253,7 @@ object FirErrors {
|
|||||||
val REPEATED_ANNOTATION by error0<KtAnnotationEntry>()
|
val REPEATED_ANNOTATION by error0<KtAnnotationEntry>()
|
||||||
val REPEATED_ANNOTATION_WARNING by warning0<KtAnnotationEntry>()
|
val REPEATED_ANNOTATION_WARNING by warning0<KtAnnotationEntry>()
|
||||||
val NOT_A_CLASS by error0<PsiElement>()
|
val NOT_A_CLASS by error0<PsiElement>()
|
||||||
|
val WRONG_EXTENSION_FUNCTION_TYPE by error0<KtAnnotationEntry>()
|
||||||
|
|
||||||
// OptIn
|
// OptIn
|
||||||
val OPT_IN_USAGE by warning2<PsiElement, FqName, String>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
val OPT_IN_USAGE by warning2<PsiElement, FqName, String>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||||
|
|||||||
+8
@@ -12,8 +12,11 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
|||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.withSuppressedDiagnostics
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.withSuppressedDiagnostics
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.classId
|
||||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
||||||
|
import org.jetbrains.kotlin.fir.types.isBuiltinFunctionalType
|
||||||
|
import org.jetbrains.kotlin.name.StandardClassIds
|
||||||
|
|
||||||
object FirTypeAnnotationChecker : FirTypeRefChecker() {
|
object FirTypeAnnotationChecker : FirTypeRefChecker() {
|
||||||
override fun check(typeRef: FirTypeRef, context: CheckerContext, reporter: DiagnosticReporter) {
|
override fun check(typeRef: FirTypeRef, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||||
@@ -29,6 +32,11 @@ object FirTypeAnnotationChecker : FirTypeRefChecker() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (annotation.classId == StandardClassIds.Annotations.ExtensionFunctionType) {
|
||||||
|
if (typeRef.type.isBuiltinFunctionalType(context.session) && typeRef.type.typeArguments.size <= 1) {
|
||||||
|
reporter.reportOn(annotation.source, FirErrors.WRONG_EXTENSION_FUNCTION_TYPE, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -561,6 +561,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_TYPE_MISMATCH
|
|||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VIRTUAL_MEMBER_HIDDEN
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VIRTUAL_MEMBER_HIDDEN
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_ANNOTATION_TARGET
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_ANNOTATION_TARGET
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_EXTENSION_FUNCTION_TYPE
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_GETTER_RETURN_TYPE
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_GETTER_RETURN_TYPE
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_INVOCATION_KIND
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_INVOCATION_KIND
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_LONG_SUFFIX
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_LONG_SUFFIX
|
||||||
@@ -853,6 +854,7 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
|||||||
"Opt-in requirement marker annotation on override makes no sense without the same marker on base declaration"
|
"Opt-in requirement marker annotation on override makes no sense without the same marker on base declaration"
|
||||||
)
|
)
|
||||||
map.put(NOT_A_CLASS, "Not a class")
|
map.put(NOT_A_CLASS, "Not a class")
|
||||||
|
map.put(WRONG_EXTENSION_FUNCTION_TYPE, "ExtensionFunctionType is forbidden on a function type without parameters")
|
||||||
|
|
||||||
// Exposed visibility group // #
|
// Exposed visibility group // #
|
||||||
map.put(
|
map.put(
|
||||||
|
|||||||
@@ -278,6 +278,7 @@ public interface Errors {
|
|||||||
DiagnosticFactory0<KtAnnotationEntry> REPEATED_ANNOTATION = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtAnnotationEntry> REPEATED_ANNOTATION = DiagnosticFactory0.create(ERROR);
|
||||||
DiagnosticFactory0<KtAnnotationEntry> REPEATED_ANNOTATION_WARNING = DiagnosticFactory0.create(WARNING);
|
DiagnosticFactory0<KtAnnotationEntry> REPEATED_ANNOTATION_WARNING = DiagnosticFactory0.create(WARNING);
|
||||||
DiagnosticFactory0<KtAnnotationEntry> NON_SOURCE_ANNOTATION_ON_INLINED_LAMBDA_EXPRESSION = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtAnnotationEntry> NON_SOURCE_ANNOTATION_ON_INLINED_LAMBDA_EXPRESSION = DiagnosticFactory0.create(ERROR);
|
||||||
|
DiagnosticFactory0<KtAnnotationEntry> WRONG_EXTENSION_FUNCTION_TYPE = DiagnosticFactory0.create(ERROR);
|
||||||
|
|
||||||
// Annotations
|
// Annotations
|
||||||
|
|
||||||
|
|||||||
+1
@@ -146,6 +146,7 @@ public class DefaultErrorMessages {
|
|||||||
MAP.put(REPEATED_ANNOTATION, "This annotation is not repeatable");
|
MAP.put(REPEATED_ANNOTATION, "This annotation is not repeatable");
|
||||||
MAP.put(REPEATED_ANNOTATION_WARNING, "This annotation is not repeatable");
|
MAP.put(REPEATED_ANNOTATION_WARNING, "This annotation is not repeatable");
|
||||||
MAP.put(NON_SOURCE_ANNOTATION_ON_INLINED_LAMBDA_EXPRESSION, "The lambda expression here is an inlined argument so this annotation cannot be stored anywhere");
|
MAP.put(NON_SOURCE_ANNOTATION_ON_INLINED_LAMBDA_EXPRESSION, "The lambda expression here is an inlined argument so this annotation cannot be stored anywhere");
|
||||||
|
MAP.put(WRONG_EXTENSION_FUNCTION_TYPE, "ExtensionFunctionType is forbidden on a function type without parameters");
|
||||||
|
|
||||||
MAP.put(INAPPLICABLE_TARGET_ON_PROPERTY, "''@{0}:'' annotations could be applied only to property declarations", TO_STRING);
|
MAP.put(INAPPLICABLE_TARGET_ON_PROPERTY, "''@{0}:'' annotations could be applied only to property declarations", TO_STRING);
|
||||||
MAP.put(INAPPLICABLE_TARGET_PROPERTY_IMMUTABLE, "''@{0}:'' annotations could be applied only to mutable properties", TO_STRING);
|
MAP.put(INAPPLICABLE_TARGET_PROPERTY_IMMUTABLE, "''@{0}:'' annotations could be applied only to mutable properties", TO_STRING);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.resolve
|
package org.jetbrains.kotlin.resolve
|
||||||
|
|
||||||
import org.jetbrains.kotlin.builtins.StandardNames
|
import org.jetbrains.kotlin.builtins.StandardNames
|
||||||
|
import org.jetbrains.kotlin.builtins.isFunctionOrKFunctionTypeWithAnySuspendability
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature.*
|
import org.jetbrains.kotlin.config.LanguageFeature.*
|
||||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
@@ -19,11 +20,13 @@ import org.jetbrains.kotlin.diagnostics.Errors
|
|||||||
import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce
|
import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.name.StandardClassIds
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getAnnotationEntries
|
import org.jetbrains.kotlin.psi.psiUtil.getAnnotationEntries
|
||||||
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
||||||
import org.jetbrains.kotlin.resolve.constants.EnumValue
|
import org.jetbrains.kotlin.resolve.constants.EnumValue
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
|
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getAnnotationRetention
|
import org.jetbrains.kotlin.resolve.descriptorUtil.getAnnotationRetention
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isAnnotatedWithKotlinRepeatable
|
import org.jetbrains.kotlin.resolve.descriptorUtil.isAnnotatedWithKotlinRepeatable
|
||||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||||
@@ -163,9 +166,9 @@ class AnnotationChecker(
|
|||||||
checkWithoutLanguageFeature: Boolean = false
|
checkWithoutLanguageFeature: Boolean = false
|
||||||
) {
|
) {
|
||||||
val shouldRunCheck = isSuperType || shouldCheckReferenceItself
|
val shouldRunCheck = isSuperType || shouldCheckReferenceItself
|
||||||
if (shouldRunCheck) {
|
|
||||||
for (entry in reference.annotationEntries) {
|
for (entry in reference.annotationEntries) {
|
||||||
val descriptor = trace.get(BindingContext.ANNOTATION, entry)
|
val descriptor = trace.get(BindingContext.ANNOTATION, entry)
|
||||||
|
if (shouldRunCheck) {
|
||||||
if (descriptor is LazyAnnotationDescriptor) {
|
if (descriptor is LazyAnnotationDescriptor) {
|
||||||
/*
|
/*
|
||||||
* There are no users of type annotations until backend, so if there are errors
|
* There are no users of type annotations until backend, so if there are errors
|
||||||
@@ -181,6 +184,12 @@ class AnnotationChecker(
|
|||||||
checkAnnotationEntry(entry, actualTargets, trace)
|
checkAnnotationEntry(entry, actualTargets, trace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (descriptor?.annotationClass?.classId == StandardClassIds.Annotations.ExtensionFunctionType) {
|
||||||
|
val type = trace[BindingContext.TYPE, reference]
|
||||||
|
if (type != null && type.isFunctionOrKFunctionTypeWithAnySuspendability && type.arguments.size <= 1) {
|
||||||
|
trace.report(Errors.WRONG_EXTENSION_FUNCTION_TYPE.on(entry))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val typeArguments = reference.typeElement?.typeArgumentsAsTypes ?: return
|
val typeArguments = reference.typeElement?.typeArgumentsAsTypes ?: return
|
||||||
for (typeArgument in typeArguments) {
|
for (typeArgument in typeArguments) {
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// WITH_STDLIB
|
||||||
|
// This test checks that annotations on extension function types are preserved. See the corresponding .txt file
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.TYPE)
|
||||||
|
annotation class ann
|
||||||
|
|
||||||
|
interface Some {
|
||||||
|
fun f1(): String.() -> Int
|
||||||
|
fun f2(): @ExtensionFunctionType() (String.() -> Int)
|
||||||
|
fun f3(): @ann String.() -> Int
|
||||||
|
fun f4(): @ExtensionFunctionType @ann() (String.() -> Int)
|
||||||
|
|
||||||
|
fun f5(): <!WRONG_EXTENSION_FUNCTION_TYPE!>@ExtensionFunctionType<!> () -> Int
|
||||||
|
|
||||||
|
fun f6(x: <!WRONG_EXTENSION_FUNCTION_TYPE!>@ExtensionFunctionType<!> () -> Int) {
|
||||||
|
<!OVERLOAD_RESOLUTION_AMBIGUITY!>println<!>(x <!UNRESOLVED_REFERENCE!>+<!> 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun f7(x: <!WRONG_EXTENSION_FUNCTION_TYPE!>@ExtensionFunctionType<!> Function0<Int>)
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// FIR_IDENTICAL
|
// WITH_STDLIB
|
||||||
// This test checks that annotations on extension function types are preserved. See the corresponding .txt file
|
// This test checks that annotations on extension function types are preserved. See the corresponding .txt file
|
||||||
|
|
||||||
@Target(AnnotationTarget.TYPE)
|
@Target(AnnotationTarget.TYPE)
|
||||||
@@ -9,4 +9,12 @@ interface Some {
|
|||||||
fun f2(): @ExtensionFunctionType() (String.() -> Int)
|
fun f2(): @ExtensionFunctionType() (String.() -> Int)
|
||||||
fun f3(): @ann String.() -> Int
|
fun f3(): @ann String.() -> Int
|
||||||
fun f4(): @ExtensionFunctionType @ann() (String.() -> Int)
|
fun f4(): @ExtensionFunctionType @ann() (String.() -> Int)
|
||||||
|
|
||||||
|
fun f5(): <!WRONG_EXTENSION_FUNCTION_TYPE!>@ExtensionFunctionType<!> () -> Int
|
||||||
|
|
||||||
|
fun f6(x: <!WRONG_EXTENSION_FUNCTION_TYPE!>@ExtensionFunctionType<!> () -> Int) {
|
||||||
|
println(x <!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>+<!> 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun f7(x: <!WRONG_EXTENSION_FUNCTION_TYPE!>@ExtensionFunctionType<!> Function0<Int>)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ public interface Some {
|
|||||||
public abstract fun f2(): kotlin.String.() -> kotlin.Int
|
public abstract fun f2(): kotlin.String.() -> kotlin.Int
|
||||||
public abstract fun f3(): @ann() (kotlin.String.() -> kotlin.Int)
|
public abstract fun f3(): @ann() (kotlin.String.() -> kotlin.Int)
|
||||||
public abstract fun f4(): @ann() (kotlin.String.() -> kotlin.Int)
|
public abstract fun f4(): @ann() (kotlin.String.() -> kotlin.Int)
|
||||||
|
public abstract fun f5(): kotlin.Int.(???) -> kotlin.Int
|
||||||
|
public open fun f6(/*0*/ x: kotlin.Int.(???) -> kotlin.Int): kotlin.Unit
|
||||||
|
public abstract fun f7(/*0*/ x: kotlin.Int.(???) -> kotlin.Int): kotlin.Unit
|
||||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -373,6 +373,9 @@ internal class DescriptorRendererImpl(
|
|||||||
|
|
||||||
append("(")
|
append("(")
|
||||||
|
|
||||||
|
if (type.isBuiltinExtensionFunctionalType && type.arguments.size <= 1) {
|
||||||
|
append("???")
|
||||||
|
} else {
|
||||||
val parameterTypes = type.getValueParameterTypesFromFunctionType()
|
val parameterTypes = type.getValueParameterTypesFromFunctionType()
|
||||||
for ((index, typeProjection) in parameterTypes.withIndex()) {
|
for ((index, typeProjection) in parameterTypes.withIndex()) {
|
||||||
if (index > 0) append(", ")
|
if (index > 0) append(", ")
|
||||||
@@ -385,6 +388,7 @@ internal class DescriptorRendererImpl(
|
|||||||
|
|
||||||
append(renderTypeProjection(typeProjection))
|
append(renderTypeProjection(typeProjection))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
append(") ").append(arrow()).append(" ")
|
append(") ").append(arrow()).append(" ")
|
||||||
renderNormalizedType(type.getReturnTypeFromFunctionType())
|
renderNormalizedType(type.getReturnTypeFromFunctionType())
|
||||||
|
|||||||
Reference in New Issue
Block a user