From d0fa3eb1d31ee13eb44ddcb24aadf6a98124df56 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Tue, 11 Jan 2022 16:42:35 +0300 Subject: [PATCH] Forbid ExtensionFunctionType on functional types without parameters #KT-43527 Fixed --- .../diagnostics/KtFirDataClassConverters.kt | 6 +++++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 4 ++++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 5 +++++ .../diagnostics/FirDiagnosticsList.kt | 1 + .../fir/analysis/diagnostics/FirErrors.kt | 1 + .../checkers/type/FirTypeAnnotationChecker.kt | 8 +++++++ .../diagnostics/FirErrorsDefaultMessages.kt | 2 ++ .../jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 1 + .../kotlin/resolve/AnnotationChecker.kt | 15 ++++++++++--- .../annotations/extensionFunctionType.fir.kt | 20 +++++++++++++++++ .../annotations/extensionFunctionType.kt | 10 ++++++++- .../annotations/extensionFunctionType.txt | 3 +++ .../kotlin/renderer/DescriptorRendererImpl.kt | 22 +++++++++++-------- 14 files changed, 86 insertions(+), 13 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/annotations/extensionFunctionType.fir.kt diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index 1a80ae100ea..adbbf059864 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -945,6 +945,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.WRONG_EXTENSION_FUNCTION_TYPE) { firDiagnostic -> + WrongExtensionFunctionTypeImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirErrors.OPT_IN_USAGE) { firDiagnostic -> OptInUsageImpl( firDiagnostic.a, diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index b15d8f68be3..ae9a3fe5295 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -680,6 +680,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = NotAClass::class } + abstract class WrongExtensionFunctionType : KtFirDiagnostic() { + override val diagnosticClass get() = WrongExtensionFunctionType::class + } + abstract class OptInUsage : KtFirDiagnostic() { override val diagnosticClass get() = OptInUsage::class abstract val optInMarkerFqName: FqName diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 7454b9a63f6..f9f84e6b27c 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -816,6 +816,11 @@ internal class NotAClassImpl( override val token: ValidityToken, ) : KtFirDiagnostic.NotAClass(), KtAbstractFirDiagnostic +internal class WrongExtensionFunctionTypeImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.WrongExtensionFunctionType(), KtAbstractFirDiagnostic + internal class OptInUsageImpl( override val optInMarkerFqName: FqName, override val message: String, diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 452b2987db4..2ee92bb19b7 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -287,6 +287,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val REPEATED_ANNOTATION by error() val REPEATED_ANNOTATION_WARNING by warning() val NOT_A_CLASS by error() + val WRONG_EXTENSION_FUNCTION_TYPE by error() } val OPT_IN by object : DiagnosticGroup("OptIn") { diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index cd4abf8e713..8f0c35f874c 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -253,6 +253,7 @@ object FirErrors { val REPEATED_ANNOTATION by error0() val REPEATED_ANNOTATION_WARNING by warning0() val NOT_A_CLASS by error0() + val WRONG_EXTENSION_FUNCTION_TYPE by error0() // OptIn val OPT_IN_USAGE by warning2(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirTypeAnnotationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirTypeAnnotationChecker.kt index a0d3e3da802..c0b64a08c9f 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirTypeAnnotationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirTypeAnnotationChecker.kt @@ -12,8 +12,11 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.diagnostics.reportOn 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.FirTypeRef +import org.jetbrains.kotlin.fir.types.isBuiltinFunctionalType +import org.jetbrains.kotlin.name.StandardClassIds object FirTypeAnnotationChecker : FirTypeRefChecker() { 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) + } + } } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt index 71ea043224f..4f48eba6f37 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt @@ -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.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_EXTENSION_FUNCTION_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_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" ) 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 // # map.put( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index cf5e07a8679..363f99f51d2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -278,6 +278,7 @@ public interface Errors { DiagnosticFactory0 REPEATED_ANNOTATION = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 REPEATED_ANNOTATION_WARNING = DiagnosticFactory0.create(WARNING); DiagnosticFactory0 NON_SOURCE_ANNOTATION_ON_INLINED_LAMBDA_EXPRESSION = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 WRONG_EXTENSION_FUNCTION_TYPE = DiagnosticFactory0.create(ERROR); // Annotations diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 9d0b47e236d..0f5d2cc8df3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -146,6 +146,7 @@ public class DefaultErrorMessages { MAP.put(REPEATED_ANNOTATION, "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(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_PROPERTY_IMMUTABLE, "''@{0}:'' annotations could be applied only to mutable properties", TO_STRING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt index 58e111b244f..346a173ec87 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.resolve import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.builtins.isFunctionOrKFunctionTypeWithAnySuspendability import org.jetbrains.kotlin.config.LanguageFeature.* import org.jetbrains.kotlin.config.LanguageVersionSettings 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.name.FqName import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getAnnotationEntries import org.jetbrains.kotlin.resolve.constants.ArrayValue import org.jetbrains.kotlin.resolve.constants.EnumValue 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.isAnnotatedWithKotlinRepeatable import org.jetbrains.kotlin.resolve.inline.InlineUtil @@ -163,9 +166,9 @@ class AnnotationChecker( checkWithoutLanguageFeature: Boolean = false ) { val shouldRunCheck = isSuperType || shouldCheckReferenceItself - if (shouldRunCheck) { - for (entry in reference.annotationEntries) { - val descriptor = trace.get(BindingContext.ANNOTATION, entry) + for (entry in reference.annotationEntries) { + val descriptor = trace.get(BindingContext.ANNOTATION, entry) + if (shouldRunCheck) { if (descriptor is LazyAnnotationDescriptor) { /* * There are no users of type annotations until backend, so if there are errors @@ -181,6 +184,12 @@ class AnnotationChecker( 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 for (typeArgument in typeArguments) { diff --git a/compiler/testData/diagnostics/tests/annotations/extensionFunctionType.fir.kt b/compiler/testData/diagnostics/tests/annotations/extensionFunctionType.fir.kt new file mode 100644 index 00000000000..033af7e4477 --- /dev/null +++ b/compiler/testData/diagnostics/tests/annotations/extensionFunctionType.fir.kt @@ -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(): @ExtensionFunctionType () -> Int + + fun f6(x: @ExtensionFunctionType () -> Int) { + println(x + 2) + } + + fun f7(x: @ExtensionFunctionType Function0) +} diff --git a/compiler/testData/diagnostics/tests/annotations/extensionFunctionType.kt b/compiler/testData/diagnostics/tests/annotations/extensionFunctionType.kt index 1b29466d9e8..ca26db3e682 100644 --- a/compiler/testData/diagnostics/tests/annotations/extensionFunctionType.kt +++ b/compiler/testData/diagnostics/tests/annotations/extensionFunctionType.kt @@ -1,4 +1,4 @@ -// FIR_IDENTICAL +// WITH_STDLIB // This test checks that annotations on extension function types are preserved. See the corresponding .txt file @Target(AnnotationTarget.TYPE) @@ -9,4 +9,12 @@ interface Some { fun f2(): @ExtensionFunctionType() (String.() -> Int) fun f3(): @ann String.() -> Int fun f4(): @ExtensionFunctionType @ann() (String.() -> Int) + + fun f5(): @ExtensionFunctionType () -> Int + + fun f6(x: @ExtensionFunctionType () -> Int) { + println(x + 2) + } + + fun f7(x: @ExtensionFunctionType Function0) } diff --git a/compiler/testData/diagnostics/tests/annotations/extensionFunctionType.txt b/compiler/testData/diagnostics/tests/annotations/extensionFunctionType.txt index bfada23df89..f80fd2e937b 100644 --- a/compiler/testData/diagnostics/tests/annotations/extensionFunctionType.txt +++ b/compiler/testData/diagnostics/tests/annotations/extensionFunctionType.txt @@ -6,6 +6,9 @@ public interface Some { public abstract fun f2(): 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 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 toString(): kotlin.String } diff --git a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt index ee0e0963fdf..0b67e2d9f0b 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt @@ -373,17 +373,21 @@ internal class DescriptorRendererImpl( append("(") - val parameterTypes = type.getValueParameterTypesFromFunctionType() - for ((index, typeProjection) in parameterTypes.withIndex()) { - if (index > 0) append(", ") + if (type.isBuiltinExtensionFunctionalType && type.arguments.size <= 1) { + append("???") + } else { + val parameterTypes = type.getValueParameterTypesFromFunctionType() + for ((index, typeProjection) in parameterTypes.withIndex()) { + if (index > 0) append(", ") - val name = if (parameterNamesInFunctionalTypes) typeProjection.type.extractParameterNameFromFunctionTypeArgument() else null - if (name != null) { - append(renderName(name, false)) - append(": ") + val name = if (parameterNamesInFunctionalTypes) typeProjection.type.extractParameterNameFromFunctionTypeArgument() else null + if (name != null) { + append(renderName(name, false)) + append(": ") + } + + append(renderTypeProjection(typeProjection)) } - - append(renderTypeProjection(typeProjection)) } append(") ").append(arrow()).append(" ")