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
This commit is contained in:
@@ -812,6 +812,7 @@ public interface Errors {
|
||||
DiagnosticFactory2<PsiElement, KtModifierKeywordToken, String> INAPPLICABLE_MODIFIER = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
DiagnosticFactory1<PsiElement, CallableDescriptor> DSL_SCOPE_VIOLATION = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, CallableDescriptor> DSL_SCOPE_VIOLATION_WARNING = DiagnosticFactory1.create(WARNING);
|
||||
|
||||
// Labels
|
||||
|
||||
|
||||
+3
@@ -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 ('{...}')");
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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<Annotations>
|
||||
|
||||
data class DslMarkersFromReceiver(
|
||||
val common: Set<FqName>,
|
||||
val fromContainingFunctionType: Set<FqName>
|
||||
)
|
||||
|
||||
fun extractDslMarkerFqNames(receiver: ReceiverValue): DslMarkersFromReceiver {
|
||||
val errorLevel = extractDslMarkerFqNames(receiver.type)
|
||||
|
||||
val deprecationLevel =
|
||||
receiver.safeAs<ExtensionReceiver>()
|
||||
?.declarationDescriptor
|
||||
?.safeAs<FunctionDescriptor>()
|
||||
?.getUserData(FunctionTypeAnnotationsKey)
|
||||
?.let(Annotations::extractDslMarkerFqNames)
|
||||
?.toSet() ?: emptySet()
|
||||
|
||||
return DslMarkersFromReceiver(errorLevel, deprecationLevel)
|
||||
}
|
||||
|
||||
fun extractDslMarkerFqNames(kotlinType: KotlinType): Set<FqName> {
|
||||
val result = mutableSetOf<FqName>()
|
||||
|
||||
@@ -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)
|
||||
|
||||
+27
-4
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+56
@@ -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()
|
||||
<!DSL_SCOPE_VIOLATION_WARNING!>foo<!>()
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
baz1 {
|
||||
baz2 {
|
||||
bar()
|
||||
<!DSL_SCOPE_VIOLATION!>foo<!>()
|
||||
}
|
||||
}
|
||||
|
||||
baz3 {
|
||||
baz2 {
|
||||
bar()
|
||||
<!DSL_SCOPE_VIOLATION_WARNING!>foo<!>()
|
||||
}
|
||||
}
|
||||
|
||||
baz1 {
|
||||
baz4 {
|
||||
bar()
|
||||
<!DSL_SCOPE_VIOLATION_WARNING!>foo<!>()
|
||||
}
|
||||
}
|
||||
|
||||
baz3 {
|
||||
baz4 {
|
||||
bar()
|
||||
<!DSL_SCOPE_VIOLATION_WARNING!>foo<!>()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -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.String>): 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
|
||||
}
|
||||
+57
@@ -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()
|
||||
<!DSL_SCOPE_VIOLATION!>foo<!>()
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
baz1 {
|
||||
baz2 {
|
||||
bar()
|
||||
<!DSL_SCOPE_VIOLATION!>foo<!>()
|
||||
}
|
||||
}
|
||||
|
||||
baz3 {
|
||||
baz2 {
|
||||
bar()
|
||||
<!DSL_SCOPE_VIOLATION!>foo<!>()
|
||||
}
|
||||
}
|
||||
|
||||
baz1 {
|
||||
baz4 {
|
||||
bar()
|
||||
<!DSL_SCOPE_VIOLATION!>foo<!>()
|
||||
}
|
||||
}
|
||||
|
||||
baz3 {
|
||||
baz4 {
|
||||
bar()
|
||||
<!DSL_SCOPE_VIOLATION!>foo<!>()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -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.String>): 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
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
Generated
+10
@@ -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");
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user