From 117abb04aa6220b794960ed0188dacb78d068121 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 10 Aug 2018 18:10:18 +0700 Subject: [PATCH] Fix applicability of dsl-marker on function type When marker is applied to function type it should work like it's applied to receiver type #KT-23255 Fixed --- .../jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 3 + .../resolve/FunctionDescriptorResolver.kt | 11 ++-- .../kotlin/resolve/calls/DslMarkerUtils.kt | 32 ++++++++++- .../checkers/DslScopeViolationCallChecker.kt | 31 ++++++++-- .../dslMarker/annotatedFunctionType.kt | 56 ++++++++++++++++++ .../dslMarker/annotatedFunctionType.txt | 29 ++++++++++ .../dslMarker/annotatedFunctionType_1_4.kt | 57 +++++++++++++++++++ .../dslMarker/annotatedFunctionType_1_4.txt | 29 ++++++++++ .../checkers/DiagnosticsTestGenerated.java | 10 ++++ .../DiagnosticsUsingJavacTestGenerated.java | 10 ++++ .../kotlin/config/LanguageVersionSettings.kt | 1 + 12 files changed, 259 insertions(+), 11 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType.kt create mode 100644 compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType.txt create mode 100644 compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType_1_4.kt create mode 100644 compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType_1_4.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index babe0946a5d..7a17f5fc644 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -812,6 +812,7 @@ public interface Errors { DiagnosticFactory2 INAPPLICABLE_MODIFIER = DiagnosticFactory2.create(ERROR); DiagnosticFactory1 DSL_SCOPE_VIOLATION = DiagnosticFactory1.create(ERROR); + DiagnosticFactory1 DSL_SCOPE_VIOLATION_WARNING = DiagnosticFactory1.create(WARNING); // Labels 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 dc22b0dac37..58a5e11c9e1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -485,6 +485,9 @@ public class DefaultErrorMessages { MAP.put(DSL_SCOPE_VIOLATION, "''{0}'' can''t be called in this context by implicit receiver. " + "Use the explicit one if necessary", COMPACT); + MAP.put(DSL_SCOPE_VIOLATION_WARNING, "''{0}'' shouldn't be called in this context by implicit receiver, it will become an error soon. " + + "Use the explicit one if necessary", COMPACT); + MAP.put(RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY, "Returns are not allowed for functions with expression body. Use block body in '{...}'"); MAP.put(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY, "A 'return' expression required in a function with a block body ('{...}')"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt index 8b037e3a113..420a5981fec 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt @@ -17,10 +17,7 @@ package org.jetbrains.kotlin.resolve import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType -import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType -import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType +import org.jetbrains.kotlin.builtins.* import org.jetbrains.kotlin.config.AnalysisFlag import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings @@ -48,6 +45,8 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils.getDispatchReceiverParameter import org.jetbrains.kotlin.resolve.ModifiersChecker.resolveMemberModalityFromModifiers import org.jetbrains.kotlin.resolve.ModifiersChecker.resolveVisibilityFromModifiers import org.jetbrains.kotlin.resolve.bindingContextUtil.recordScope +import org.jetbrains.kotlin.resolve.calls.DslMarkerUtils +import org.jetbrains.kotlin.resolve.calls.checkers.DslScopeViolationCallChecker import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.calls.util.createValueParametersForInvokeInFunctionType import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil @@ -204,6 +203,10 @@ class FunctionDescriptorResolver( if (contractProvider != null) { put(ContractProviderKey, contractProvider) } + + if (receiverType != null && expectedFunctionType.functionTypeExpected() && !expectedFunctionType.annotations.isEmpty()) { + put(DslMarkerUtils.FunctionTypeAnnotationsKey, expectedFunctionType.annotations) + } } functionDescriptor.initialize( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DslMarkerUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DslMarkerUtils.kt index 0992bf7eb38..c5d49ef61e8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DslMarkerUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DslMarkerUtils.kt @@ -5,17 +5,42 @@ package org.jetbrains.kotlin.resolve.calls +import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers +import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.getAbbreviation import org.jetbrains.kotlin.utils.addToStdlib.safeAs object DslMarkerUtils { + + object FunctionTypeAnnotationsKey : FunctionDescriptor.UserDataKey + + data class DslMarkersFromReceiver( + val common: Set, + val fromContainingFunctionType: Set + ) + + fun extractDslMarkerFqNames(receiver: ReceiverValue): DslMarkersFromReceiver { + val errorLevel = extractDslMarkerFqNames(receiver.type) + + val deprecationLevel = + receiver.safeAs() + ?.declarationDescriptor + ?.safeAs() + ?.getUserData(FunctionTypeAnnotationsKey) + ?.let(Annotations::extractDslMarkerFqNames) + ?.toSet() ?: emptySet() + + return DslMarkersFromReceiver(errorLevel, deprecationLevel) + } + fun extractDslMarkerFqNames(kotlinType: KotlinType): Set { val result = mutableSetOf() @@ -34,12 +59,13 @@ object DslMarkerUtils { return result } - private fun Annotations.extractDslMarkerFqNames() = - filter(AnnotationDescriptor::isDslMarker).map { it.fqName!! } - val DSL_MARKER_FQ_NAME = FqName("kotlin.DslMarker") } + +private fun Annotations.extractDslMarkerFqNames() = + filter(AnnotationDescriptor::isDslMarker).map { it.fqName!! } + private fun AnnotationDescriptor.isDslMarker(): Boolean { val classDescriptor = annotationClass ?: return false return classDescriptor.annotations.hasAnnotation(DslMarkerUtils.DSL_MARKER_FQ_NAME) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/DslScopeViolationCallChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/DslScopeViolationCallChecker.kt index 2b80123de57..36570be692b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/DslScopeViolationCallChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/DslScopeViolationCallChecker.kt @@ -49,15 +49,38 @@ object DslScopeViolationCallChecker : CallChecker { if (receiversUntilOneFromTheCall.isEmpty()) return - val callDslMarkers = extractDslMarkerFqNames(callImplicitReceiver.type) - if (callDslMarkers.isEmpty()) return + val (callDslMarkers, additionalCallDslMarkers) = extractDslMarkerFqNames(callImplicitReceiver) + if (callDslMarkers.isEmpty() && additionalCallDslMarkers.isEmpty()) return + + val dslMarkersFromOuterReceivers = receiversUntilOneFromTheCall.map(::extractDslMarkerFqNames) val closestAnotherReceiverWithSameDslMarker = - receiversUntilOneFromTheCall.firstOrNull { receiver -> extractDslMarkerFqNames(receiver.type).any(callDslMarkers::contains) } + dslMarkersFromOuterReceivers.firstOrNull { (dslMarkersFromReceiver, _) -> + dslMarkersFromReceiver.any(callDslMarkers::contains) + } if (closestAnotherReceiverWithSameDslMarker != null) { // TODO: report receivers configuration (what's one is used and what's one is the closest) context.trace.report(Errors.DSL_SCOPE_VIOLATION.on(reportOn, resolvedCall.resultingDescriptor)) + return + } + + val allDslMarkersFromCall = callDslMarkers + additionalCallDslMarkers + + val closestAnotherReceiverWithSameDslMarkerWithDeprecation = + dslMarkersFromOuterReceivers.firstOrNull { (dslMarkersFromReceiver, additionalDslMarkersFromReceiver) -> + val allMarkersFromReceiver = dslMarkersFromReceiver + additionalDslMarkersFromReceiver + allDslMarkersFromCall.any(allMarkersFromReceiver::contains) + } + + if (closestAnotherReceiverWithSameDslMarkerWithDeprecation != null) { + val diagnostic = + if (context.languageVersionSettings.supportsFeature(LanguageFeature.DslMarkerOnFunctionTypeReceiver)) + Errors.DSL_SCOPE_VIOLATION + else + Errors.DSL_SCOPE_VIOLATION_WARNING + + context.trace.report(diagnostic.on(reportOn, resolvedCall.resultingDescriptor)) } } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType.kt b/compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType.kt new file mode 100644 index 00000000000..80677778fde --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType.kt @@ -0,0 +1,56 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +@Target(AnnotationTarget.TYPE) +@DslMarker +annotation class MyDsl + +interface A { + fun foo() +} + +interface B { + fun bar() +} + +fun baz1(x: (@MyDsl A).() -> Unit) {} +fun baz2(x: (@MyDsl B).() -> Unit) {} +fun baz3(x: @MyDsl A.() -> Unit) {} +fun baz4(x: @MyDsl B.() -> Unit) {} + +fun @MyDsl A.baz5() { + baz4 { + bar() + foo() + } +} + +fun main(args: Array) { + baz1 { + baz2 { + bar() + foo() + } + } + + baz3 { + baz2 { + bar() + foo() + } + } + + baz1 { + baz4 { + bar() + foo() + } + } + + baz3 { + baz4 { + bar() + foo() + } + } + +} diff --git a/compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType.txt b/compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType.txt new file mode 100644 index 00000000000..f2b741aba2f --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType.txt @@ -0,0 +1,29 @@ +package + +public fun baz1(/*0*/ x: (@MyDsl A).() -> kotlin.Unit): kotlin.Unit +public fun baz2(/*0*/ x: (@MyDsl B).() -> kotlin.Unit): kotlin.Unit +public fun baz3(/*0*/ x: @MyDsl() (A.() -> kotlin.Unit)): kotlin.Unit +public fun baz4(/*0*/ x: @MyDsl() (B.() -> kotlin.Unit)): kotlin.Unit +public fun main(/*0*/ args: kotlin.Array): kotlin.Unit +public fun @MyDsl A.baz5(): kotlin.Unit + +public interface A { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface B { + public abstract fun bar(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.TYPE}) @kotlin.DslMarker public final annotation class MyDsl : kotlin.Annotation { + public constructor MyDsl() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType_1_4.kt b/compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType_1_4.kt new file mode 100644 index 00000000000..3c9334838d3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType_1_4.kt @@ -0,0 +1,57 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +// !LANGUAGE: +DslMarkerOnFunctionTypeReceiver + +@Target(AnnotationTarget.TYPE) +@DslMarker +annotation class MyDsl + +interface A { + fun foo() +} + +interface B { + fun bar() +} + +fun baz1(x: (@MyDsl A).() -> Unit) {} +fun baz2(x: (@MyDsl B).() -> Unit) {} +fun baz3(x: @MyDsl A.() -> Unit) {} +fun baz4(x: @MyDsl B.() -> Unit) {} + +fun @MyDsl A.baz5() { + baz4 { + bar() + foo() + } +} + +fun main(args: Array) { + baz1 { + baz2 { + bar() + foo() + } + } + + baz3 { + baz2 { + bar() + foo() + } + } + + baz1 { + baz4 { + bar() + foo() + } + } + + baz3 { + baz4 { + bar() + foo() + } + } + +} diff --git a/compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType_1_4.txt b/compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType_1_4.txt new file mode 100644 index 00000000000..f2b741aba2f --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType_1_4.txt @@ -0,0 +1,29 @@ +package + +public fun baz1(/*0*/ x: (@MyDsl A).() -> kotlin.Unit): kotlin.Unit +public fun baz2(/*0*/ x: (@MyDsl B).() -> kotlin.Unit): kotlin.Unit +public fun baz3(/*0*/ x: @MyDsl() (A.() -> kotlin.Unit)): kotlin.Unit +public fun baz4(/*0*/ x: @MyDsl() (B.() -> kotlin.Unit)): kotlin.Unit +public fun main(/*0*/ args: kotlin.Array): kotlin.Unit +public fun @MyDsl A.baz5(): kotlin.Unit + +public interface A { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface B { + public abstract fun bar(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.TYPE}) @kotlin.DslMarker public final annotation class MyDsl : kotlin.Annotation { + public constructor MyDsl() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index bf990665121..676eab44eef 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -16890,6 +16890,16 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/resolve/dslMarker"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("annotatedFunctionType.kt") + public void testAnnotatedFunctionType() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType.kt"); + } + + @TestMetadata("annotatedFunctionType_1_4.kt") + public void testAnnotatedFunctionType_1_4() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType_1_4.kt"); + } + @TestMetadata("annotatedTypeArgument.kt") public void testAnnotatedTypeArgument() throws Exception { runTest("compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedTypeArgument.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 83412c16fbc..16ff775420b 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -16890,6 +16890,16 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/resolve/dslMarker"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("annotatedFunctionType.kt") + public void testAnnotatedFunctionType() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType.kt"); + } + + @TestMetadata("annotatedFunctionType_1_4.kt") + public void testAnnotatedFunctionType_1_4() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedFunctionType_1_4.kt"); + } + @TestMetadata("annotatedTypeArgument.kt") public void testAnnotatedTypeArgument() throws Exception { runTest("compiler/testData/diagnostics/tests/resolve/dslMarker/annotatedTypeArgument.kt"); diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index ef59fa33de9..d6e8406703b 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -86,6 +86,7 @@ enum class LanguageFeature( StrictJavaNullabilityAssertions(KOTLIN_1_3, kind = BUG_FIX), SoundSmartcastForEnumEntries(KOTLIN_1_3, kind = BUG_FIX), SoundSmartcastFromLoopConditionForLoopAssignedVariables(KOTLIN_1_3, kind = BUG_FIX), + DslMarkerOnFunctionTypeReceiver(KOTLIN_1_4, kind = BUG_FIX), RestrictReturnStatementTarget(KOTLIN_1_4, kind = BUG_FIX), ProperIeee754Comparisons(sinceVersion = null, defaultState = State.DISABLED, kind = BUG_FIX),