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) {
|
||||
val exitPoint = getSubroutineExitPoint(subroutine) ?: return
|
||||
handleJumpInsideTryFinally(exitPoint)
|
||||
add(ReturnValueInstruction(returnExpression, currentScope, exitPoint, returnValue))
|
||||
add(ReturnValueInstruction(returnExpression, currentScope, exitPoint, returnValue, subroutine))
|
||||
}
|
||||
|
||||
override fun returnNoValue(returnExpression: KtReturnExpression, subroutine: KtElement) {
|
||||
val exitPoint = getSubroutineExitPoint(subroutine) ?: return
|
||||
handleJumpInsideTryFinally(exitPoint)
|
||||
add(ReturnNoValueInstruction(returnExpression, currentScope, exitPoint))
|
||||
add(ReturnNoValueInstruction(returnExpression, currentScope, exitPoint, subroutine))
|
||||
}
|
||||
|
||||
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.PseudoValueFactory
|
||||
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.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
@@ -39,6 +41,13 @@ sealed class 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(
|
||||
element: KtElement,
|
||||
blockScope: BlockScope,
|
||||
|
||||
+3
-2
@@ -25,7 +25,8 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithRe
|
||||
class ReturnNoValueInstruction(
|
||||
element: KtElement,
|
||||
blockScope: BlockScope,
|
||||
targetLabel: Label
|
||||
targetLabel: Label,
|
||||
val subroutine: KtElement
|
||||
) : AbstractJumpInstruction(element, targetLabel, blockScope) {
|
||||
override fun accept(visitor: InstructionVisitor) {
|
||||
visitor.visitReturnNoValue(this)
|
||||
@@ -38,5 +39,5 @@ class ReturnNoValueInstruction(
|
||||
override fun toString(): String = "ret $targetLabel"
|
||||
|
||||
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.InstructionVisitor
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtReturnExpression
|
||||
|
||||
class ReturnValueInstruction(
|
||||
returnExpression: KtExpression,
|
||||
blockScope: BlockScope,
|
||||
targetLabel: Label,
|
||||
val returnedValue: PseudoValue
|
||||
val returnedValue: PseudoValue,
|
||||
val subroutine: KtElement
|
||||
) : AbstractJumpInstruction(returnExpression, targetLabel, blockScope) {
|
||||
override val inputValues: List<PseudoValue> get() = Collections.singletonList(returnedValue)
|
||||
|
||||
@@ -46,7 +48,7 @@ class ReturnValueInstruction(
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -130,6 +130,7 @@ import org.jetbrains.kotlin.idea.repl.AbstractIdeReplCompletionTest
|
||||
import org.jetbrains.kotlin.idea.resolve.*
|
||||
import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationHighlightingTest
|
||||
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.stubs.AbstractMultiFileHighlightingTest
|
||||
import org.jetbrains.kotlin.idea.stubs.AbstractResolveByStubTest
|
||||
@@ -1022,6 +1023,10 @@ fun main(args: Array<String>) {
|
||||
testClass<AbstractNameSuggestionProviderTest> {
|
||||
model("refactoring/nameSuggestionProvider")
|
||||
}
|
||||
|
||||
testClass<AbstractSlicerTest> {
|
||||
model("slicer", singleClass = true)
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("idea/idea-maven/test", "idea/idea-maven/testData") {
|
||||
|
||||
@@ -810,6 +810,8 @@
|
||||
|
||||
<facetType implementation="org.jetbrains.kotlin.idea.facet.KotlinFacetType"/>
|
||||
|
||||
<lang.sliceProvider language="kotlin" implementationClass="org.jetbrains.kotlin.idea.slicer.KotlinSliceProvider"/>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisIntention</className>
|
||||
<category>Kotlin</category>
|
||||
|
||||
@@ -64,7 +64,7 @@ class KotlinFindUsagesHandlerFactory(project: Project) : FindUsagesHandlerFactor
|
||||
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)
|
||||
|
||||
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) {
|
||||
var isReadWriteAccess: Boolean = true
|
||||
override var searchOverrides: Boolean = false
|
||||
|
||||
override fun toJavaOptions(project: Project): JavaVariableFindUsagesOptions {
|
||||
|
||||
+1
-1
@@ -112,7 +112,7 @@ abstract class KotlinFindMemberUsagesHandler<T : KtNamedDeclaration>
|
||||
when (access) {
|
||||
ReadWriteAccessDetector.Access.Read -> kotlinOptions.isReadAccess
|
||||
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