Navigation to inline functions doesn't work for thread dump captured using "Get thread dump" (KT-14916)
#KT-14916 Fixed
This commit is contained in:
@@ -22,6 +22,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
public class KotlinFileTypeFactory extends FileTypeFactory {
|
||||
public final static String[] KOTLIN_EXTENSIONS = new String[] { "kt", "kts" };
|
||||
|
||||
@Override
|
||||
public void createFileTypes(@NotNull FileTypeConsumer consumer) {
|
||||
consumer.consume(KotlinFileType.INSTANCE, "kt;kts");
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.io.FileUtilRt
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.idea.KotlinFileTypeFactory
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
||||
@@ -39,9 +40,6 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
|
||||
import java.util.*
|
||||
|
||||
object DebuggerUtils {
|
||||
|
||||
private val KOTLIN_EXTENSIONS = Sets.newHashSet("kt", "kts")
|
||||
|
||||
fun findSourceFileForClassIncludeLibrarySources(
|
||||
project: Project,
|
||||
scope: GlobalSearchScope,
|
||||
@@ -62,7 +60,7 @@ object DebuggerUtils {
|
||||
className: JvmClassName,
|
||||
fileName: String): KtFile? {
|
||||
val extension = FileUtilRt.getExtension(fileName)
|
||||
if (!KOTLIN_EXTENSIONS.contains(extension)) return null
|
||||
if (extension !in KotlinFileTypeFactory.KOTLIN_EXTENSIONS) return null
|
||||
if (DumbService.getInstance(project).isDumb) return null
|
||||
|
||||
val filesWithExactName = scopes.findFirstNotEmpty { findFilesByNameInPackage(className, fileName, project, it) } ?: return null
|
||||
|
||||
@@ -16,15 +16,17 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.filters
|
||||
|
||||
import com.intellij.execution.filters.ExceptionFilter
|
||||
import com.intellij.execution.filters.Filter
|
||||
import com.intellij.execution.filters.HyperlinkInfo
|
||||
import com.intellij.execution.filters.OpenFileHyperlinkInfo
|
||||
import com.intellij.execution.filters.*
|
||||
import com.intellij.execution.filters.impl.HyperlinkInfoFactoryImpl
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.io.FileUtilRt
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.search.FilenameIndex
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.idea.KotlinFileTypeFactory
|
||||
import org.jetbrains.kotlin.idea.debugger.*
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class KotlinExceptionFilter(private val searchScope: GlobalSearchScope) : Filter {
|
||||
@@ -36,31 +38,62 @@ class KotlinExceptionFilter(private val searchScope: GlobalSearchScope) : Filter
|
||||
}
|
||||
|
||||
private fun patchResult(result: Filter.Result, line: String): Filter.Result {
|
||||
val newHyperlinkInfo = createHyperlinkInfo(line) ?: return result
|
||||
val newHyperlinkInfo = createHyperlinkInfo(line, result) ?: return result
|
||||
|
||||
return Filter.Result(result.resultItems.map {
|
||||
Filter.ResultItem(it.getHighlightStartOffset(), it.getHighlightEndOffset(), newHyperlinkInfo, it.getHighlightAttributes())
|
||||
})
|
||||
}
|
||||
|
||||
private fun createHyperlinkInfo(line: String): HyperlinkInfo? {
|
||||
private fun createHyperlinkInfo(line: String, defaultResult: Filter.Result): HyperlinkInfo? {
|
||||
val project = searchScope.project ?: return null
|
||||
|
||||
val element = parseStackTraceLine(line) ?: return null
|
||||
val stackTraceElement = parseStackTraceLine(line) ?: return null
|
||||
|
||||
// All true classes should be handled correctly in the default ExceptionFilter. Special cases:
|
||||
// - static facades;
|
||||
// - package facades / package parts (generated by pre-M13 compiled);
|
||||
// - local classes (and closures) in top-level function and property declarations.
|
||||
val fileName = element.fileName
|
||||
// - bad line numbers for inline functions
|
||||
// - already applied smap for inline functions
|
||||
|
||||
val fileName = stackTraceElement.fileName
|
||||
|
||||
val extension = FileUtilRt.getExtension(fileName).toLowerCase()
|
||||
if (extension !in KotlinFileTypeFactory.KOTLIN_EXTENSIONS) return null
|
||||
|
||||
// fullyQualifiedName is of format "package.Class$Inner"
|
||||
val fullyQualifiedName = element.className
|
||||
val lineNumber = element.lineNumber - 1
|
||||
val fullyQualifiedName = stackTraceElement.className
|
||||
val lineNumber = stackTraceElement.lineNumber - 1
|
||||
|
||||
val internalName = fullyQualifiedName.replace('.', '/')
|
||||
val jvmClassName = JvmClassName.byInternalName(internalName)
|
||||
|
||||
val file = DebuggerUtils.findSourceFileForClassIncludeLibrarySources(project, searchScope, jvmClassName, fileName) ?: return null
|
||||
val file = DebuggerUtils.findSourceFileForClassIncludeLibrarySources(project, searchScope, jvmClassName, fileName)
|
||||
|
||||
if (file == null) {
|
||||
// File can't be found by class name and file name this can happen when smap info is already applied.
|
||||
// Default filter favours looking for file from class name and that can lead to wrong navigation to inline fun call file and
|
||||
// line from inline function definition.
|
||||
val defaultLinkFileNames = defaultResult.resultItems.mapNotNullTo(HashSet()) { (it as? FileHyperlinkInfo)?.descriptor?.file?.name }
|
||||
if (!defaultLinkFileNames.contains(fileName)) {
|
||||
val filesByName = FilenameIndex.getFilesByName(project, fileName, searchScope).mapNotNullTo(HashSet()) {
|
||||
if (!it.isValid) return@mapNotNullTo null
|
||||
it.virtualFile
|
||||
}
|
||||
|
||||
if (filesByName.isNotEmpty()) {
|
||||
if (filesByName.size > 1) {
|
||||
return HyperlinkInfoFactoryImpl.getInstance().createMultipleFilesHyperlinkInfo(filesByName.toList(), lineNumber, project)
|
||||
}
|
||||
else {
|
||||
return OpenFileHyperlinkInfo(project, filesByName.first(), lineNumber)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
val virtualFile = file.virtualFile ?: return null
|
||||
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package inlineFunSameFile
|
||||
|
||||
import inlineFunPackage.*
|
||||
|
||||
fun box() {
|
||||
foo {
|
||||
println()
|
||||
}
|
||||
}
|
||||
|
||||
// SMAP_APPLIED
|
||||
// FILE: inlineFunctionFile.kt
|
||||
// LINE: 4
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package inlineFunPackage
|
||||
|
||||
inline fun foo(f: () -> Unit) {
|
||||
null!!
|
||||
f()
|
||||
}
|
||||
@@ -42,7 +42,7 @@ import java.net.URLClassLoader
|
||||
private var MOCK_LIBRARY_JAR: File? = null
|
||||
private val MOCK_LIBRARY_SOURCES = PluginTestCaseBase.getTestDataPathBase() + "/debugger/mockLibraryForExceptionFilter"
|
||||
|
||||
abstract class AbstractKotlinExceptionFilterTest: KotlinCodeInsightTestCase() {
|
||||
abstract class AbstractKotlinExceptionFilterTest : KotlinCodeInsightTestCase() {
|
||||
override fun getTestDataPath() = ""
|
||||
|
||||
protected fun doTest(path: String) {
|
||||
@@ -101,7 +101,22 @@ abstract class AbstractKotlinExceptionFilterTest: KotlinCodeInsightTestCase() {
|
||||
|
||||
val filter = KotlinExceptionFilterFactory().create(GlobalSearchScope.allScope(project))
|
||||
val prefix = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// PREFIX: ") ?: "at"
|
||||
val result = filter.applyFilter("$prefix $stackTraceElement", 0) ?: throw AssertionError("Couldn't apply filter to $stackTraceElement")
|
||||
val stackTraceString = stackTraceElement.toString()
|
||||
var result = filter.applyFilter("$prefix $stackTraceString", 0) ?: throw AssertionError("Couldn't apply filter to $stackTraceElement")
|
||||
|
||||
if (InTextDirectivesUtils.isDirectiveDefined(fileText, "SMAP_APPLIED")) {
|
||||
val fileHyperlinkInfo = result.firstHyperlinkInfo as FileHyperlinkInfo
|
||||
val descriptor = fileHyperlinkInfo.descriptor!!
|
||||
|
||||
val file = descriptor.file
|
||||
val line = descriptor.line + 1
|
||||
|
||||
val newStackString = stackTraceString
|
||||
.replace(mainFile.name, file.name)
|
||||
.replace(Regex("\\:\\d+\\)"), ":$line)")
|
||||
|
||||
result = filter.applyFilter("$prefix $newStackString", 0) ?: throw AssertionError("Couldn't apply filter to $stackTraceElement")
|
||||
}
|
||||
|
||||
val info = result.firstHyperlinkInfo as FileHyperlinkInfo
|
||||
val descriptor = info.descriptor!!
|
||||
|
||||
@@ -66,6 +66,12 @@ public class KotlinExceptionFilterTestGenerated extends AbstractKotlinExceptionF
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inlineFunctionAnotherFileWithSmapApplied")
|
||||
public void testInlineFunctionAnotherFileWithSmapApplied() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/exceptionFilter/inlineFunctionAnotherFileWithSmapApplied/");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inlineFunctionInnerClass")
|
||||
public void testInlineFunctionInnerClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/exceptionFilter/inlineFunctionInnerClass/");
|
||||
|
||||
Reference in New Issue
Block a user