FIR checker: honor DSL marker annotation on the function type

It seems the original function type gets lost during resolution. Hence
thie change added a custom attribute to recover the original function
type on an anonymous function
This commit is contained in:
Tianyu Geng
2021-09-15 21:36:32 -07:00
committed by TeamCityServer
parent 826ea122a9
commit c572bf05b5
11 changed files with 65 additions and 190 deletions
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.FirVisibilityChecker
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.matchingParameterFunctionType
import org.jetbrains.kotlin.fir.references.FirSuperReference
import org.jetbrains.kotlin.fir.resolve.directExpansionType
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
@@ -20,10 +21,7 @@ import org.jetbrains.kotlin.fir.scopes.FirUnstableSmartcastTypeScope
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.SyntheticSymbol
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visibilityChecker
@@ -234,6 +232,24 @@ object CheckDslScopeViolation : ResolutionStage() {
@OptIn(ExperimentalStdlibApi::class)
private fun ImplicitReceiverValue<*>.getDslMarkersOfImplicitReceiver(context: ResolutionContext): Set<ClassId> {
return buildSet {
(boundSymbol as? FirAnonymousFunctionSymbol)?.fir?.matchingParameterFunctionType?.let {
// collect annotations in the function type at declaration site. For example, the `@A` and `@B` in the following code.
// ```
// fun <T> body(block: @A ((@B T).() -> Unit)) { ... }
// ```
// Collect the annotation on the function type, or `@A` in the example above.
collectDslMarkerAnnotations(context, it.attributes.customAnnotations)
// Collect the annotation on the extension receiver, or `@B` in the example above.
if (CompilerConeAttributes.ExtensionFunctionType in it.attributes) {
it.typeArguments.firstOrNull()?.type?.let { receiverType ->
collectDslMarkerAnnotations(context, receiverType)
}
}
}
// Collect annotations on the actual receiver type.
collectDslMarkerAnnotations(context, type)
}
}
@@ -10,10 +10,7 @@ import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirResolvable
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.expectedType
@@ -236,11 +233,24 @@ class FirCallCompleter(
receiverType: ConeKotlinType?,
parameters: List<ConeKotlinType>,
expectedReturnType: ConeKotlinType?,
stubsForPostponedVariables: Map<TypeVariableMarker, StubTypeMarker>
stubsForPostponedVariables: Map<TypeVariableMarker, StubTypeMarker>,
candidate: Candidate
): ReturnArgumentsAnalysisResult {
val lambdaArgument: FirAnonymousFunction = lambdaAtom.atom
val needItParam = lambdaArgument.valueParameters.isEmpty() && parameters.size == 1
val matchedParameter = candidate.argumentMapping?.firstNotNullOfOrNull { (currentArgument, currentValueParameter) ->
val currentLambdaArgument =
((currentArgument as? FirLambdaArgumentExpression)?.expression as? FirAnonymousFunctionExpression)?.anonymousFunction
if (currentLambdaArgument === lambdaArgument) {
currentValueParameter
} else {
null
}
}
lambdaArgument.matchingParameterFunctionType = matchedParameter?.returnTypeRef?.coneType
val itParam = when {
needItParam -> {
val name = Name.identifier("it")
@@ -42,7 +42,8 @@ interface LambdaAnalyzer {
receiverType: ConeKotlinType?,
parameters: List<ConeKotlinType>,
expectedReturnType: ConeKotlinType?, // null means, that return type is not proper i.e. it depends on some type variables
stubsForPostponedVariables: Map<TypeVariableMarker, StubTypeMarker>
stubsForPostponedVariables: Map<TypeVariableMarker, StubTypeMarker>,
candidate: Candidate
): ReturnArgumentsAnalysisResult
}
@@ -139,7 +140,8 @@ class PostponedArgumentsAnalyzer(
receiver,
parameters,
expectedTypeForReturnArguments,
stubsForPostponedVariables
stubsForPostponedVariables,
candidate
)
applyResultsOfAnalyzedLambdaToCandidateSystem(
c,
@@ -103,4 +103,23 @@ var <D : FirCallableDeclaration>
D.originalForIntersectionOverrideAttr: D? by FirDeclarationDataRegistry.data(IntersectionOverrideOriginalKey)
private object InitialSignatureKey : FirDeclarationDataKey()
var FirCallableDeclaration.initialSignatureAttr: FirCallableDeclaration? by FirDeclarationDataRegistry.data(InitialSignatureKey)
private object MatchingParameterFunctionTypeKey : FirDeclarationDataKey()
/**
* Consider the following
* ```
* fun <T> run(block: @Foo T.() -> Unit) {...}
*
* fun test() {
* run<String> {
* <caret>
* }
* }
* ```
* The original function type `@Foo T.() -> Unit` can be accessed with this property on the FirAnonymousFunction at caret.
*/
var <D : FirAnonymousFunction>
D.matchingParameterFunctionType: ConeKotlinType? by FirDeclarationDataRegistry.data(MatchingParameterFunctionTypeKey)
@@ -21,7 +21,7 @@ fun baz4(x: @MyDsl B.() -> Unit) {}
fun @MyDsl A.baz5() {
baz4 {
bar()
foo()
<!DSL_SCOPE_VIOLATION!>foo<!>()
}
}
@@ -36,21 +36,21 @@ fun main() {
baz3 {
baz2 {
bar()
foo()
<!DSL_SCOPE_VIOLATION!>foo<!>()
}
}
baz1 {
baz4 {
bar()
foo()
<!DSL_SCOPE_VIOLATION!>foo<!>()
}
}
baz3 {
baz4 {
bar()
foo()
<!DSL_SCOPE_VIOLATION!>foo<!>()
}
}
@@ -1,57 +0,0 @@
// !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() {
baz1 {
baz2 {
bar()
<!DSL_SCOPE_VIOLATION!>foo<!>()
}
}
baz3 {
baz2 {
bar()
foo()
}
}
baz1 {
baz4 {
bar()
foo()
}
}
baz3 {
baz4 {
bar()
foo()
}
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
// !LANGUAGE: +DslMarkerOnFunctionTypeReceiver
@@ -1,92 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
@DslMarker
@Target(AnnotationTarget.TYPE)
annotation class L1
@DslMarker
@Target(AnnotationTarget.TYPE)
annotation class L2
class A {
fun a() = 1
}
class B {
fun b() = 2
}
fun foo1(x: (@L1 A).() -> Unit) {}
fun foo2(x: (@L2 A).() -> Unit) {}
fun foo12(x: (@L1 @L2 A).() -> Unit) {}
fun bar1(x: (@L1 B).() -> Unit) {}
fun bar2(x: (@L2 B).() -> Unit) {}
fun <T> bar1t(q: T, x: (@L1 T).() -> Unit) {}
fun test() {
foo12 {
a()
bar1 {
<!DSL_SCOPE_VIOLATION!>a<!>()
b()
}
bar2 {
<!DSL_SCOPE_VIOLATION!>a<!>()
b()
}
}
bar1 {
b()
foo12 {
a()
<!DSL_SCOPE_VIOLATION!>b<!>()
}
}
bar2 {
b()
foo12 {
a()
<!DSL_SCOPE_VIOLATION!>b<!>()
}
}
foo2 {
bar1t(this) {
a()
bar1 {
a()
b()
}
bar2 {
<!DSL_SCOPE_VIOLATION!>a<!>()
b()
}
}
}
bar1 {
b()
foo2 {
bar1t(this) {
a()
b()
}
}
}
bar2 {
b()
foo2 {
bar1t(this) {
a()
<!DSL_SCOPE_VIOLATION!>b<!>()
}
}
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
@DslMarker
@Target(AnnotationTarget.TYPE)
@@ -1,26 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
@DslMarker
@Target(AnnotationTarget.TYPE)
annotation class Ann
class A {
fun a() = 1
}
class B {
fun b() = 2
}
fun <T> foo(x: (@Ann T).() -> Unit) {}
fun <E> bar(x: (@Ann E).() -> Unit) {}
fun test() {
foo<A> {
a()
bar<B> {
a()
this@foo.a()
b()
}
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
@DslMarker
@Target(AnnotationTarget.TYPE)