Analyze Data Flow: Initial implementation
#KT-11994 In Progress
This commit is contained in:
+2
-2
@@ -248,13 +248,13 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
|
|||||||
override fun returnValue(returnExpression: KtExpression, returnValue: PseudoValue, subroutine: KtElement) {
|
override fun returnValue(returnExpression: KtExpression, returnValue: PseudoValue, subroutine: KtElement) {
|
||||||
val exitPoint = getSubroutineExitPoint(subroutine) ?: return
|
val exitPoint = getSubroutineExitPoint(subroutine) ?: return
|
||||||
handleJumpInsideTryFinally(exitPoint)
|
handleJumpInsideTryFinally(exitPoint)
|
||||||
add(ReturnValueInstruction(returnExpression, currentScope, exitPoint, returnValue))
|
add(ReturnValueInstruction(returnExpression, currentScope, exitPoint, returnValue, subroutine))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun returnNoValue(returnExpression: KtReturnExpression, subroutine: KtElement) {
|
override fun returnNoValue(returnExpression: KtReturnExpression, subroutine: KtElement) {
|
||||||
val exitPoint = getSubroutineExitPoint(subroutine) ?: return
|
val exitPoint = getSubroutineExitPoint(subroutine) ?: return
|
||||||
handleJumpInsideTryFinally(exitPoint)
|
handleJumpInsideTryFinally(exitPoint)
|
||||||
add(ReturnNoValueInstruction(returnExpression, currentScope, exitPoint))
|
add(ReturnNoValueInstruction(returnExpression, currentScope, exitPoint, subroutine))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun write(
|
override fun write(
|
||||||
|
|||||||
+9
@@ -19,6 +19,8 @@ package org.jetbrains.kotlin.cfg.pseudocode.instructions.eval
|
|||||||
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
|
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValueFactory
|
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValueFactory
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.*
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.*
|
||||||
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||||
@@ -39,6 +41,13 @@ sealed class AccessTarget {
|
|||||||
object BlackBox: AccessTarget()
|
object BlackBox: AccessTarget()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val AccessTarget.accessedDescriptor: CallableDescriptor?
|
||||||
|
get() = when (this) {
|
||||||
|
is AccessTarget.Declaration -> descriptor
|
||||||
|
is AccessTarget.Call -> resolvedCall.resultingDescriptor
|
||||||
|
is AccessTarget.BlackBox -> null
|
||||||
|
}
|
||||||
|
|
||||||
abstract class AccessValueInstruction protected constructor(
|
abstract class AccessValueInstruction protected constructor(
|
||||||
element: KtElement,
|
element: KtElement,
|
||||||
blockScope: BlockScope,
|
blockScope: BlockScope,
|
||||||
|
|||||||
+3
-2
@@ -25,7 +25,8 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithRe
|
|||||||
class ReturnNoValueInstruction(
|
class ReturnNoValueInstruction(
|
||||||
element: KtElement,
|
element: KtElement,
|
||||||
blockScope: BlockScope,
|
blockScope: BlockScope,
|
||||||
targetLabel: Label
|
targetLabel: Label,
|
||||||
|
val subroutine: KtElement
|
||||||
) : AbstractJumpInstruction(element, targetLabel, blockScope) {
|
) : AbstractJumpInstruction(element, targetLabel, blockScope) {
|
||||||
override fun accept(visitor: InstructionVisitor) {
|
override fun accept(visitor: InstructionVisitor) {
|
||||||
visitor.visitReturnNoValue(this)
|
visitor.visitReturnNoValue(this)
|
||||||
@@ -38,5 +39,5 @@ class ReturnNoValueInstruction(
|
|||||||
override fun toString(): String = "ret $targetLabel"
|
override fun toString(): String = "ret $targetLabel"
|
||||||
|
|
||||||
override fun createCopy(newLabel: Label, blockScope: BlockScope): AbstractJumpInstruction =
|
override fun createCopy(newLabel: Label, blockScope: BlockScope): AbstractJumpInstruction =
|
||||||
ReturnNoValueInstruction(element, blockScope, newLabel)
|
ReturnNoValueInstruction(element, blockScope, newLabel, subroutine)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -23,13 +23,15 @@ import java.util.Collections
|
|||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.BlockScope
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.BlockScope
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult
|
||||||
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
import org.jetbrains.kotlin.psi.KtReturnExpression
|
import org.jetbrains.kotlin.psi.KtReturnExpression
|
||||||
|
|
||||||
class ReturnValueInstruction(
|
class ReturnValueInstruction(
|
||||||
returnExpression: KtExpression,
|
returnExpression: KtExpression,
|
||||||
blockScope: BlockScope,
|
blockScope: BlockScope,
|
||||||
targetLabel: Label,
|
targetLabel: Label,
|
||||||
val returnedValue: PseudoValue
|
val returnedValue: PseudoValue,
|
||||||
|
val subroutine: KtElement
|
||||||
) : AbstractJumpInstruction(returnExpression, targetLabel, blockScope) {
|
) : AbstractJumpInstruction(returnExpression, targetLabel, blockScope) {
|
||||||
override val inputValues: List<PseudoValue> get() = Collections.singletonList(returnedValue)
|
override val inputValues: List<PseudoValue> get() = Collections.singletonList(returnedValue)
|
||||||
|
|
||||||
@@ -46,7 +48,7 @@ class ReturnValueInstruction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun createCopy(newLabel: Label, blockScope: BlockScope): AbstractJumpInstruction {
|
override fun createCopy(newLabel: Label, blockScope: BlockScope): AbstractJumpInstruction {
|
||||||
return ReturnValueInstruction((element as KtExpression), blockScope, newLabel, returnedValue)
|
return ReturnValueInstruction((element as KtExpression), blockScope, newLabel, returnedValue, subroutine)
|
||||||
}
|
}
|
||||||
|
|
||||||
val returnExpressionIfAny: KtReturnExpression? = element as? KtReturnExpression
|
val returnExpressionIfAny: KtReturnExpression? = element as? KtReturnExpression
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ import org.jetbrains.kotlin.idea.repl.AbstractIdeReplCompletionTest
|
|||||||
import org.jetbrains.kotlin.idea.resolve.*
|
import org.jetbrains.kotlin.idea.resolve.*
|
||||||
import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationHighlightingTest
|
import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationHighlightingTest
|
||||||
import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationNavigationTest
|
import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationNavigationTest
|
||||||
|
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerTest
|
||||||
import org.jetbrains.kotlin.idea.structureView.AbstractKotlinFileStructureTest
|
import org.jetbrains.kotlin.idea.structureView.AbstractKotlinFileStructureTest
|
||||||
import org.jetbrains.kotlin.idea.stubs.AbstractMultiFileHighlightingTest
|
import org.jetbrains.kotlin.idea.stubs.AbstractMultiFileHighlightingTest
|
||||||
import org.jetbrains.kotlin.idea.stubs.AbstractResolveByStubTest
|
import org.jetbrains.kotlin.idea.stubs.AbstractResolveByStubTest
|
||||||
@@ -1022,6 +1023,10 @@ fun main(args: Array<String>) {
|
|||||||
testClass<AbstractNameSuggestionProviderTest> {
|
testClass<AbstractNameSuggestionProviderTest> {
|
||||||
model("refactoring/nameSuggestionProvider")
|
model("refactoring/nameSuggestionProvider")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testClass<AbstractSlicerTest> {
|
||||||
|
model("slicer", singleClass = true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testGroup("idea/idea-maven/test", "idea/idea-maven/testData") {
|
testGroup("idea/idea-maven/test", "idea/idea-maven/testData") {
|
||||||
|
|||||||
@@ -810,6 +810,8 @@
|
|||||||
|
|
||||||
<facetType implementation="org.jetbrains.kotlin.idea.facet.KotlinFacetType"/>
|
<facetType implementation="org.jetbrains.kotlin.idea.facet.KotlinFacetType"/>
|
||||||
|
|
||||||
|
<lang.sliceProvider language="kotlin" implementationClass="org.jetbrains.kotlin.idea.slicer.KotlinSliceProvider"/>
|
||||||
|
|
||||||
<intentionAction>
|
<intentionAction>
|
||||||
<className>org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisIntention</className>
|
<className>org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisIntention</className>
|
||||||
<category>Kotlin</category>
|
<category>Kotlin</category>
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class KotlinFindUsagesHandlerFactory(project: Project) : FindUsagesHandlerFactor
|
|||||||
return createFindUsagesHandler(element, forHighlightUsages, canAsk = !forHighlightUsages)
|
return createFindUsagesHandler(element, forHighlightUsages, canAsk = !forHighlightUsages)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createFindUsagesHandler(element: PsiElement, forHighlightUsages: Boolean, canAsk: Boolean): FindUsagesHandler {
|
fun createFindUsagesHandler(element: PsiElement, forHighlightUsages: Boolean, canAsk: Boolean): FindUsagesHandler {
|
||||||
val handler = createFindUsagesHandlerNoDecoration(element, canAsk)
|
val handler = createFindUsagesHandlerNoDecoration(element, canAsk)
|
||||||
|
|
||||||
return Extensions.getArea(element.project).getExtensionPoint(KotlinFindUsagesHandlerDecorator.EP_NAME).extensions.fold(handler) {
|
return Extensions.getArea(element.project).getExtensionPoint(KotlinFindUsagesHandlerDecorator.EP_NAME).extensions.fold(handler) {
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.findUsages
|
||||||
|
|
||||||
|
import com.intellij.find.findUsages.FindUsagesOptions
|
||||||
|
import com.intellij.usageView.UsageInfo
|
||||||
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
|
|
||||||
|
fun KtDeclaration.processAllUsages(
|
||||||
|
options: () -> FindUsagesOptions,
|
||||||
|
processor: (UsageInfo) -> Unit
|
||||||
|
) {
|
||||||
|
val findUsagesHandler = KotlinFindUsagesHandlerFactory(project).createFindUsagesHandler(this, false, false)
|
||||||
|
findUsagesHandler.processElementUsages(
|
||||||
|
this,
|
||||||
|
{
|
||||||
|
processor(it)
|
||||||
|
true
|
||||||
|
},
|
||||||
|
options()
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -65,6 +65,7 @@ class KotlinFunctionFindUsagesOptions(project: Project): KotlinCallableFindUsage
|
|||||||
}
|
}
|
||||||
|
|
||||||
class KotlinPropertyFindUsagesOptions(project: Project): KotlinCallableFindUsagesOptions, JavaVariableFindUsagesOptions(project) {
|
class KotlinPropertyFindUsagesOptions(project: Project): KotlinCallableFindUsagesOptions, JavaVariableFindUsagesOptions(project) {
|
||||||
|
var isReadWriteAccess: Boolean = true
|
||||||
override var searchOverrides: Boolean = false
|
override var searchOverrides: Boolean = false
|
||||||
|
|
||||||
override fun toJavaOptions(project: Project): JavaVariableFindUsagesOptions {
|
override fun toJavaOptions(project: Project): JavaVariableFindUsagesOptions {
|
||||||
|
|||||||
+1
-1
@@ -112,7 +112,7 @@ abstract class KotlinFindMemberUsagesHandler<T : KtNamedDeclaration>
|
|||||||
when (access) {
|
when (access) {
|
||||||
ReadWriteAccessDetector.Access.Read -> kotlinOptions.isReadAccess
|
ReadWriteAccessDetector.Access.Read -> kotlinOptions.isReadAccess
|
||||||
ReadWriteAccessDetector.Access.Write -> kotlinOptions.isWriteAccess
|
ReadWriteAccessDetector.Access.Write -> kotlinOptions.isWriteAccess
|
||||||
ReadWriteAccessDetector.Access.ReadWrite -> true
|
ReadWriteAccessDetector.Access.ReadWrite -> kotlinOptions.isReadWriteAccess
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.slicer
|
||||||
|
|
||||||
|
import com.intellij.ide.util.treeView.AbstractTreeStructure
|
||||||
|
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.slicer.SliceAnalysisParams
|
||||||
|
import com.intellij.slicer.SliceLanguageSupportProvider
|
||||||
|
import com.intellij.slicer.SliceTreeBuilder
|
||||||
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.isPlainWithEscapes
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||||
|
|
||||||
|
class KotlinSliceProvider : SliceLanguageSupportProvider {
|
||||||
|
override fun createRootUsage(element: PsiElement, params: SliceAnalysisParams) = KotlinSliceUsage(element, params)
|
||||||
|
|
||||||
|
override fun getExpressionAtCaret(atCaret: PsiElement?, dataFlowToThis: Boolean): KtExpression? {
|
||||||
|
val element =
|
||||||
|
atCaret?.parentsWithSelf
|
||||||
|
?.firstOrNull {
|
||||||
|
it is KtProperty ||
|
||||||
|
it is KtParameter ||
|
||||||
|
it is KtFunction ||
|
||||||
|
(it is KtExpression && it !is KtDeclaration)
|
||||||
|
}
|
||||||
|
?.let { KtPsiUtil.safeDeparenthesize(it as KtExpression) } ?: return null
|
||||||
|
if (dataFlowToThis) {
|
||||||
|
if (element is KtConstantExpression) return null
|
||||||
|
if (element is KtStringTemplateExpression && element.isPlainWithEscapes()) return null
|
||||||
|
if (element is KtClassLiteralExpression) return null
|
||||||
|
if (element is KtCallableReferenceExpression) return null
|
||||||
|
}
|
||||||
|
return element
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getElementForDescription(element: PsiElement): PsiElement {
|
||||||
|
return (element as? KtSimpleNameExpression)?.mainReference?.resolve() ?: element
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRenderer() = KotlinSliceUsageCellRenderer
|
||||||
|
|
||||||
|
override fun startAnalyzeLeafValues(structure: AbstractTreeStructure, finalRunnable: Runnable) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun startAnalyzeNullness(structure: AbstractTreeStructure, finalRunnable: Runnable) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun registerExtraPanelActions(group: DefaultActionGroup, builder: SliceTreeBuilder) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.slicer
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.slicer.SliceAnalysisParams
|
||||||
|
import com.intellij.slicer.SliceUsage
|
||||||
|
import com.intellij.util.Processor
|
||||||
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
|
|
||||||
|
open class KotlinSliceUsage : SliceUsage {
|
||||||
|
constructor(element: PsiElement, parent: SliceUsage) : super(element, parent)
|
||||||
|
constructor(element: PsiElement, params: SliceAnalysisParams) : super(element, params)
|
||||||
|
|
||||||
|
override fun copy(): KotlinSliceUsage {
|
||||||
|
val element = usageInfo.element!!
|
||||||
|
if (parent == null) return KotlinSliceUsage(element, params)
|
||||||
|
return KotlinSliceUsage(element, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun processUsagesFlownDownTo(element: PsiElement, uniqueProcessor: Processor<SliceUsage>) {
|
||||||
|
InflowSlicer(element as? KtExpression ?: return, uniqueProcessor, this).processChildren()
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun processUsagesFlownFromThe(element: PsiElement, uniqueProcessor: Processor<SliceUsage>) {
|
||||||
|
OutflowSlicer(element as? KtExpression ?: return, uniqueProcessor, this).processChildren()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.slicer
|
||||||
|
|
||||||
|
import com.intellij.slicer.SliceUsage
|
||||||
|
import com.intellij.slicer.SliceUsageCellRendererBase
|
||||||
|
import com.intellij.ui.SimpleTextAttributes
|
||||||
|
import com.intellij.util.FontUtil
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||||
|
import org.jetbrains.kotlin.renderer.ParameterNameRenderingPolicy
|
||||||
|
|
||||||
|
// Based on com.intellij.slicer.SliceUsageCellRenderer
|
||||||
|
object KotlinSliceUsageCellRenderer : SliceUsageCellRendererBase() {
|
||||||
|
private val descriptorRenderer = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.withOptions {
|
||||||
|
withDefinedIn = true
|
||||||
|
withoutTypeParameters = true
|
||||||
|
parameterNameRenderingPolicy = ParameterNameRenderingPolicy.NONE
|
||||||
|
includeAdditionalModifiers = false
|
||||||
|
withoutSuperTypes = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun customizeCellRendererFor(sliceUsage: SliceUsage) {
|
||||||
|
if (sliceUsage !is KotlinSliceUsage) return
|
||||||
|
|
||||||
|
for ((i, textChunk) in sliceUsage.getText().withIndex()) {
|
||||||
|
append(textChunk.text, textChunk.simpleAttributesIgnoreBackground)
|
||||||
|
if (i == 0) {
|
||||||
|
append(FontUtil.spaceAndThinSpace())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val declaration = sliceUsage.element?.parents?.firstOrNull {
|
||||||
|
it is KtClass ||
|
||||||
|
it is KtObjectDeclaration && !it.isObjectLiteral() ||
|
||||||
|
it is KtDeclarationWithBody ||
|
||||||
|
it is KtProperty && it.isLocal
|
||||||
|
} as? KtDeclaration ?: return
|
||||||
|
val descriptor = declaration.resolveToDescriptor()
|
||||||
|
append(" in ${descriptorRenderer.render(descriptor)}", SimpleTextAttributes.GRAY_ATTRIBUTES)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.slicer
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.search.SearchScope
|
||||||
|
import com.intellij.slicer.SliceUsage
|
||||||
|
import com.intellij.usageView.UsageInfo
|
||||||
|
import com.intellij.util.Processor
|
||||||
|
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
|
||||||
|
import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode
|
||||||
|
import org.jetbrains.kotlin.cfg.pseudocode.getContainingPseudocode
|
||||||
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
|
||||||
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
|
||||||
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ReturnValueInstruction
|
||||||
|
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder
|
||||||
|
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverse
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions
|
||||||
|
import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions
|
||||||
|
import org.jetbrains.kotlin.idea.findUsages.processAllUsages
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
|
||||||
|
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
private fun KtFunction.processCalls(scope: SearchScope, processor: (UsageInfo) -> Unit) {
|
||||||
|
processAllUsages(
|
||||||
|
{
|
||||||
|
KotlinFunctionFindUsagesOptions(project).apply {
|
||||||
|
isSearchForTextOccurrences = false
|
||||||
|
isSkipImportStatements = true
|
||||||
|
searchScope = scope.intersectWith(useScope)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
processor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtDeclaration.processVariableAccesses(scope: SearchScope, isRead: Boolean, processor: (UsageInfo) -> Unit) {
|
||||||
|
processAllUsages(
|
||||||
|
{
|
||||||
|
KotlinPropertyFindUsagesOptions(project).apply {
|
||||||
|
isReadAccess = isRead
|
||||||
|
isWriteAccess = !isRead
|
||||||
|
isReadWriteAccess = false
|
||||||
|
isSearchForTextOccurrences = false
|
||||||
|
isSkipImportStatements = true
|
||||||
|
searchScope = scope.intersectWith(useScope)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
processor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtProperty.canProcess(): Boolean {
|
||||||
|
if (hasDelegate()) return false
|
||||||
|
if (isLocal) return true
|
||||||
|
val descriptor = resolveToDescriptor() as? PropertyDescriptor ?: return false
|
||||||
|
return descriptor.accessors.all { it.isDefault }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtParameter.canProcess(): Boolean {
|
||||||
|
return !(isLoopParameter || isVarArg || hasValOrVar())
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Slicer(
|
||||||
|
protected val element: KtExpression,
|
||||||
|
protected val processor: Processor<SliceUsage>,
|
||||||
|
protected val parentUsage: SliceUsage
|
||||||
|
) {
|
||||||
|
protected class PseudocodeCache {
|
||||||
|
private val computedPseudocodes = HashMap<KtElement, Pseudocode>()
|
||||||
|
|
||||||
|
operator fun get(element: KtElement): Pseudocode? {
|
||||||
|
val container = element.containingDeclarationForPseudocode ?: return null
|
||||||
|
return computedPseudocodes.getOrPut(container) {
|
||||||
|
container.getContainingPseudocode(container.analyzeFully())?.apply { computedPseudocodes[container] = this } ?: return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected val pseudocodeCache = PseudocodeCache()
|
||||||
|
|
||||||
|
protected fun PsiElement.passToProcessor() {
|
||||||
|
processor.process(KotlinSliceUsage(this, parentUsage))
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun processChildren()
|
||||||
|
}
|
||||||
|
|
||||||
|
class InflowSlicer(
|
||||||
|
element: KtExpression,
|
||||||
|
processor: Processor<SliceUsage>,
|
||||||
|
parentUsage: SliceUsage
|
||||||
|
) : Slicer(element, processor, parentUsage) {
|
||||||
|
private fun KtProperty.processProperty() {
|
||||||
|
if (!canProcess()) return
|
||||||
|
|
||||||
|
initializer?.passToProcessor()
|
||||||
|
processVariableAccesses(parentUsage.scope.toSearchScope(), false) body@ {
|
||||||
|
val refExpression = it.element as? KtExpression ?: return@body
|
||||||
|
val rhs = KtPsiUtil.safeDeparenthesize(refExpression).getAssignmentByLHS()?.right ?: return@body
|
||||||
|
rhs.passToProcessor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtParameter.processParameter() {
|
||||||
|
if (!canProcess()) return
|
||||||
|
|
||||||
|
val function = ownerFunction ?: return
|
||||||
|
if (function.isOverridable()) return
|
||||||
|
|
||||||
|
val parameterDescriptor = resolveToDescriptor() as ValueParameterDescriptor
|
||||||
|
|
||||||
|
function.processCalls(parentUsage.scope.toSearchScope()) body@ {
|
||||||
|
val refExpression = it.element as? KtExpression ?: return@body
|
||||||
|
val callElement = refExpression.getParentOfTypeAndBranch<KtCallElement> { calleeExpression } ?: return@body
|
||||||
|
val resolvedCall = callElement.getResolvedCall(callElement.analyze()) ?: return@body
|
||||||
|
val resolvedArgument = resolvedCall.valueArguments[parameterDescriptor] ?: return@body
|
||||||
|
val flownExpression = when (resolvedArgument) {
|
||||||
|
is DefaultValueArgument -> defaultValue
|
||||||
|
is ExpressionValueArgument -> resolvedArgument.valueArgument?.getArgumentExpression()
|
||||||
|
else -> null
|
||||||
|
} ?: return@body
|
||||||
|
flownExpression.passToProcessor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtFunction.processFunction() {
|
||||||
|
val pseudocode = pseudocodeCache[bodyExpression ?: return] ?: return
|
||||||
|
pseudocode.traverse(TraversalOrder.FORWARD) { instr ->
|
||||||
|
if (instr is ReturnValueInstruction && instr.subroutine == this) {
|
||||||
|
(instr.returnExpressionIfAny?.returnedExpression ?: instr.element as? KtExpression)?.passToProcessor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Instruction.passInputsToProcessor() {
|
||||||
|
inputValues.forEach { it.element?.passToProcessor() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtExpression.processExpression() {
|
||||||
|
val pseudocode = pseudocodeCache[this] ?: return
|
||||||
|
val expressionValue = pseudocode.getElementValue(this) ?: return
|
||||||
|
val createdAt = expressionValue.createdAt
|
||||||
|
when (createdAt) {
|
||||||
|
is ReadValueInstruction -> (createdAt.target.accessedDescriptor?.source?.getPsi() as? KtDeclaration)?.passToProcessor()
|
||||||
|
|
||||||
|
is MergeInstruction -> createdAt.passInputsToProcessor()
|
||||||
|
|
||||||
|
is MagicInstruction -> when (createdAt.kind) {
|
||||||
|
MagicKind.NOT_NULL_ASSERTION, MagicKind.CAST -> createdAt.passInputsToProcessor()
|
||||||
|
else -> return
|
||||||
|
}
|
||||||
|
|
||||||
|
is CallInstruction -> (createdAt.resolvedCall.resultingDescriptor.source.getPsi() as? KtDeclarationWithBody)?.passToProcessor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun processChildren() {
|
||||||
|
when (element) {
|
||||||
|
is KtProperty -> element.processProperty()
|
||||||
|
is KtParameter -> element.processParameter()
|
||||||
|
is KtFunction -> element.processFunction()
|
||||||
|
else -> element.processExpression()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OutflowSlicer(
|
||||||
|
element: KtExpression,
|
||||||
|
processor: Processor<SliceUsage>,
|
||||||
|
parentUsage: SliceUsage
|
||||||
|
) : Slicer(element, processor, parentUsage) {
|
||||||
|
private fun KtDeclaration.processVariable() {
|
||||||
|
when (this) {
|
||||||
|
is KtProperty -> if (!canProcess()) return
|
||||||
|
is KtParameter -> if (!canProcess()) return
|
||||||
|
else -> return
|
||||||
|
}
|
||||||
|
processVariableAccesses(parentUsage.scope.toSearchScope(),true) body@ {
|
||||||
|
val refExpression = (it.element as? KtExpression)?.let { KtPsiUtil.safeDeparenthesize(it) } ?: return@body
|
||||||
|
refExpression.processExpression()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PsiElement.getCallElementForExactCallee(): KtElement? {
|
||||||
|
if (this is KtArrayAccessExpression) return this
|
||||||
|
|
||||||
|
val operationRefExpr = getNonStrictParentOfType<KtOperationReferenceExpression>()
|
||||||
|
if (operationRefExpr != null) return operationRefExpr.parent as? KtOperationExpression
|
||||||
|
|
||||||
|
val parentCall = getParentOfTypeAndBranch<KtCallElement> { calleeExpression } ?: return null
|
||||||
|
val callee = parentCall.calleeExpression?.let { KtPsiUtil.safeDeparenthesize(it) }
|
||||||
|
if (callee == this || callee is KtConstructorCalleeExpression && callee.isAncestor(this, true)) return parentCall
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtFunction.processFunctionCalls() {
|
||||||
|
processCalls(parentUsage.scope.toSearchScope()) {
|
||||||
|
it.element?.getCallElementForExactCallee()?.passToProcessor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtExpression.processExpression() {
|
||||||
|
val pseudocode = pseudocodeCache[this] ?: return
|
||||||
|
val pseudoValue = pseudocode.getElementValue(this) ?: return
|
||||||
|
pseudocode.getUsages(pseudoValue).forEach { instr ->
|
||||||
|
when (instr) {
|
||||||
|
is WriteValueInstruction -> (instr.target.accessedDescriptor?.source?.getPsi() as? KtDeclaration)?.passToProcessor()
|
||||||
|
is CallInstruction -> instr.arguments[pseudoValue]?.source?.getPsi()?.passToProcessor()
|
||||||
|
is ReturnValueInstruction -> (instr.owner.correspondingElement as? KtFunction)?.passToProcessor()
|
||||||
|
is MagicInstruction -> when (instr.kind) {
|
||||||
|
MagicKind.NOT_NULL_ASSERTION, MagicKind.CAST -> instr.outputValue.element?.passToProcessor()
|
||||||
|
else -> return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun processChildren() {
|
||||||
|
when (element) {
|
||||||
|
is KtProperty, is KtParameter -> (element as KtDeclaration).processVariable()
|
||||||
|
is KtFunction -> element.processFunctionCalls()
|
||||||
|
else -> element.processExpression()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
fun test(o: Any) {
|
||||||
|
val <caret>x = o as String
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
4 val <bold>x = o as String</bold>
|
||||||
|
4 val x = <bold>o as String</bold>
|
||||||
|
4 val x = <bold>o</bold> as String
|
||||||
|
3 fun test(<bold>o: Any</bold>) {
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
fun foo(<caret>n: Int, s: String = "???") {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1)
|
||||||
|
foo(1, "2")
|
||||||
|
foo(1, s = "2")
|
||||||
|
foo(n = 1, s = "2")
|
||||||
|
foo(s = "2", n = 1)
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
3 fun foo(<bold>n: Int</bold>, s: String = "???") {
|
||||||
|
8 foo(<bold>1</bold>)
|
||||||
|
9 foo(<bold>1</bold>, "2")
|
||||||
|
10 foo(<bold>1</bold>, s = "2")
|
||||||
|
11 foo(n = <bold>1</bold>, s = "2")
|
||||||
|
12 foo(s = "2", n = <bold>1</bold>)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
fun foo(n: Int, <caret>s: String = "???") {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1)
|
||||||
|
foo(1, "2")
|
||||||
|
foo(1, s = "2")
|
||||||
|
foo(n = 1, s = "2")
|
||||||
|
foo(s = "2", n = 1)
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
3 fun foo(n: Int, <bold>s: String = "???"</bold>) {
|
||||||
|
3 fun foo(n: Int, s: String = <bold>"???"</bold>) {
|
||||||
|
9 foo(1, <bold>"2"</bold>)
|
||||||
|
10 foo(1, s = <bold>"2"</bold>)
|
||||||
|
11 foo(n = 1, s = <bold>"2"</bold>)
|
||||||
|
12 foo(s = <bold>"2"</bold>, n = 1)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
fun <caret>foo(n: Int) = n + 1
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
3 fun <bold>foo(n: Int) = n + 1</bold>
|
||||||
|
3 fun foo(n: Int) = <bold>n + 1</bold>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
fun <caret>foo(n: Int): Int {
|
||||||
|
if (n > 0) return n
|
||||||
|
return -n
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
3 fun <bold>foo(n: Int): Int {</bold>
|
||||||
|
4 if (n > 0) return <bold>n</bold>
|
||||||
|
3 fun foo(<bold>n: Int</bold>): Int {
|
||||||
|
5 return <bold>-n</bold>
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
fun test(m: Int, n: Int) {
|
||||||
|
val <caret>x = if (m > 1) n else 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
4 val <bold>x = if (m > 1) n else 1</bold>
|
||||||
|
4 val x = <bold>if (m > 1) n else 1</bold>
|
||||||
|
4 val x = if (m > 1) <bold>n</bold> else 1
|
||||||
|
3 fun test(m: Int, <bold>n: Int</bold>) {
|
||||||
|
4 val x = if (m > 1) n else <bold>1</bold>
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
fun test(n: Int) {
|
||||||
|
val <caret>x = n
|
||||||
|
val y = x
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
4 val <bold>x = n</bold>
|
||||||
|
4 val x = <bold>n</bold>
|
||||||
|
3 fun test(<bold>n: Int</bold>) {
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
fun test(n: Int) {
|
||||||
|
var <caret>x = n
|
||||||
|
val y = x
|
||||||
|
x = 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
4 var <bold>x = n</bold>
|
||||||
|
4 var x = <bold>n</bold>
|
||||||
|
3 fun test(<bold>n: Int</bold>) {
|
||||||
|
6 x = <bold>0</bold>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val <caret>x: Int = 1
|
||||||
|
val y = x
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val y = x
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
4 val <bold>x: Int = 1</bold>
|
||||||
|
4 val x: Int = <bold>1</bold>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val <caret>x: Int
|
||||||
|
|
||||||
|
init {
|
||||||
|
x = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
val y = x
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val y = x
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
4 val <bold>x: Int</bold>
|
||||||
|
7 x = <bold>1</bold>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
class A {
|
||||||
|
var <caret>x: Int = 1
|
||||||
|
val y = x
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val y = x
|
||||||
|
x = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
4 var <bold>x: Int = 1</bold>
|
||||||
|
4 var x: Int = <bold>1</bold>
|
||||||
|
9 x = <bold>2</bold>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
class A {
|
||||||
|
var <caret>x: Int
|
||||||
|
|
||||||
|
init {
|
||||||
|
x = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
val y = x
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val y = x
|
||||||
|
x = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
4 var <bold>x: Int</bold>
|
||||||
|
7 x = <bold>1</bold>
|
||||||
|
14 x = <bold>2</bold>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
fun test(s: String?) {
|
||||||
|
val <caret>x = s!!
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
4 val <bold>x = s!!</bold>
|
||||||
|
4 val x = <bold>s!!</bold>
|
||||||
|
4 val x = <bold>s</bold>!!
|
||||||
|
3 fun test(<bold>s: String?</bold>) {
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
open class A(<caret>n: Int, s: String = "???")
|
||||||
|
|
||||||
|
class B1: A(1)
|
||||||
|
class B2: A(1, "2")
|
||||||
|
class B3: A(1, s = "2")
|
||||||
|
class B4: A(n = 1, s = "2")
|
||||||
|
class B5: A(s = "2", n = 1)
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
A(1)
|
||||||
|
A(1, "2")
|
||||||
|
A(1, s = "2")
|
||||||
|
A(n = 1, s = "2")
|
||||||
|
A(s = "2", n = 1)
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
3 open class A(<bold>n: Int</bold>, s: String = "???")
|
||||||
|
5 class B1: A(<bold>1</bold>)
|
||||||
|
6 class B2: A(<bold>1</bold>, "2")
|
||||||
|
7 class B3: A(<bold>1</bold>, s = "2")
|
||||||
|
8 class B4: A(n = <bold>1</bold>, s = "2")
|
||||||
|
9 class B5: A(s = "2", n = <bold>1</bold>)
|
||||||
|
12 A(<bold>1</bold>)
|
||||||
|
13 A(<bold>1</bold>, "2")
|
||||||
|
14 A(<bold>1</bold>, s = "2")
|
||||||
|
15 A(n = <bold>1</bold>, s = "2")
|
||||||
|
16 A(s = "2", n = <bold>1</bold>)
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
open class A(n: Int, <caret>s: String = "???")
|
||||||
|
|
||||||
|
class B1: A(1)
|
||||||
|
class B2: A(1, "2")
|
||||||
|
class B3: A(1, s = "2")
|
||||||
|
class B4: A(n = 1, s = "2")
|
||||||
|
class B5: A(s = "2", n = 1)
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
A(1)
|
||||||
|
A(1, "2")
|
||||||
|
A(1, s = "2")
|
||||||
|
A(n = 1, s = "2")
|
||||||
|
A(s = "2", n = 1)
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
3 open class A(n: Int, <bold>s: String = "???"</bold>)
|
||||||
|
3 open class A(n: Int, s: String = <bold>"???"</bold>)
|
||||||
|
6 class B2: A(1, <bold>"2"</bold>)
|
||||||
|
7 class B3: A(1, s = <bold>"2"</bold>)
|
||||||
|
8 class B4: A(n = 1, s = <bold>"2"</bold>)
|
||||||
|
9 class B5: A(s = <bold>"2"</bold>, n = 1)
|
||||||
|
13 A(1, <bold>"2"</bold>)
|
||||||
|
14 A(1, s = <bold>"2"</bold>)
|
||||||
|
15 A(n = 1, s = <bold>"2"</bold>)
|
||||||
|
16 A(s = <bold>"2"</bold>, n = 1)
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
fun test(o: Any) {
|
||||||
|
val <caret>x = o as? String
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
4 val <bold>x = o as? String</bold>
|
||||||
|
4 val x = <bold>o as? String</bold>
|
||||||
|
4 val x = <bold>o</bold> as? String
|
||||||
|
3 fun test(<bold>o: Any</bold>) {
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
open class A {
|
||||||
|
constructor(<caret>n: Int, s: String = "???")
|
||||||
|
}
|
||||||
|
|
||||||
|
class B1: A(1)
|
||||||
|
class B2: A(1, "2")
|
||||||
|
class B3: A(1, s = "2")
|
||||||
|
class B4: A(n = 1, s = "2")
|
||||||
|
class B5: A(s = "2", n = 1)
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
A(1)
|
||||||
|
A(1, "2")
|
||||||
|
A(1, s = "2")
|
||||||
|
A(n = 1, s = "2")
|
||||||
|
A(s = "2", n = 1)
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
4 constructor(<bold>n: Int</bold>, s: String = "???")
|
||||||
|
7 class B1: A(<bold>1</bold>)
|
||||||
|
8 class B2: A(<bold>1</bold>, "2")
|
||||||
|
9 class B3: A(<bold>1</bold>, s = "2")
|
||||||
|
10 class B4: A(n = <bold>1</bold>, s = "2")
|
||||||
|
11 class B5: A(s = "2", n = <bold>1</bold>)
|
||||||
|
14 A(<bold>1</bold>)
|
||||||
|
15 A(<bold>1</bold>, "2")
|
||||||
|
16 A(<bold>1</bold>, s = "2")
|
||||||
|
17 A(n = <bold>1</bold>, s = "2")
|
||||||
|
18 A(s = "2", n = <bold>1</bold>)
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
open class A {
|
||||||
|
constructor(n: Int, <caret>s: String = "???")
|
||||||
|
}
|
||||||
|
|
||||||
|
class B1: A(1)
|
||||||
|
class B2: A(1, "2")
|
||||||
|
class B3: A(1, s = "2")
|
||||||
|
class B4: A(n = 1, s = "2")
|
||||||
|
class B5: A(s = "2", n = 1)
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
A(1)
|
||||||
|
A(1, "2")
|
||||||
|
A(1, s = "2")
|
||||||
|
A(n = 1, s = "2")
|
||||||
|
A(s = "2", n = 1)
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
4 constructor(n: Int, <bold>s: String = "???"</bold>)
|
||||||
|
4 constructor(n: Int, s: String = <bold>"???"</bold>)
|
||||||
|
8 class B2: A(1, <bold>"2"</bold>)
|
||||||
|
9 class B3: A(1, s = <bold>"2"</bold>)
|
||||||
|
10 class B4: A(n = 1, s = <bold>"2"</bold>)
|
||||||
|
11 class B5: A(s = <bold>"2"</bold>, n = 1)
|
||||||
|
15 A(1, <bold>"2"</bold>)
|
||||||
|
16 A(1, s = <bold>"2"</bold>)
|
||||||
|
17 A(n = 1, s = <bold>"2"</bold>)
|
||||||
|
18 A(s = <bold>"2"</bold>, n = 1)
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
val <caret>foo: Int = 1
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val x = foo
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
3 val <bold>foo: Int = 1</bold>
|
||||||
|
3 val foo: Int = <bold>1</bold>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
var <caret>foo: Int = 1
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val x = foo
|
||||||
|
foo = 2
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
3 var <bold>foo: Int = 1</bold>
|
||||||
|
3 var foo: Int = <bold>1</bold>
|
||||||
|
7 foo = <bold>2</bold>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
fun test(m: Int, n: Int) {
|
||||||
|
val <caret>x = when (m) {
|
||||||
|
1 -> 1
|
||||||
|
2 -> n
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
4 val <bold>x = when (m) {</bold>
|
||||||
|
4 val x = <bold>when (m) {</bold>
|
||||||
|
5 1 -> <bold>1</bold>
|
||||||
|
6 2 -> <bold>n</bold>
|
||||||
|
3 fun test(m: Int, <bold>n: Int</bold>) {
|
||||||
|
7 else -> <bold>0</bold>
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
fun foo(n: Int) {
|
||||||
|
val y = n
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val x = <caret>1
|
||||||
|
|
||||||
|
foo(x)
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
7 val x = <bold>1</bold>
|
||||||
|
7 val <bold>x = 1</bold>
|
||||||
|
2 fun foo(<bold>n: Int</bold>) {
|
||||||
|
3 val <bold>y = n</bold>
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
|
||||||
|
fun test(<caret>o: Any) {
|
||||||
|
val x = o as String
|
||||||
|
val y = o as? String
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
3 fun test(<bold>o: Any</bold>) {
|
||||||
|
4 val x = <bold>o as String</bold>
|
||||||
|
4 val <bold>x = o as String</bold>
|
||||||
|
5 val y = <bold>o as? String</bold>
|
||||||
|
5 val <bold>y = o as? String</bold>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
|
||||||
|
fun foo(n: Int): Int = <caret>n
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val x = foo(1)
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
3 fun foo(n: Int): Int = <bold>n</bold>
|
||||||
|
3 fun <bold>foo(n: Int): Int = n</bold>
|
||||||
|
6 val x = <bold>foo(1)</bold>
|
||||||
|
6 val <bold>x = foo(1)</bold>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
|
||||||
|
fun foo(<caret>n: Int) {
|
||||||
|
val x = n
|
||||||
|
|
||||||
|
val y: Int
|
||||||
|
y = n
|
||||||
|
|
||||||
|
bar(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar(m: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
3 fun foo(<bold>n: Int</bold>) {
|
||||||
|
4 val <bold>x = n</bold>
|
||||||
|
6 val <bold>y: Int</bold>
|
||||||
|
12 fun bar(<bold>m: Int</bold>) {
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
|
||||||
|
fun foo(n: Int): Int {
|
||||||
|
return <caret>n
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val x = foo(1)
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
4 return <bold>n</bold>
|
||||||
|
3 fun <bold>foo(n: Int): Int {</bold>
|
||||||
|
8 val x = <bold>foo(1)</bold>
|
||||||
|
8 val <bold>x = foo(1)</bold>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
|
||||||
|
fun <caret>foo(n: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(m: Int) {
|
||||||
|
val x = foo(1)
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
3 fun <bold>foo(n: Int) {</bold>
|
||||||
|
8 val x = <bold>foo(1)</bold>
|
||||||
|
8 val <bold>x = foo(1)</bold>
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
|
||||||
|
class A {
|
||||||
|
operator fun <caret>get(n: Int) = this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val x = A()[2]
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
4 operator fun <bold>get(n: Int) = this</bold>
|
||||||
|
8 val x = <bold>A()[2]</bold>
|
||||||
|
8 val <bold>x = A()[2]</bold>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
|
||||||
|
fun test(m: Int, <caret>n: Int) {
|
||||||
|
val x = if (m > 1) n else 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
3 fun test(m: Int, <bold>n: Int</bold>) {
|
||||||
|
4 val <bold>x = if (m > 1) n else 1</bold>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
fun foo(n: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val <caret>x = 1
|
||||||
|
|
||||||
|
val y = x
|
||||||
|
|
||||||
|
val z: Int
|
||||||
|
z = x
|
||||||
|
|
||||||
|
foo(x)
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
6 val <bold>x = 1</bold>
|
||||||
|
8 val <bold>y = x</bold>
|
||||||
|
10 val <bold>z: Int</bold>
|
||||||
|
2 fun foo(<bold>n: Int</bold>) {
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val <caret>x = 1
|
||||||
|
|
||||||
|
val y = x
|
||||||
|
|
||||||
|
val z: Int
|
||||||
|
|
||||||
|
init {
|
||||||
|
z = x
|
||||||
|
|
||||||
|
bar(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar(m: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
4 val <bold>x = 1</bold>
|
||||||
|
6 val <bold>y = x</bold>
|
||||||
|
8 val <bold>z: Int</bold>
|
||||||
|
16 fun bar(<bold>m: Int</bold>) {
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
|
||||||
|
fun test(<caret>s: String?) {
|
||||||
|
val x = s!!
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
3 fun test(<bold>s: String?</bold>) {
|
||||||
|
4 val x = <bold>s!!</bold>
|
||||||
|
4 val <bold>x = s!!</bold>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
|
||||||
|
class A {
|
||||||
|
operator fun <caret>plus(n: Int) = this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val x = A() + 2
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
4 operator fun <bold>plus(n: Int) = this</bold>
|
||||||
|
8 val x = <bold>A() + 2</bold>
|
||||||
|
8 val <bold>x = A() + 2</bold>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
|
||||||
|
open class A<caret>(n: Int) {
|
||||||
|
constructor() : this(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
class B : A(1)
|
||||||
|
|
||||||
|
class C : A {
|
||||||
|
constructor() : super(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val x = A(1)
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
3 open class A<bold>(n: Int)</bold> {
|
||||||
|
7 class B : <bold>A(1)</bold>
|
||||||
|
14 val x = <bold>A(1)</bold>
|
||||||
|
14 val <bold>x = A(1)</bold>
|
||||||
|
4 constructor() : <bold>this(1)</bold>
|
||||||
|
10 constructor() : <bold>super(1)</bold>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
|
||||||
|
class A(<caret>n: Int) {
|
||||||
|
val x = n
|
||||||
|
|
||||||
|
val y: Int
|
||||||
|
|
||||||
|
init {
|
||||||
|
y = n
|
||||||
|
|
||||||
|
bar(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar(m: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
3 class A(<bold>n: Int</bold>) {
|
||||||
|
4 val <bold>x = n</bold>
|
||||||
|
6 val <bold>y: Int</bold>
|
||||||
|
14 fun bar(<bold>m: Int</bold>) {
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
|
||||||
|
open class A {
|
||||||
|
<caret>constructor(n: Int)
|
||||||
|
|
||||||
|
constructor() : this(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
class B : A(1)
|
||||||
|
|
||||||
|
class C : A {
|
||||||
|
constructor() : super(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val x = A(1)
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
4 <bold>constructor(n: Int)</bold>
|
||||||
|
9 class B : <bold>A(1)</bold>
|
||||||
|
16 val x = <bold>A(1)</bold>
|
||||||
|
16 val <bold>x = A(1)</bold>
|
||||||
|
6 constructor() : <bold>this(1)</bold>
|
||||||
|
12 constructor() : <bold>super(1)</bold>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
|
||||||
|
val <caret>x = 1
|
||||||
|
|
||||||
|
val y = x
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val y = x
|
||||||
|
|
||||||
|
val z: Int
|
||||||
|
|
||||||
|
init {
|
||||||
|
z = x
|
||||||
|
|
||||||
|
bar(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar(m: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
3 val <bold>x = 1</bold>
|
||||||
|
5 val <bold>y = x</bold>
|
||||||
|
8 val <bold>y = x</bold>
|
||||||
|
10 val <bold>z: Int</bold>
|
||||||
|
19 fun bar(<bold>m: Int</bold>) {
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
|
||||||
|
fun test(m: Int, <caret>n: Int) {
|
||||||
|
val x = when (m) {
|
||||||
|
1 -> 1
|
||||||
|
2 -> n
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
3 fun test(m: Int, <bold>n: Int</bold>) {
|
||||||
|
4 val <bold>x = when (m) {</bold>
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.slicer
|
||||||
|
|
||||||
|
import com.intellij.analysis.AnalysisScope
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.slicer.SliceAnalysisParams
|
||||||
|
import com.intellij.slicer.SliceUsage
|
||||||
|
import com.intellij.usages.TextChunk
|
||||||
|
import com.intellij.util.CommonProcessors
|
||||||
|
import gnu.trove.TObjectHashingStrategy
|
||||||
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
|
import java.awt.Font
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
abstract class AbstractSlicerTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||||
|
object SliceUsageHashingStrategy : TObjectHashingStrategy<SliceUsage> {
|
||||||
|
override fun computeHashCode(`object`: SliceUsage) = `object`.usageInfo.hashCode()
|
||||||
|
override fun equals(o1: SliceUsage, o2: SliceUsage) = o1.usageInfo == o2.usageInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on SliceUsage.processChildren
|
||||||
|
private fun KotlinSliceUsage.processChildrenWithoutProgress(processor: (KotlinSliceUsage) -> Unit) {
|
||||||
|
val element = runReadAction { element }
|
||||||
|
|
||||||
|
val uniqueProcessor = CommonProcessors.UniqueProcessor(
|
||||||
|
{
|
||||||
|
processor(it as KotlinSliceUsage)
|
||||||
|
true
|
||||||
|
},
|
||||||
|
SliceUsageHashingStrategy
|
||||||
|
)
|
||||||
|
|
||||||
|
runReadAction {
|
||||||
|
if (params.dataFlowToThis) {
|
||||||
|
processUsagesFlownDownTo(element, uniqueProcessor)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
processUsagesFlownFromThe(element, uniqueProcessor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildTreeRepresentation(rootUsage: KotlinSliceUsage): String {
|
||||||
|
val visitedElements = HashSet<PsiElement>()
|
||||||
|
|
||||||
|
fun TextChunk.render(): String {
|
||||||
|
var text = text
|
||||||
|
if (attributes.fontType == Font.BOLD) {
|
||||||
|
text = "<bold>$text</bold>"
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
fun process(usage: KotlinSliceUsage, indent: Int): String {
|
||||||
|
val isDuplicated = !visitedElements.add(usage.element)
|
||||||
|
|
||||||
|
return buildString {
|
||||||
|
val chunks = usage.text
|
||||||
|
append(chunks.first().render() + " ")
|
||||||
|
append("\t".repeat(indent))
|
||||||
|
chunks.slice(1..chunks.size - 1).joinTo(
|
||||||
|
this,
|
||||||
|
separator = "",
|
||||||
|
prefix = if (isDuplicated) "DUPLICATE: " else "",
|
||||||
|
postfix = "\n"
|
||||||
|
) { it.render() }
|
||||||
|
if (!isDuplicated) {
|
||||||
|
usage.processChildrenWithoutProgress { append(process(it, indent + 1)) }
|
||||||
|
}
|
||||||
|
}.replace(Regex("</bold><bold>"), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
return process(rootUsage, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun doTest(path: String) {
|
||||||
|
val mainFile = File(path)
|
||||||
|
|
||||||
|
myFixture.testDataPath = "${KotlinTestUtils.getHomeDirectory()}/${mainFile.parent}"
|
||||||
|
|
||||||
|
val fileText = FileUtil.loadFile(mainFile, true)
|
||||||
|
val flowKind = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// FLOW: ")
|
||||||
|
val analysisParams = SliceAnalysisParams().apply {
|
||||||
|
dataFlowToThis = when (flowKind) {
|
||||||
|
"IN" -> true
|
||||||
|
"OUT" -> false
|
||||||
|
else -> throw AssertionError("Invalid flow kind: $flowKind")
|
||||||
|
}
|
||||||
|
scope = AnalysisScope(project)
|
||||||
|
}
|
||||||
|
|
||||||
|
val file = myFixture.configureByFile(mainFile.name) as KtFile
|
||||||
|
val elementAtCaret = file.findElementAt(editor.caretModel.offset)
|
||||||
|
val sliceProvider = KotlinSliceProvider()
|
||||||
|
val expression = sliceProvider.getExpressionAtCaret(elementAtCaret, analysisParams.dataFlowToThis)!!
|
||||||
|
val rootUsage = sliceProvider.createRootUsage(expression, analysisParams)
|
||||||
|
KotlinTestUtils.assertEqualsToFile(File(path.replace(".kt", ".results.txt")), buildTreeRepresentation(rootUsage))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,266 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.slicer;
|
||||||
|
|
||||||
|
import com.intellij.testFramework.TestDataPath;
|
||||||
|
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||||
|
import org.jetbrains.kotlin.test.TargetBackend;
|
||||||
|
import org.jetbrains.kotlin.test.TestMetadata;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
@TestMetadata("idea/testData/slicer")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public class SlicerTestGenerated extends AbstractSlicerTest {
|
||||||
|
public void testAllFilesPresentInSlicer() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/slicer"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/cast.kt")
|
||||||
|
public void testInflow_Cast() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/cast.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/funParamerer.kt")
|
||||||
|
public void testInflow_FunParamerer() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/funParamerer.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/funParamererWithDefault.kt")
|
||||||
|
public void testInflow_FunParamererWithDefault() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/funParamererWithDefault.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/funWithExpressionBody.kt")
|
||||||
|
public void testInflow_FunWithExpressionBody() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/funWithExpressionBody.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/funWithReturnExpressions.kt")
|
||||||
|
public void testInflow_FunWithReturnExpressions() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/funWithReturnExpressions.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/ifExpression.kt")
|
||||||
|
public void testInflow_IfExpression() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/ifExpression.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/localVal.kt")
|
||||||
|
public void testInflow_LocalVal() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/localVal.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/localVar.kt")
|
||||||
|
public void testInflow_LocalVar() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/localVar.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/memberValWithInitializer.kt")
|
||||||
|
public void testInflow_MemberValWithInitializer() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/memberValWithInitializer.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/memberValWithSplitInitializer.kt")
|
||||||
|
public void testInflow_MemberValWithSplitInitializer() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/memberValWithSplitInitializer.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/memberVarWithInitializer.kt")
|
||||||
|
public void testInflow_MemberVarWithInitializer() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/memberVarWithInitializer.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/memberVarWithSplitInitializer.kt")
|
||||||
|
public void testInflow_MemberVarWithSplitInitializer() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/memberVarWithSplitInitializer.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/notNullAssertion.kt")
|
||||||
|
public void testInflow_NotNullAssertion() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/notNullAssertion.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/primaryConstructorParameter.kt")
|
||||||
|
public void testInflow_PrimaryConstructorParameter() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/primaryConstructorParameter.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/primaryConstructorParameterWithDefault.kt")
|
||||||
|
public void testInflow_PrimaryConstructorParameterWithDefault() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/primaryConstructorParameterWithDefault.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/safeCast.kt")
|
||||||
|
public void testInflow_SafeCast() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/safeCast.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/secondaryConstructorParameter.kt")
|
||||||
|
public void testInflow_SecondaryConstructorParameter() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/secondaryConstructorParameter.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/secondaryConstructorParameterWithDefault.kt")
|
||||||
|
public void testInflow_SecondaryConstructorParameterWithDefault() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/secondaryConstructorParameterWithDefault.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/topLevelVal.kt")
|
||||||
|
public void testInflow_TopLevelVal() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/topLevelVal.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/topLevelVar.kt")
|
||||||
|
public void testInflow_TopLevelVar() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/topLevelVar.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/whenExpression.kt")
|
||||||
|
public void testInflow_WhenExpression() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/whenExpression.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("outflow/callArgument.kt")
|
||||||
|
public void testOutflow_CallArgument() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/callArgument.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("outflow/cast.kt")
|
||||||
|
public void testOutflow_Cast() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/cast.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("outflow/funBodyExpression.kt")
|
||||||
|
public void testOutflow_FunBodyExpression() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/funBodyExpression.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("outflow/functionCalls.kt")
|
||||||
|
public void testOutflow_FunctionCalls() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/functionCalls.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("outflow/funParameterUsages.kt")
|
||||||
|
public void testOutflow_FunParameterUsages() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/funParameterUsages.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("outflow/funReturnExpression.kt")
|
||||||
|
public void testOutflow_FunReturnExpression() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/funReturnExpression.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("outflow/getFunCalls.kt")
|
||||||
|
public void testOutflow_GetFunCalls() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/getFunCalls.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("outflow/ifExpression.kt")
|
||||||
|
public void testOutflow_IfExpression() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/ifExpression.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("outflow/localVariableUsages.kt")
|
||||||
|
public void testOutflow_LocalVariableUsages() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/localVariableUsages.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("outflow/memberPropertyUsages.kt")
|
||||||
|
public void testOutflow_MemberPropertyUsages() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/memberPropertyUsages.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("outflow/notNullAssertion.kt")
|
||||||
|
public void testOutflow_NotNullAssertion() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/notNullAssertion.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("outflow/operatorFunCalls.kt")
|
||||||
|
public void testOutflow_OperatorFunCalls() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/operatorFunCalls.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("outflow/primaryConstructorCalls.kt")
|
||||||
|
public void testOutflow_PrimaryConstructorCalls() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/primaryConstructorCalls.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("outflow/primaryConstructorParameterUsages.kt")
|
||||||
|
public void testOutflow_PrimaryConstructorParameterUsages() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/primaryConstructorParameterUsages.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("outflow/secondaryConstructorCalls.kt")
|
||||||
|
public void testOutflow_SecondaryConstructorCalls() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/secondaryConstructorCalls.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("outflow/topLevelPropertyUsages.kt")
|
||||||
|
public void testOutflow_TopLevelPropertyUsages() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/topLevelPropertyUsages.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("outflow/whenExpression.kt")
|
||||||
|
public void testOutflow_WhenExpression() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/whenExpression.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user