202: Fix compilation

This commit is contained in:
Nikita Bobko
2020-06-09 13:20:35 +03:00
committed by Nikolay Krasko
parent eb67c4519d
commit ff7576f8e4
19 changed files with 2193 additions and 35 deletions
@@ -0,0 +1,185 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.codeInsight
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzerSettings
import com.intellij.codeInsight.daemon.LineMarkerInfo
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl
import com.intellij.openapi.editor.Document
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.FileUtil
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiFile
import com.intellij.rt.execution.junit.FileComparisonFailure
import com.intellij.testFramework.ExpectedHighlightingData
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.PlatformTestUtil
import com.intellij.testFramework.UsefulTestCase
import junit.framework.TestCase
import org.jetbrains.kotlin.idea.highlighter.markers.TestableLineMarkerNavigator
import org.jetbrains.kotlin.idea.navigation.NavigationTestUtils
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TagsTestDataUtil
import org.jetbrains.kotlin.test.util.renderAsGotoImplementation
import org.junit.Assert
import java.io.File
abstract class AbstractLineMarkersTest : KotlinLightCodeInsightFixtureTestCase() {
override fun getProjectDescriptor(): LightProjectDescriptor {
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
}
fun doTest(path: String) = doTest(path) {}
protected fun doAndCheckHighlighting(
psiFile: PsiFile,
documentToAnalyze: Document,
expectedHighlighting: ExpectedHighlightingData,
expectedFile: File
): List<LineMarkerInfo<*>> {
myFixture.doHighlighting()
return checkHighlighting(psiFile, documentToAnalyze, expectedHighlighting, expectedFile)
}
fun doTest(path: String, additionalCheck: () -> Unit) {
val fileText = FileUtil.loadFile(testDataFile())
try {
ConfigLibraryUtil.configureLibrariesByDirective(myFixture.module, PlatformTestUtil.getCommunityPath(), fileText)
if (InTextDirectivesUtils.findStringWithPrefixes(fileText, "METHOD_SEPARATORS") != null) {
DaemonCodeAnalyzerSettings.getInstance().SHOW_METHOD_SEPARATORS = true
}
myFixture.configureByFile(fileName())
val project = myFixture.project
val document = myFixture.editor.document
val data = ExpectedHighlightingData(document, false, false, false)
data.init()
PsiDocumentManager.getInstance(project).commitAllDocuments()
val markers = doAndCheckHighlighting(myFixture.file, document, data, testDataFile())
assertNavigationElements(myFixture.project, myFixture.file as KtFile, markers)
additionalCheck()
} catch (exc: Exception) {
throw RuntimeException(exc)
} finally {
ConfigLibraryUtil.unconfigureLibrariesByDirective(module, fileText)
DaemonCodeAnalyzerSettings.getInstance().SHOW_METHOD_SEPARATORS = false
}
}
companion object {
@Suppress("SpellCheckingInspection")
private const val LINE_MARKER_PREFIX = "LINEMARKER:"
private const val TARGETS_PREFIX = "TARGETS"
fun assertNavigationElements(project: Project, file: KtFile, markers: List<LineMarkerInfo<*>>) {
val navigationDataComments = KotlinTestUtils.getLastCommentsInFile(
file, KotlinTestUtils.CommentType.BLOCK_COMMENT, false
)
if (navigationDataComments.isEmpty()) return
for ((navigationCommentIndex, navigationComment) in navigationDataComments.reversed().withIndex()) {
val description = getLineMarkerDescription(navigationComment)
val navigateMarkers = markers.filter { it.lineMarkerTooltip?.startsWith(description) == true }
val navigateMarker = navigateMarkers.singleOrNull() ?: navigateMarkers.getOrNull(navigationCommentIndex)
TestCase.assertNotNull(
String.format("Can't find marker for navigation check with description \"%s\"", description),
navigateMarker
)
val handler = navigateMarker!!.navigationHandler
if (handler is TestableLineMarkerNavigator) {
val navigateElements = handler.getTargetsPopupDescriptor(navigateMarker.element)?.targets?.sortedBy {
it.renderAsGotoImplementation()
}
val actualNavigationData = NavigationTestUtils.getNavigateElementsText(project, navigateElements)
UsefulTestCase.assertSameLines(getExpectedNavigationText(navigationComment), actualNavigationData)
} else {
Assert.fail("Only TestableLineMarkerNavigator are supported in navigate check")
}
}
}
private fun getLineMarkerDescription(navigationComment: String): String {
val firstLineEnd = navigationComment.indexOf("\n")
TestCase.assertTrue(
"The first line in block comment must contain description of marker for navigation check", firstLineEnd != -1
)
var navigationMarkerText = navigationComment.substring(0, firstLineEnd)
TestCase.assertTrue(
String.format("Add %s directive in first line of comment", LINE_MARKER_PREFIX),
navigationMarkerText.startsWith(LINE_MARKER_PREFIX)
)
navigationMarkerText = navigationMarkerText.substring(LINE_MARKER_PREFIX.length)
return navigationMarkerText.trim { it <= ' ' }
}
private fun getExpectedNavigationText(navigationComment: String): String {
val firstLineEnd = navigationComment.indexOf("\n")
var expectedNavigationText = navigationComment.substring(firstLineEnd + 1)
TestCase.assertTrue(
String.format("Marker %s is expected before navigation data", TARGETS_PREFIX),
expectedNavigationText.startsWith(TARGETS_PREFIX)
)
expectedNavigationText = expectedNavigationText.substring(expectedNavigationText.indexOf("\n") + 1)
return expectedNavigationText
}
fun checkHighlighting(
psiFile: PsiFile,
documentToAnalyze: Document,
expectedHighlighting: ExpectedHighlightingData,
expectedFile: File
): MutableList<LineMarkerInfo<*>> {
val markers = DaemonCodeAnalyzerImpl.getLineMarkers(documentToAnalyze, psiFile.project)
try {
expectedHighlighting.checkLineMarkers(psiFile, markers, documentToAnalyze.text)
// This is a workaround for sad bug in ExpectedHighlightingData:
// the latter doesn't throw assertion error when some line markers are expected, but none are present.
if (FileUtil.loadFile(expectedFile).contains("<lineMarker") && markers.isEmpty()) {
throw AssertionError("Some line markers are expected, but nothing is present at all")
}
} catch (error: AssertionError) {
try {
val actualTextWithTestData = TagsTestDataUtil.insertInfoTags(markers, true, documentToAnalyze.text)
KotlinTestUtils.assertEqualsToFile(expectedFile, actualTextWithTestData)
} catch (failure: FileComparisonFailure) {
throw FileComparisonFailure(
error.message + "\n" + failure.message,
failure.expected,
failure.actual,
failure.filePath
)
}
}
return markers
}
}
}
@@ -0,0 +1,91 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.codeInsight
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiDocumentManager
import com.intellij.testFramework.ExpectedHighlightingData
import com.intellij.util.io.createFile
import com.intellij.util.io.write
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.idea.test.SdkAndMockLibraryProjectDescriptor
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import java.io.File
import java.nio.file.Files
abstract class AbstractLineMarkersTestInLibrarySources : AbstractLineMarkersTest() {
private var libraryCleanPath: String? = null
private var libraryClean: File? = null
private fun getLibraryCleanPath(): String = libraryCleanPath!!
private fun getLibraryOriginalPath(): String = PluginTestCaseBase.getTestDataPathBase() + "/codeInsightInLibrary/_library"
override fun getProjectDescriptor(): SdkAndMockLibraryProjectDescriptor {
if (libraryCleanPath == null) {
val libraryClean = Files.createTempDirectory("lineMarkers_library")
val libraryOriginal = File(getLibraryOriginalPath())
libraryCleanPath = libraryClean.toString()
for (file in libraryOriginal.walkTopDown().filter { !it.isDirectory }) {
val text = file.readText().replace("</?lineMarker.*?>".toRegex(), "")
val cleanFile = libraryClean.resolve(file.relativeTo(libraryOriginal).path)
cleanFile.createFile()
cleanFile.write(text)
}
this.libraryClean = File(libraryCleanPath)
}
return object : SdkAndMockLibraryProjectDescriptor(getLibraryCleanPath(), false) {
override fun configureModule(module: Module, model: ModifiableRootModel) {
super.configureModule(module, model)
val library = model.moduleLibraryTable.getLibraryByName(LIBRARY_NAME)!!
val modifiableModel = library.modifiableModel
modifiableModel.addRoot(LocalFileSystem.getInstance().findFileByIoFile(libraryClean!!)!!, OrderRootType.SOURCES)
modifiableModel.commit()
}
}
}
fun doTestWithLibrary(path: String) {
doTest(path) {
val fileSystem = VirtualFileManager.getInstance().getFileSystem("file")
val libraryOriginal = File(getLibraryOriginalPath())
val project = myFixture.project
for (file in libraryOriginal.walkTopDown().filter { !it.isDirectory }) {
myFixture.openFileInEditor(fileSystem.findFileByPath(file.absolutePath)!!)
val data = ExpectedHighlightingData(myFixture.editor.document, false, false, false)
data.init()
val librarySourceFile = libraryClean!!.resolve(file.relativeTo(libraryOriginal).path)
myFixture.openFileInEditor(fileSystem.findFileByPath(librarySourceFile.absolutePath)!!)
val document = myFixture.editor.document
PsiDocumentManager.getInstance(project).commitAllDocuments()
if (!ProjectRootsUtil.isLibrarySourceFile(project, myFixture.file.virtualFile)) {
throw AssertionError("File ${myFixture.file.virtualFile.path} should be in library sources!")
}
doAndCheckHighlighting(myFixture.file, document, data, file)
}
}
}
override fun tearDown() {
libraryClean?.deleteRecursively()
ConfigLibraryUtil.removeLibrary(module, SdkAndMockLibraryProjectDescriptor.LIBRARY_NAME)
super.tearDown()
}
}
@@ -0,0 +1,64 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.highlighter
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.codeInsight.daemon.impl.HighlightInfoType
import com.intellij.codeInsight.highlighting.HighlightUsagesHandler
import com.intellij.openapi.editor.colors.EditorColors
import com.intellij.openapi.editor.colors.EditorColorsManager
import com.intellij.openapi.editor.markup.RangeHighlighter
import com.intellij.openapi.editor.markup.TextAttributes
import com.intellij.testFramework.ExpectedHighlightingData
import org.jetbrains.kotlin.idea.test.*
abstract class AbstractUsageHighlightingTest : KotlinLightCodeInsightFixtureTestCase() {
companion object {
// Not standard <caret> to leave it in text after configureByFile and remove manually after collecting highlighting information
const val CARET_TAG = "~"
}
protected fun doTest(unused: String) {
myFixture.configureByFile(fileName())
val document = myFixture.editor.document
val data = ExpectedHighlightingData(document, false, false, true, false)
data.init()
val caret = document.extractMarkerOffset(project, CARET_TAG)
assert(caret != -1) { "Caret marker '$CARET_TAG' expected" }
editor.caretModel.moveToOffset(caret)
HighlightUsagesHandler.invoke(project, editor, myFixture.file)
val highlighters = myFixture.editor.markupModel.allHighlighters
val infos = highlighters
.filter { isUsageHighlighting(it) }
.map { highlighter ->
var startOffset = highlighter.startOffset
var endOffset = highlighter.endOffset
if (startOffset > caret) startOffset += CARET_TAG.length
if (endOffset > caret) endOffset += CARET_TAG.length
HighlightInfo.newHighlightInfo(HighlightInfoType.INFORMATION)
.range(startOffset, endOffset)
.create()
}
data.checkResult(myFixture.file, infos, StringBuilder(document.text).insert(caret, CARET_TAG).toString())
}
private fun isUsageHighlighting(info: RangeHighlighter): Boolean {
val globalScheme = EditorColorsManager.getInstance().globalScheme
val readAttributes: TextAttributes =
globalScheme.getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES)
val writeAttributes: TextAttributes =
globalScheme.getAttributes(EditorColors.WRITE_SEARCH_RESULT_ATTRIBUTES)
return info.textAttributes == readAttributes || info.textAttributes == writeAttributes
}
}
@@ -0,0 +1,149 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.InspectionEP
import com.intellij.codeInspection.InspectionProfileEntry
import com.intellij.codeInspection.LocalInspectionEP
import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.codeInspection.ex.InspectionToolRegistrar
import com.intellij.codeInspection.ex.InspectionToolWrapper
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.util.text.StringUtil
import com.intellij.testFramework.LightPlatformTestCase
import com.intellij.testFramework.UsefulTestCase
import gnu.trove.THashMap
import org.jetbrains.kotlin.idea.KotlinPluginUtil
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
import org.junit.runner.RunWith
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
class InspectionDescriptionTest : LightPlatformTestCase() {
fun testDescriptionsAndShortNames() {
val shortNames = THashMap<String, InspectionToolWrapper<InspectionProfileEntry, InspectionEP>>()
val inspectionTools = loadKotlinInspections()
val errors = StringBuilder()
for (toolWrapper in inspectionTools) {
val description = toolWrapper.loadDescription()
if (description == null) {
errors.append("description is null for inspection '").append(desc(toolWrapper))
}
val shortName = toolWrapper.shortName
val tool = shortNames[shortName]
if (tool != null) {
errors.append(
"Short names must be unique: " + shortName + "\n" +
"inspection: '" + desc(tool) + "\n" +
" and '" + desc(toolWrapper)
)
}
shortNames.put(shortName, toolWrapper)
}
UsefulTestCase.assertEmpty(errors.toString())
}
private fun loadKotlinInspections(): List<InspectionToolWrapper<InspectionProfileEntry, InspectionEP>> {
return InspectionToolRegistrar.getInstance().createTools().filter {
it.extension.pluginDescriptor.pluginId == KotlinPluginUtil.KOTLIN_PLUGIN_ID
} as List<InspectionToolWrapper<InspectionProfileEntry, InspectionEP>>
}
private fun loadKotlinInspectionExtensions() =
LocalInspectionEP.LOCAL_INSPECTION.extensions.filter {
it.pluginDescriptor.pluginId == KotlinPluginUtil.KOTLIN_PLUGIN_ID
}
private fun desc(tool: InspectionToolWrapper<InspectionProfileEntry, InspectionEP>): String {
return tool.toString() + " ('" + tool.descriptionContextClass + "') " +
"in " + if (tool.extension == null) null else tool.extension.pluginDescriptor
}
fun testExtensionPoints() {
val shortNames = THashMap<String, LocalInspectionEP>()
@Suppress("DEPRECATION")
val inspectionEPs = Extensions.getExtensions(LocalInspectionEP.LOCAL_INSPECTION)
val tools = inspectionEPs.size
val errors = StringBuilder()
for (ep in inspectionEPs) {
val shortName = ep.getShortName()
val ep1 = shortNames[shortName]
if (ep1 != null) {
errors.append(
"Short names must be unique: '" + shortName + "':\n" +
"inspection: '" + ep1.implementationClass + "' in '" + ep1.pluginDescriptor + "'\n" +
"; and '" + ep.implementationClass + "' in '" + ep.pluginDescriptor + "'"
)
}
shortNames.put(shortName, ep)
}
println("$tools inspection tools total via EP")
UsefulTestCase.assertEmpty(errors.toString())
}
fun testInspectionMappings() {
val toolWrappers = loadKotlinInspections()
val errors = StringBuilder()
toolWrappers.filter({ toolWrapper -> toolWrapper.extension == null }).forEach { toolWrapper ->
errors.append("Please add XML mapping for ").append(toolWrapper.tool::class.java)
}
UsefulTestCase.assertEmpty(errors.toString())
}
fun testMismatchedIds() {
val failMessages = mutableListOf<String>()
for (ep in loadKotlinInspectionExtensions()) {
val toolName = ep.implementationClass
val tool = ep.instantiateTool()
if (tool is LocalInspectionTool) {
checkValue(failMessages, toolName, "suppressId", ep.id, ep.getShortName(), tool.id)
checkValue(failMessages, toolName, "alternateId", ep.alternativeId, null, tool.alternativeID)
checkValue(failMessages, toolName, "shortName", ep.getShortName(), null, tool.shortName)
checkValue(failMessages, toolName, "runForWholeFile", null, "false", tool.runForWholeFile().toString())
}
}
UsefulTestCase.assertEmpty(StringUtil.join(failMessages, "\n"), failMessages)
}
fun testNotEmptyToolNames() {
val failMessages = mutableListOf<String>()
for (ep in LocalInspectionEP.LOCAL_INSPECTION.extensions) {
val toolName = ep.implementationClass
if (ep.getDisplayName().isNullOrEmpty()) {
failMessages.add(toolName + ": toolName is not set, tool won't be available in `run inspection` action")
}
}
UsefulTestCase.assertEmpty(failMessages.joinToString("\n"), failMessages)
}
private fun checkValue(
failMessages: MutableCollection<String>,
toolName: String,
attributeName: String,
xmlValue: String?,
defaultXmlValue: String?,
javaValue: String?
) {
if (StringUtil.isNotEmpty(xmlValue)) {
if (javaValue != xmlValue) {
failMessages.add("$toolName: mismatched $attributeName. Xml: $xmlValue; Java: $javaValue")
}
} else if (StringUtil.isNotEmpty(javaValue)) {
if (javaValue != defaultXmlValue) {
failMessages.add("$toolName: $attributeName overridden in wrong way, will work in tests only. Please set appropriate $attributeName value in XML ($javaValue)")
}
}
}
}
@@ -0,0 +1,65 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.intentions
import com.intellij.codeInsight.intention.IntentionActionBean
import com.intellij.codeInsight.intention.IntentionManager
import com.intellij.codeInsight.intention.impl.config.IntentionManagerImpl
import com.intellij.openapi.extensions.Extensions
import com.intellij.testFramework.LightPlatformTestCase
import com.intellij.testFramework.UsefulTestCase
import org.jetbrains.kotlin.idea.KotlinPluginUtil
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
import org.junit.runner.RunWith
import java.io.File
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
class IntentionDescriptionTest : LightPlatformTestCase() {
private val necessaryNormalNames = listOf("description.html", "before.kt.template", "after.kt.template")
private val necessaryXmlNames = listOf("description.html", "before.xml.template", "after.xml.template")
private val necessaryMavenNames = listOf("description.html")
fun testDescriptionsAndShortNames() {
val intentionTools = loadKotlinIntentions()
val errors = StringBuilder()
for (tool in intentionTools) {
val className = tool.className
val shortName = className.substringAfterLast(".").replace("$", "")
val directory = File("idea/resources-en/intentionDescriptions/$shortName")
if (!directory.exists() || !directory.isDirectory) {
if (tool.categories != null) {
errors.append("No description directory for intention '").append(className).append("'\n")
}
} else {
val necessaryNames = when {
shortName.isMavenIntentionName() -> necessaryMavenNames
shortName.isXmlIntentionName() -> necessaryXmlNames
else -> necessaryNormalNames
}
for (fileName in necessaryNames) {
val file = directory.resolve(fileName)
if (!file.exists() || !file.isFile) {
errors.append("No description file $fileName for intention '").append(className).append("'\n")
}
}
}
}
UsefulTestCase.assertEmpty(errors.toString())
}
private fun String.isMavenIntentionName() = startsWith("MavenPlugin")
private fun String.isXmlIntentionName() = startsWith("Add") && endsWith("ToManifest")
private fun loadKotlinIntentions(): List<IntentionActionBean> {
val extensionPoint = Extensions.getRootArea().getExtensionPoint(IntentionManagerImpl.EP_INTENTION_ACTIONS)
return extensionPoint.extensions.toList().filter {
it.pluginDescriptor.pluginId == KotlinPluginUtil.KOTLIN_PLUGIN_ID
}
}
}
@@ -0,0 +1,136 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.slicer
import com.intellij.analysis.AnalysisScope
import com.intellij.ide.projectView.TreeStructureProvider
import com.intellij.ide.util.treeView.AbstractTreeStructureBase
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.PsiSearchScopeUtil
import com.intellij.slicer.DuplicateMap
import com.intellij.slicer.SliceAnalysisParams
import com.intellij.slicer.SliceNode
import com.intellij.slicer.SliceRootNode
import com.intellij.usages.TextChunk
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.contains
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import java.awt.Font
internal class TestSliceTreeStructure(private val rootNode: SliceNode) : AbstractTreeStructureBase(rootNode.project) {
override fun getProviders() = emptyList<TreeStructureProvider>()
override fun getRootElement() = rootNode
override fun commit() {
}
override fun hasSomethingToCommit() = false
}
internal fun buildTreeRepresentation(rootNode: SliceNode): String {
val project = rootNode.element!!.project!!
val projectScope = GlobalSearchScope.projectScope(project)
fun TextChunk.render(): String {
var text = text
if (attributes.fontType == Font.BOLD) {
text = "<bold>$text</bold>"
}
return text
}
fun SliceNode.isSliceLeafValueClassNode() = this is HackedSliceLeafValueClassNode
fun process(node: SliceNode, indent: Int): String {
val usage = node.element!!.value
node.calculateDupNode()
val isDuplicated = !node.isSliceLeafValueClassNode() && node.duplicate != null
return buildString {
when {
node is SliceRootNode && usage.element is KtFile -> {
node.sortedChildren.forEach { append(process(it, indent)) }
return@buildString
}
// SliceLeafValueClassNode is package-private
node.isSliceLeafValueClassNode() -> append("[${node.nodeText}]\n")
else -> {
val chunks = usage.text
if (!PsiSearchScopeUtil.isInScope(projectScope, usage.element!!)) {
append("LIB ")
} else {
append(chunks.first().render() + " ")
}
repeat(indent) { append('\t') }
if (usage is KotlinSliceDereferenceUsage) {
append("DEREFERENCE: ")
}
if (usage is KotlinSliceUsage) {
usage.mode.inlineCallStack.forEach {
append("(INLINE CALL ${it.function?.name}) ")
}
usage.mode.behaviourStack.reversed().joinTo(this, separator = "") { it.testPresentationPrefix }
}
if (isDuplicated) {
append("DUPLICATE: ")
}
chunks.slice(1 until chunks.size).joinTo(this, separator = "") { it.render() }
KotlinSliceUsageCellRenderer.containerSuffix(usage)?.let {
append(" ($it)")
}
append("\n")
}
}
if (!isDuplicated) {
node.sortedChildren.forEach { append(process(it, indent + 1)) }
}
}.replace(Regex("</bold><bold>"), "")
}
return process(rootNode, 0)
}
private val SliceNode.sortedChildren: List<SliceNode>
get() = children.sortedBy { it.value.element?.startOffset ?: -1 }
internal fun testSliceFromOffset(
file: KtFile,
offset: Int,
doTest: (sliceProvider: KotlinSliceProvider, rootNode: SliceRootNode) -> Unit
) {
val fileText = file.text
val flowKind = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// FLOW: ")
val withDereferences = InTextDirectivesUtils.isDirectiveDefined(fileText, "// WITH_DEREFERENCES")
val analysisParams = SliceAnalysisParams().apply {
dataFlowToThis = when (flowKind) {
"IN" -> true
"OUT" -> false
else -> throw AssertionError("Invalid flow kind: $flowKind")
}
showInstanceDereferences = withDereferences
scope = AnalysisScope(file.project)
}
val elementAtCaret = file.findElementAt(offset)!!
val sliceProvider = KotlinSliceProvider()
val expression = sliceProvider.getExpressionAtCaret(elementAtCaret, analysisParams.dataFlowToThis)!!
val rootUsage = sliceProvider.createRootUsage(expression, analysisParams)
val rootNode = SliceRootNode(file.project, DuplicateMap(), rootUsage)
doTest(sliceProvider, rootNode)
}
@@ -0,0 +1,50 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.stubs
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.DumbService
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.impl.cache.CacheManager
import com.intellij.psi.impl.source.tree.TreeElement
import com.intellij.psi.impl.source.tree.TreeUtil
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.UsageSearchContext
import com.intellij.testFramework.ExpectedHighlightingData
abstract class AbstractMultiHighlightingTest : AbstractMultiModuleTest() {
protected open val shouldCheckLineMarkers = false
protected open val shouldCheckResult = true
override fun checkHighlighting(data: ExpectedHighlightingData): Collection<HighlightInfo> {
data.init()
PsiDocumentManager.getInstance(myProject).commitAllDocuments()
//to load text
ApplicationManager.getApplication().runWriteAction { TreeUtil.clearCaches(myFile.node as TreeElement) }
//to initialize caches
if (!DumbService.isDumb(project)) {
CacheManager.SERVICE.getInstance(myProject)
.getFilesWithWord("XXX", UsageSearchContext.IN_COMMENTS, GlobalSearchScope.allScope(myProject), true)
}
val infos = doHighlighting()
val text = myEditor.document.text
if (shouldCheckLineMarkers) {
data.checkLineMarkers(myFile, DaemonCodeAnalyzerImpl.getLineMarkers(getDocument(file), project), text)
}
if (shouldCheckResult) {
data.checkResult(myFile, infos, text)
}
return infos
}
}