From 95b589fd9455ca35d15c9e0c7d78f8b29ca3a9ea Mon Sep 17 00:00:00 2001 From: Nikolay Lunyak Date: Wed, 4 Jan 2023 12:20:10 +0200 Subject: [PATCH] [FIR JS] Support DEBUG_INFO_DYNAMIC --- .../j+k/deprecations/forMixedOverride.fir.kt | 2 +- .../allowedDynamicFunctionType.fir.kt | 8 +- .../dynamicTypes/block.fir.kt | 4 +- .../dynamicTypes/conventions.fir.kt | 91 ++++++------ .../dynamicTypes/extensionVals.fir.kt | 26 ++-- .../dynamicTypes/extensionVars.fir.kt | 24 ++-- .../dynamicTypes/extensions.fir.kt | 18 +-- .../dynamicTypes/extensionsToDynamic.fir.kt | 44 +++--- .../dynamicTypes/inference.fir.kt | 26 ---- .../dynamicTypes/inference.kt | 3 +- .../dynamicTypes/overloadingAmbiguity.fir.kt | 6 +- .../dynamicTypes/smartCast.fir.kt | 12 -- .../dynamicTypes/smartCast.kt | 1 + .../staticCallsInDynamicContext.fir.kt | 18 +-- .../dynamicTypes/staticExtensions.fir.kt | 6 +- .../dynamicTypes/substitution.fir.kt | 8 -- .../dynamicTypes/substitution.kt | 1 + .../dynamicTypes/varargs.fir.kt | 2 +- .../wrongMultipleInheritance.fir.kt | 25 ---- .../fir/handlers/FirDiagnosticCodeMetaInfo.kt | 7 +- .../fir/handlers/FirDiagnosticsHandler.kt | 129 +++++++++++++++++- .../kotlin/util/OperatorNameConventions.kt | 6 + 22 files changed, 263 insertions(+), 204 deletions(-) delete mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/inference.fir.kt delete mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/smartCast.fir.kt delete mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/substitution.fir.kt delete mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/wrongMultipleInheritance.fir.kt diff --git a/compiler/testData/diagnostics/tests/j+k/deprecations/forMixedOverride.fir.kt b/compiler/testData/diagnostics/tests/j+k/deprecations/forMixedOverride.fir.kt index 53bb1249c79..29cc1c21a5b 100644 --- a/compiler/testData/diagnostics/tests/j+k/deprecations/forMixedOverride.fir.kt +++ b/compiler/testData/diagnostics/tests/j+k/deprecations/forMixedOverride.fir.kt @@ -17,7 +17,7 @@ interface WithDeprecation { } class A : J(), WithDeprecation { - override fun foo() {} + override fun foo() {} } fun main() { diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/allowedDynamicFunctionType.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/allowedDynamicFunctionType.fir.kt index 5e098348d13..16086d31658 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/allowedDynamicFunctionType.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/allowedDynamicFunctionType.fir.kt @@ -3,8 +3,8 @@ fun withDynamicReceiver(d: dynamic.() -> Unit) {} fun test() = withDynamicReceiver { - foo - bar = 1 + foo + bar = 1 } fun test2() = withDynamicReceiver(fun dynamic.() {}) @@ -12,6 +12,6 @@ fun test2() = withDynamicReceiver(fun dynamic.() {}) val dynamicProperty: dynamic.() -> Unit = TODO() fun test(d: dynamic, dynamicParameter: dynamic.() -> Unit) { - d.dynamicProperty() - d.dynamicParameter() + d.dynamicProperty() + d.dynamicParameter() } diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/block.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/block.fir.kt index 8587d2f7080..d65a35ffc89 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/block.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/block.fir.kt @@ -2,8 +2,8 @@ fun test() { dynamic { - foo() - bar.baz(0) + foo() + bar.baz(0) } } diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/conventions.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/conventions.fir.kt index 31f72feee7f..9a7527cf1d7 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/conventions.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/conventions.fir.kt @@ -2,43 +2,43 @@ // !MARK_DYNAMIC_CALLS fun test(d: dynamic) { - +d - -d - ! d + +d + -d + ! d - d + d - d + 1 + d + d + d + 1 "" + d - d - d - d * d - d / d - d % d + d - d + d * d + d / d + d % d - d and d + d and d - d[1] + d[1] - d[1] = 2 + d[1] = 2 - d[1]++ - ++d[1] + d[1]++ + ++d[1] - d[1]-- - --d[1] + d[1]-- + --d[1] - d() - d(1) - d(name = 1) - d {} + d() + d(1) + d(name = 1) + d {} class C { val plus: dynamic = null } C() + 5 // todo should be marked as DEBUG_INFO_DYNAMIC - C().plus(5) + C().plus(5) d == d d != d @@ -46,37 +46,38 @@ fun test(d: dynamic) { d === d d !== d - d < d - d <= d - d >= d - d > d + d < d + d <= d + d >= d + d > d for (i in d) { - i.foo() + i.foo() } var dVar = d - dVar++ - ++dVar + dVar++ + ++dVar - dVar-- - --dVar + dVar-- + --dVar - dVar += 1 - dVar -= 1 - dVar *= 1 - dVar /= 1 - dVar %= 1 + dVar += 1 + dVar -= 1 + dVar *= 1 + dVar /= 1 + dVar %= 1 - d += 1 - d -= 1 - d *= 1 - d /= 1 - d %= 1 + d += 1 + d -= 1 + d *= 1 + d /= 1 + d %= 1 - d[1] += 1 - d[1] -= 1 - d[1] *= 1 - d[1] /= 1 - d[1] %= 1 + d[1] += 1 + d[1] -= 1 + d[1] *= 1 + d[1] /= 1 + d[1] %= 1 } + diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVals.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVals.fir.kt index fd099b12a3a..d466157ce4d 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVals.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVals.fir.kt @@ -2,24 +2,24 @@ // !MARK_DYNAMIC_CALLS fun test(d: dynamic) { - d.onAnyVal - d.onAnyVal = 1 + d.onAnyVal + d.onAnyVal = 1 - d?.onAnyVal - d?.onAnyVal = 1 + d?.onAnyVal + d?.onAnyVal = 1 run { - d!!.onAnyVal + d!!.onAnyVal } run { d!!.onAnyVal = 1 } - d.onNullableAnyVal = 1 + d.onNullableAnyVal = 1 - d.onStringVal = 1 + d.onStringVal = 1 - d.onDynamicVal = 1 + d.onDynamicVal = 1 (d as String).onStringVal (d as Any).onAnyVal @@ -28,7 +28,7 @@ fun test(d: dynamic) { } fun testReassignmentWithSafeCall(d: dynamic) { - d?.onDynamicVal = 1 + d?.onDynamicVal = 1 } fun testReassignmentWithStaticCalls(d: dynamic) { @@ -45,11 +45,11 @@ val dynamic.onDynamicVal: Int get() = 1 class C { fun test(d: dynamic) { - d.memberVal - d.memberVal = 1 + d.memberVal + d.memberVal = 1 - d.memberExtensionVal - d.memberExtensionVal = 1 + d.memberExtensionVal + d.memberExtensionVal = 1 } val memberVal = 1 diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVars.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVars.fir.kt index 342b19ac5f8..a791fc20aaf 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVars.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVars.fir.kt @@ -2,25 +2,25 @@ // !DIAGNOSTICS:-USELESS_CAST fun test(d: dynamic) { - d.onAnyVar - d.onAnyVar = 1 + d.onAnyVar + d.onAnyVar = 1 - d?.onAnyVar - d?.onAnyVar = 1 + d?.onAnyVar + d?.onAnyVar = 1 run { - d!!.onAnyVar + d!!.onAnyVar } run { d!!.onAnyVar = 1 } - d.onNullableAnyVar = 1 + d.onNullableAnyVar = 1 - d.onStringVar = 1 + d.onStringVar = 1 - d.onDynamicVar = 1 + d.onDynamicVar = 1 d?.onDynamicVar = 1 @@ -53,11 +53,11 @@ var dynamic.onDynamicVar: Int class C { fun test(d: dynamic) { - d.memberVar - d.memberExtensionVar + d.memberVar + d.memberExtensionVar - d.memberVar = 1 - d.memberExtensionVar = 1 + d.memberVar = 1 + d.memberExtensionVar = 1 } var memberVar = 1 diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensions.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensions.fir.kt index d79d3a233f7..1928b0ce01c 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensions.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensions.fir.kt @@ -2,18 +2,18 @@ // !MARK_DYNAMIC_CALLS fun test(d: dynamic) { - d.onAny() - d?.onAny() + d.onAny() + d?.onAny() run { - d!!.onAny() + d!!.onAny() } - d.onAny(1) + d.onAny(1) - d.onNullableAny() - d.onString() + d.onNullableAny() + d.onString() - d.onDynamic() + d.onDynamic() d?.onDynamic() (d as String).onString() @@ -29,8 +29,8 @@ fun dynamic.onDynamic() {} class C { fun test(d: dynamic) { - d.member() - d.memberExtension() + d.member() + d.memberExtension() } fun member() {} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionsToDynamic.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionsToDynamic.fir.kt index f6514f2e852..fa382fff4a5 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionsToDynamic.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionsToDynamic.fir.kt @@ -1,34 +1,34 @@ // !MARK_DYNAMIC_CALLS fun test(d: dynamic) { - d.onDynamic() - d.onNullableDynamic() + d.onDynamic() + d.onNullableDynamic() - d.valOnDynamic - d.valOnDynamic = 1 + d.valOnDynamic + d.valOnDynamic = 1 - d.varOnDynamic - d.varOnDynamic = 1 + d.varOnDynamic + d.varOnDynamic = 1 } fun dynamic.extTest() { - onDynamic() - onNullableDynamic() + onDynamic() + onNullableDynamic() - valOnDynamic - valOnDynamic = 1 + valOnDynamic + valOnDynamic = 1 - varOnDynamic - varOnDynamic = 1 + varOnDynamic + varOnDynamic = 1 - this.onDynamic() - this.onNullableDynamic() + this.onDynamic() + this.onNullableDynamic() - this.valOnDynamic - this.valOnDynamic = 1 + this.valOnDynamic + this.valOnDynamic = 1 - this.varOnDynamic - this.varOnDynamic = 1 + this.varOnDynamic + this.varOnDynamic = 1 } @@ -44,11 +44,11 @@ var dynamic.varOnDynamic: Int class ForMemberExtensions { fun test(d: dynamic) { - d.memberExtensionVar - d.memberExtensionVar = 1 + d.memberExtensionVar + d.memberExtensionVar = 1 - d.memberExtensionVal - d.memberExtensionVal = 1 + d.memberExtensionVal + d.memberExtensionVal = 1 } val dynamic.memberExtensionVal: Int get() = 1 diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/inference.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/inference.fir.kt deleted file mode 100644 index e951c3a91d4..00000000000 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/inference.fir.kt +++ /dev/null @@ -1,26 +0,0 @@ -// !MARK_DYNAMIC_CALLS -// !DIAGNOSTICS: -UNUSED_PARAMETER - -fun foo(t1: T, t2: T): T = t1 - -interface Tr -class C: Tr -fun foo1(t1: T, t2: T): T = t1 - -fun test(d: dynamic, b: Boolean, n: String?) { - foo(d, "").foo() - foo1(d, C()).foo() - - val fromIf = if (b) d else "" - fromIf.doo() - - val fromElvis = n ?: d - fromElvis.doo() -} - -class In(t: T) -fun contra(a: In, b: In): T = null!! - -fun testContra(d: dynamic) { - contra(In(d), In("")).get(0) // not a dynamic call -} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/inference.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/inference.kt index 195c6108764..f916ca69590 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/inference.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/inference.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !MARK_DYNAMIC_CALLS // !DIAGNOSTICS: -UNUSED_PARAMETER @@ -23,4 +24,4 @@ fun contra(a: In, b: In): T = null!! fun testContra(d: dynamic) { contra(In(d), In("")).get(0) // not a dynamic call -} +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/overloadingAmbiguity.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/overloadingAmbiguity.fir.kt index 121daac5ea4..b2cd32f75a9 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/overloadingAmbiguity.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/overloadingAmbiguity.fir.kt @@ -5,7 +5,7 @@ fun dynamic.foo(s: String, a: Any) {} fun dynamic.foo(s: Any, a: String) {} fun test(d: dynamic) { - d.foo(1, "") - d.foo("", "") - d.foo(1, 1) + d.foo(1, "") + d.foo("", "") + d.foo(1, 1) } diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/smartCast.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/smartCast.fir.kt deleted file mode 100644 index 26a6e979430..00000000000 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/smartCast.fir.kt +++ /dev/null @@ -1,12 +0,0 @@ -// !MARK_DYNAMIC_CALLS - -fun foo(d: dynamic) { - if (d is Foo) { - d.bar() // resolved statically - d.baz() - } -} - -class Foo { - fun bar() {} -} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/smartCast.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/smartCast.kt index bca56453688..b9f45e92456 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/smartCast.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/smartCast.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !MARK_DYNAMIC_CALLS fun foo(d: dynamic) { diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticCallsInDynamicContext.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticCallsInDynamicContext.fir.kt index dce0478c1df..2b322cc893c 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticCallsInDynamicContext.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticCallsInDynamicContext.fir.kt @@ -2,26 +2,26 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER fun dynamic.test() { - foo() - ext() + foo() + ext() bar() - this.bar() + this.bar() baz = 2 - this.baz = 2 + this.baz = 2 "".ext() - ext() + ext() "".extValFun() - extValFun() + extValFun() "".extVal - extVal + extVal baz.extExtVal() - extExtVal() + extExtVal() ""() this() @@ -29,7 +29,7 @@ fun dynamic.test() { C() + C() +C() - this + C() + this + C() 0.missing() } diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticExtensions.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticExtensions.fir.kt index 5c078be7525..606d461caf2 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticExtensions.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticExtensions.fir.kt @@ -6,7 +6,7 @@ fun Any?.staticExtension() = 1 val Any?.staticProperty get() = 2 fun test(d: dynamic, staticParameter: Any?.() -> Unit) { - d.staticExtension() - d.staticProperty - d.staticParameter + d.staticExtension() + d.staticProperty + d.staticParameter } diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/substitution.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/substitution.fir.kt deleted file mode 100644 index 4df336cca2c..00000000000 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/substitution.fir.kt +++ /dev/null @@ -1,8 +0,0 @@ -// !MARK_DYNAMIC_CALLS - -fun foo(d: dynamic) { - Foo(d).p.bar() - -} - -class Foo(val p: T) diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/substitution.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/substitution.kt index d42d6128ff8..caa1ac55f14 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/substitution.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/substitution.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !MARK_DYNAMIC_CALLS fun foo(d: dynamic) { diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/varargs.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/varargs.fir.kt index 4a9db6f50f2..30ac9395728 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/varargs.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/varargs.fir.kt @@ -8,6 +8,6 @@ fun test() { fun v(vararg d: dynamic) { for (dd in d) { - dd.foo() + dd.foo() } } diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/wrongMultipleInheritance.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/wrongMultipleInheritance.fir.kt deleted file mode 100644 index 4670c87b429..00000000000 --- a/compiler/testData/diagnostics/testsWithJsStdLib/wrongMultipleInheritance.fir.kt +++ /dev/null @@ -1,25 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER - -open class A { - fun get(index: Int): Char = '*' -} - -abstract class B : A(), CharSequence - -interface I { - fun nextChar(): Char -} - -abstract class C : CharIterator(), I { - override fun nextChar(): Char = '*' -} - -class CC(val s: CharSequence) : CharSequence by s, MyCharSequence {} - -interface MyCharSequence { - val length: Int - - operator fun get(index: Int): Char - - fun subSequence(startIndex: Int, endIndex: Int): CharSequence -} diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticCodeMetaInfo.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticCodeMetaInfo.kt index 2f10ddb1dc6..357938bd119 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticCodeMetaInfo.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticCodeMetaInfo.kt @@ -72,8 +72,13 @@ class FirDiagnosticCodeMetaRenderConfiguration( params.add("severity='${diagnostic.severity}'") params.add(getAdditionalParams(codeMetaInfo)) + val nonEmptyParams = params.filter { it.isNotEmpty() } - return "(\"${params.filter { it.isNotEmpty() }.joinToString("; ")}\")" + return if (nonEmptyParams.isNotEmpty()) { + "(\"${params.filter { it.isNotEmpty() }.joinToString("; ")}\")" + } else { + "" + } } fun getTag(codeMetaInfo: FirDiagnosticCodeMetaInfo): String { diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt index 172e1eedacd..33abf5b8ca0 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.test.frontend.fir.handlers import com.intellij.psi.PsiElement import org.jetbrains.kotlin.* +import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFactory0 import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFactory1 import org.jetbrains.kotlin.checkers.utils.TypeOfCall import org.jetbrains.kotlin.diagnostics.* @@ -15,21 +16,22 @@ import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.builder.FirSyntaxErrors import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirFunction import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.expressions.* -import org.jetbrains.kotlin.fir.references.FirNamedReference -import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference +import org.jetbrains.kotlin.fir.references.* +import org.jetbrains.kotlin.fir.renderForDebugInfo import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol -import org.jetbrains.kotlin.fir.types.ConeKotlinType -import org.jetbrains.kotlin.fir.types.coneTypeSafe +import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitorVoid import org.jetbrains.kotlin.name.FqNameUnsafe +import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtQualifiedExpression import org.jetbrains.kotlin.resolve.AnalyzingUtils import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives @@ -91,7 +93,7 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes } globalMetadataInfoHandler.addMetadataInfosForFile(file, diagnosticsMetadataInfos) collectSyntaxDiagnostics(file, firFile, lightTreeEnabled, lightTreeComparingModeEnabled) - collectDebugInfoDiagnostics(file, firFile, lightTreeEnabled, lightTreeComparingModeEnabled) + collectDebugInfoDiagnostics(module, file, firFile, lightTreeEnabled, lightTreeComparingModeEnabled) } } @@ -128,6 +130,7 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes } private fun collectDebugInfoDiagnostics( + module: TestModule, testFile: TestFile, firFile: FirFile, lightTreeEnabled: Boolean, @@ -140,13 +143,16 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes .mapValues { (_, value) -> value.toSet() } val consumer = DebugDiagnosticConsumer(result, diagnosedRangesToDiagnosticNames) + val shouldRenderDynamic = DiagnosticsDirectives.MARK_DYNAMIC_CALLS in module.directives object : FirDefaultVisitorVoid() { override fun visitElement(element: FirElement) { if (element is FirExpression) { consumer.reportExpressionTypeDiagnostic(element) } - + if (shouldRenderDynamic && element is FirResolvable) { + reportDynamic(element) + } if (element is FirSmartCastExpression) { element.originalExpression.acceptChildren(this) } else { @@ -154,6 +160,39 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes } } + private fun reportDynamic(element: FirResolvable) { + val calleeDeclaration = element.calleeReference.toResolvedCallableSymbol() ?: return + val isInvokeCallWithDynamicReceiver = calleeDeclaration.name == OperatorNameConventions.INVOKE + && element is FirQualifiedAccess + && element.dispatchReceiver.typeRef.isFunctionTypeWithDynamicReceiver(firFile.moduleData.session) + + if (calleeDeclaration.origin !is FirDeclarationOrigin.DynamicScope && !isInvokeCallWithDynamicReceiver) { + return + } + + val source = element.calleeReference.source + + // Unfortunately I had to repeat positioning strategy logic here + // (we need to check diagnostic range before applying it) + val target = when (calleeDeclaration.name) { + OperatorNameConventions.INVOKE -> when { + isInvokeCallWithDynamicReceiver -> source + else -> source?.parentAsSourceElement ?: source + } + in OperatorNameConventions.ALL_BINARY_OPERATION_NAMES, + in OperatorNameConventions.UNARY_OPERATION_NAMES, + in OperatorNameConventions.ASSIGNMENT_OPERATIONS, + OperatorNameConventions.GET, OperatorNameConventions.SET -> { + source?.operatorSignIfBinary ?: source + } + else -> { + source + } + } + + consumer.reportDynamicDiagnostic(target) + } + override fun visitFunctionCall(functionCall: FirFunctionCall) { val reference = functionCall.calleeReference consumer.reportCallDiagnostic(functionCall, reference) @@ -238,6 +277,10 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes } } + private fun DebugDiagnosticConsumer.reportDynamicDiagnostic(sourceElement: KtSourceElement?) { + report(DebugInfoDiagnosticFactory0.DYNAMIC, sourceElement) + } + private fun getTypeOfCall( reference: FirNamedReference, resolvedSymbol: FirBasedSymbol<*>? @@ -274,6 +317,37 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes override fun processAfterAllModules(someAssertionWasFailed: Boolean) {} } +private fun FirTypeRef.isFunctionTypeWithDynamicReceiver(session: FirSession) = + coneTypeSafe()?.isFunctionTypeWithDynamicReceiver(session) == true + +private fun ConeKotlinType.isFunctionTypeWithDynamicReceiver(session: FirSession): Boolean { + val hasExplicitDynamicReceiver = receiverType(session) is ConeDynamicType + val hasImplicitDynamicReceiver = isExtensionFunctionType && this.typeArguments.firstOrNull()?.type is ConeDynamicType + return hasExplicitDynamicReceiver || hasImplicitDynamicReceiver +} + +private val KtSourceElement.parentAsSourceElement: KtSourceElement? + get() = when (elementType) { + KtNodeTypes.REFERENCE_EXPRESSION -> when (this) { + is KtPsiSourceElement -> psi.parent.toKtPsiSourceElement(kind) + is KtLightSourceElement -> treeStructure.getParent(lighterASTNode)?.toKtLightSourceElement(treeStructure, kind) + else -> null + } + else -> null + } + +private val KtSourceElement.operatorSignIfBinary: KtSourceElement? + get() = when (elementType) { + KtNodeTypes.BINARY_EXPRESSION -> when (this) { + is KtPsiSourceElement -> (psi as? KtBinaryExpression)?.operationReference?.toKtPsiSourceElement(kind) + is KtLightSourceElement -> treeStructure.getParent(lighterASTNode) + ?.let { treeStructure.findChildByType(it, KtNodeTypes.OPERATION_REFERENCE) } + ?.toKtLightSourceElement(treeStructure, kind) + else -> null + } + else -> null + } + private class DebugDiagnosticConsumer( private val result: MutableList, private val diagnosedRangesToDiagnosticNames: Map> @@ -284,10 +358,51 @@ private class DebugDiagnosticConsumer( KtFakeSourceElementKind.DesugaredCompoundAssignment, KtFakeSourceElementKind.ReferenceInAtomicQualifiedAccess, KtFakeSourceElementKind.SmartCastExpression, - KtFakeSourceElementKind.DelegatingConstructorCall + KtFakeSourceElementKind.DelegatingConstructorCall, + KtFakeSourceElementKind.ArrayAccessNameReference, + KtFakeSourceElementKind.ArrayIndexExpressionReference, + KtFakeSourceElementKind.DesugaredPrefixNameReference, + KtFakeSourceElementKind.DesugaredPostfixNameReference, ) } + fun report(debugFactory: DebugInfoDiagnosticFactory0, sourceElement: KtSourceElement?) { + if (sourceElement == null || sourceElement.kind !in allowedKindsForDebugInfo) return + + // Lambda argument is always (?) duplicated by function literal + // Block expression is always (?) duplicated by single block expression + if (sourceElement.elementType == KtNodeTypes.LAMBDA_ARGUMENT || sourceElement.elementType == KtNodeTypes.BLOCK) return + + val availableDiagnostics = diagnosedRangesToDiagnosticNames[sourceElement.startOffset..sourceElement.endOffset] + if (availableDiagnostics == null || debugFactory.name !in availableDiagnostics) { + return + } + + val factory = KtDiagnosticFactory0( + name = debugFactory.name, + severity = debugFactory.severity, + defaultPositioningStrategy = AbstractSourceElementPositioningStrategy.DEFAULT, + psiType = PsiElement::class + ) + + val diagnostic = when (sourceElement) { + is KtPsiSourceElement -> KtPsiSimpleDiagnostic( + sourceElement, + debugFactory.severity, + factory, + factory.defaultPositioningStrategy + ) + is KtLightSourceElement -> KtLightSimpleDiagnostic( + sourceElement, + debugFactory.severity, + factory, + factory.defaultPositioningStrategy + ) + } + + result.add(diagnostic) + } + fun report(debugFactory: DebugInfoDiagnosticFactory1, element: FirElement, argumentFactory: () -> String) { val sourceElement = element.source?.takeIf { it.kind in allowedKindsForDebugInfo } ?: return diff --git a/core/compiler.common/src/org/jetbrains/kotlin/util/OperatorNameConventions.kt b/core/compiler.common/src/org/jetbrains/kotlin/util/OperatorNameConventions.kt index 23ae53884b9..73a41f88dc8 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/util/OperatorNameConventions.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/util/OperatorNameConventions.kt @@ -81,6 +81,12 @@ object OperatorNameConventions { @JvmField val BINARY_OPERATION_NAMES = setOf(TIMES, PLUS, MINUS, DIV, MOD, REM, RANGE_TO, RANGE_UNTIL) + @JvmField + val BITWISE_OPERATION_NAMES = setOf(AND, OR, XOR, INV, SHL, SHR, USHR) + + @JvmField + val ALL_BINARY_OPERATION_NAMES = BINARY_OPERATION_NAMES + BITWISE_OPERATION_NAMES + setOf(EQUALS, CONTAINS, COMPARE_TO) + @JvmField val ASSIGNMENT_OPERATIONS = setOf(TIMES_ASSIGN, DIV_ASSIGN, MOD_ASSIGN, REM_ASSIGN, PLUS_ASSIGN, MINUS_ASSIGN)