diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/FileRankingCalculator.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/FileRankingCalculator.kt index 1b371bd5d41..dd36e100045 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/FileRankingCalculator.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/FileRankingCalculator.kt @@ -40,30 +40,20 @@ object FileRankingCalculatorForIde : FileRankingCalculator() { override fun analyze(element: KtElement) = element.analyze(BodyResolveMode.PARTIAL) } -abstract class FileRankingCalculator( - private val checkClassFqName: Boolean = true, - private val strictMode: Boolean = false -) { +abstract class FileRankingCalculator(private val checkClassFqName: Boolean = true) { abstract fun analyze(element: KtElement): BindingContext fun findMostAppropriateSource(files: Collection, location: Location): KtFile { - assert(files.isNotEmpty()) - - val fileWithRankings = files.keysToMap { fileRankingSafe(it, location) } + val fileWithRankings: Map = rankFiles(files, location) val fileWithMaxScore = fileWithRankings.maxBy { it.value }!! - - if (strictMode) { - require(fileWithMaxScore.value.value >= 0) { "Max score is negative" } - - // Allow only one element with max ranking - require(fileWithRankings.count { it.value == fileWithMaxScore.value } == 1) { - "Score is the same for several files" - } - } - return fileWithMaxScore.key } + fun rankFiles(files: Collection, location: Location): Map { + assert(files.isNotEmpty()) + return files.keysToMap { fileRankingSafe(it, location).value } + } + private class Ranking(val value: Int) : Comparable { companion object { val LOW = Ranking(-1000) diff --git a/idea/testData/debugger/fileRanking/anonymousClasses.kt b/idea/testData/debugger/fileRanking/anonymousClasses.kt new file mode 100644 index 00000000000..85b7d33b84d --- /dev/null +++ b/idea/testData/debugger/fileRanking/anonymousClasses.kt @@ -0,0 +1,22 @@ +//FILE: a/a.kt +// DISABLE_STRICT_MODE +package a + +abstract class R { + abstract fun run() +} + +fun eval(r: R) { + r.run() +} + +class Some { + fun foo() { + eval(object : R() { // Line with negative score + override fun run() { + val a = 1 // R: 4 L: 17 + val b = 12 // R: 4 L: 18 + } + }) + } +} \ No newline at end of file diff --git a/idea/testData/debugger/fileRanking/lambdas.kt b/idea/testData/debugger/fileRanking/lambdas.kt index 167fa5e2c41..e49a637fe3a 100644 --- a/idea/testData/debugger/fileRanking/lambdas.kt +++ b/idea/testData/debugger/fileRanking/lambdas.kt @@ -1,11 +1,12 @@ // DO_NOT_CHECK_CLASS_FQNAME +// DISABLE_STRICT_MODE //FILE: a/a.kt package a fun block(l: () -> Unit) {} -class A { +class A { // Line with the same rank fun a() { block { val a = 5 @@ -18,12 +19,13 @@ class A { //FILE: b/a.kt package b +// Fake Line import a.block class A { fun b() { - val g = 5 + val g = 5 // Line with the same rank val x = 1 block { val y = 2 } block { diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractFileRankingTest.kt b/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractFileRankingTest.kt index 8bba90b85a1..ec1b3e4b614 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractFileRankingTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractFileRankingTest.kt @@ -8,6 +8,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.org.objectweb.asm.tree.ClassNode +import org.junit.Assert abstract class AbstractFileRankingTest : LowLevelDebuggerTestBase() { override fun doTest( @@ -23,7 +24,26 @@ abstract class AbstractFileRankingTest : LowLevelDebuggerTestBase() { val doNotCheckClassFqName = "DO_NOT_CHECK_CLASS_FQNAME" in options val strictMode = "DISABLE_STRICT_MODE" !in options - val calculator = object : FileRankingCalculator(checkClassFqName = !doNotCheckClassFqName, strictMode = strictMode) { + val expectedRanks: Map, Int> = allKtFiles.asSequence().flatMap { ktFile -> + ktFile.text.lines() + .asSequence() + .withIndex() + .map { + val matchResult = "^.*// (R: (-?\\d+)( L: (\\d+))?)\\s*$".toRegex().matchEntire(it.value) ?: return@map null + + val rank = matchResult.groupValues[2].toInt() + val line = matchResult.groupValues.getOrNull(4)?.takeIf { !it.isEmpty() }?.toInt() + + if (line != null && line != it.index + 1) { + throw IllegalArgumentException("Bad line in directive at ${ktFile.name}:${it.index + 1}\n${it.value}") + } + + (ktFile to it.index + 1) to rank + } + .filterNotNull() + }.toMap() + + val calculator = object : FileRankingCalculator(checkClassFqName = !doNotCheckClassFqName) { override fun analyze(element: KtElement) = state.bindingContext } @@ -60,7 +80,27 @@ abstract class AbstractFileRankingTest : LowLevelDebuggerTestBase() { for (location in locations) { if (location.method().isBridge || location.method().isSynthetic) continue - val actualFile = calculator.findMostAppropriateSource(allFilesWithSameName, location) + val fileWithRankings: Map = calculator.rankFiles(allFilesWithSameName, location) + + for ((ktFile, rank) in fileWithRankings) { + val expectedRank = expectedRanks[ktFile to (location.lineNumber())] + if (expectedRank != null) { + Assert.assertEquals("Invalid expected rank at $location", expectedRank, rank) + } + } + + val fileWithMaxScore = fileWithRankings.maxBy { it.value }!! + val actualFile = fileWithMaxScore.key + + if (strictMode) { + require(fileWithMaxScore.value >= 0) { "Max score is negative at $location" } + + // Allow only one element with max ranking + require(fileWithRankings.filter { it.value == fileWithMaxScore.value }.count() == 1) { + "Score is the same for several files at $location" + } + } + if (actualFile != expectedFile) { problems += "Location ${location.sourceName()}:${location.lineNumber() - 1} is associated with a wrong KtFile:\n" + " - expected: ${expectedFile.virtualFilePath}\n" + diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/FileRankingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/FileRankingTestGenerated.java index e2ce68775ad..f39ce376f8f 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/FileRankingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/FileRankingTestGenerated.java @@ -29,6 +29,11 @@ public class FileRankingTestGenerated extends AbstractFileRankingTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/fileRanking"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("anonymousClasses.kt") + public void testAnonymousClasses() throws Exception { + runTest("idea/testData/debugger/fileRanking/anonymousClasses.kt"); + } + @TestMetadata("differentFlags.kt") public void testDifferentFlags() throws Exception { runTest("idea/testData/debugger/fileRanking/differentFlags.kt");