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:
Denis Zharkov
2018-08-10 18:10:18 +07:00
parent 99fe6de52a
commit 117abb04aa
12 changed files with 259 additions and 11 deletions
@@ -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
@@ -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)
@@ -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))
}
}
}
}
@@ -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<!>()
}
}
}
@@ -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
}
@@ -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<!>()
}
}
}
@@ -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");
@@ -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),