diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/ControlFlowInstructionsGenerator.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/ControlFlowInstructionsGenerator.kt index c5efbd9c20f..e2cebacee90 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/ControlFlowInstructionsGenerator.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/ControlFlowInstructionsGenerator.kt @@ -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( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/instructions/eval/accessInstructions.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/instructions/eval/accessInstructions.kt index cd4b315d0d1..5f5d8c1af77 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/instructions/eval/accessInstructions.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/instructions/eval/accessInstructions.kt @@ -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, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/instructions/jumps/ReturnNoValueInstruction.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/instructions/jumps/ReturnNoValueInstruction.kt index dadecd9f4eb..6ddb1ea0318 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/instructions/jumps/ReturnNoValueInstruction.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/instructions/jumps/ReturnNoValueInstruction.kt @@ -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) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/instructions/jumps/ReturnValueInstruction.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/instructions/jumps/ReturnValueInstruction.kt index 8e68efa630f..29d541065f1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/instructions/jumps/ReturnValueInstruction.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/instructions/jumps/ReturnValueInstruction.kt @@ -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 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 diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 8223b61d470..0d324224a13 100755 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -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) { testClass { model("refactoring/nameSuggestionProvider") } + + testClass { + model("slicer", singleClass = true) + } } testGroup("idea/idea-maven/test", "idea/idea-maven/testData") { diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 6bd87f1c224..1b706965e74 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -810,6 +810,8 @@ + + org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisIntention Kotlin diff --git a/idea/src/org/jetbrains/kotlin/idea/findUsages/KotlinFindUsagesHandlerFactory.kt b/idea/src/org/jetbrains/kotlin/idea/findUsages/KotlinFindUsagesHandlerFactory.kt index 93d3db1ba5c..33aaa319227 100644 --- a/idea/src/org/jetbrains/kotlin/idea/findUsages/KotlinFindUsagesHandlerFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/findUsages/KotlinFindUsagesHandlerFactory.kt @@ -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) { diff --git a/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsageUtils.kt b/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsageUtils.kt new file mode 100644 index 00000000000..d7213d7c8d4 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsageUtils.kt @@ -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() + ) +} diff --git a/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsagesOptions.kt b/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsagesOptions.kt index d713e335eb4..baa98c0305d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsagesOptions.kt +++ b/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsagesOptions.kt @@ -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 { diff --git a/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/KotlinFindMemberUsagesHandler.kt b/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/KotlinFindMemberUsagesHandler.kt index 736e66527e2..ed1fa6a6b79 100644 --- a/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/KotlinFindMemberUsagesHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/KotlinFindMemberUsagesHandler.kt @@ -112,7 +112,7 @@ abstract class KotlinFindMemberUsagesHandler when (access) { ReadWriteAccessDetector.Access.Read -> kotlinOptions.isReadAccess ReadWriteAccessDetector.Access.Write -> kotlinOptions.isWriteAccess - ReadWriteAccessDetector.Access.ReadWrite -> true + ReadWriteAccessDetector.Access.ReadWrite -> kotlinOptions.isReadWriteAccess } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceProvider.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceProvider.kt new file mode 100644 index 00000000000..3b35238a79c --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceProvider.kt @@ -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) { + + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsage.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsage.kt new file mode 100644 index 00000000000..9146dc93f68 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsage.kt @@ -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) { + InflowSlicer(element as? KtExpression ?: return, uniqueProcessor, this).processChildren() + } + + public override fun processUsagesFlownFromThe(element: PsiElement, uniqueProcessor: Processor) { + OutflowSlicer(element as? KtExpression ?: return, uniqueProcessor, this).processChildren() + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsageCellRenderer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsageCellRenderer.kt new file mode 100644 index 00000000000..d98716a3856 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsageCellRenderer.kt @@ -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) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt new file mode 100644 index 00000000000..5cf64ffde49 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt @@ -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, + protected val parentUsage: SliceUsage +) { + protected class PseudocodeCache { + private val computedPseudocodes = HashMap() + + 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, + 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 { 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, + 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() + if (operationRefExpr != null) return operationRefExpr.parent as? KtOperationExpression + + val parentCall = getParentOfTypeAndBranch { 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() + } + } +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/cast.kt b/idea/testData/slicer/inflow/cast.kt new file mode 100644 index 00000000000..cc3652d5fe2 --- /dev/null +++ b/idea/testData/slicer/inflow/cast.kt @@ -0,0 +1,5 @@ +// FLOW: IN + +fun test(o: Any) { + val x = o as String +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/cast.results.txt b/idea/testData/slicer/inflow/cast.results.txt new file mode 100644 index 00000000000..4ce165fe7e3 --- /dev/null +++ b/idea/testData/slicer/inflow/cast.results.txt @@ -0,0 +1,4 @@ +4 val x = o as String +4 val x = o as String +4 val x = o as String +3 fun test(o: Any) { diff --git a/idea/testData/slicer/inflow/funParamerer.kt b/idea/testData/slicer/inflow/funParamerer.kt new file mode 100644 index 00000000000..ee4e564c9be --- /dev/null +++ b/idea/testData/slicer/inflow/funParamerer.kt @@ -0,0 +1,13 @@ +// FLOW: IN + +fun foo(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) +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/funParamerer.results.txt b/idea/testData/slicer/inflow/funParamerer.results.txt new file mode 100644 index 00000000000..9512d61dacb --- /dev/null +++ b/idea/testData/slicer/inflow/funParamerer.results.txt @@ -0,0 +1,6 @@ +3 fun foo(n: Int, s: String = "???") { +8 foo(1) +9 foo(1, "2") +10 foo(1, s = "2") +11 foo(n = 1, s = "2") +12 foo(s = "2", n = 1) diff --git a/idea/testData/slicer/inflow/funParamererWithDefault.kt b/idea/testData/slicer/inflow/funParamererWithDefault.kt new file mode 100644 index 00000000000..0ec7f9a3878 --- /dev/null +++ b/idea/testData/slicer/inflow/funParamererWithDefault.kt @@ -0,0 +1,13 @@ +// FLOW: IN + +fun foo(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) +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/funParamererWithDefault.results.txt b/idea/testData/slicer/inflow/funParamererWithDefault.results.txt new file mode 100644 index 00000000000..24826078dda --- /dev/null +++ b/idea/testData/slicer/inflow/funParamererWithDefault.results.txt @@ -0,0 +1,6 @@ +3 fun foo(n: Int, s: String = "???") { +3 fun foo(n: Int, s: String = "???") { +9 foo(1, "2") +10 foo(1, s = "2") +11 foo(n = 1, s = "2") +12 foo(s = "2", n = 1) diff --git a/idea/testData/slicer/inflow/funWithExpressionBody.kt b/idea/testData/slicer/inflow/funWithExpressionBody.kt new file mode 100644 index 00000000000..e38b66cfb4d --- /dev/null +++ b/idea/testData/slicer/inflow/funWithExpressionBody.kt @@ -0,0 +1,3 @@ +// FLOW: IN + +fun foo(n: Int) = n + 1 \ No newline at end of file diff --git a/idea/testData/slicer/inflow/funWithExpressionBody.results.txt b/idea/testData/slicer/inflow/funWithExpressionBody.results.txt new file mode 100644 index 00000000000..f1737af11fc --- /dev/null +++ b/idea/testData/slicer/inflow/funWithExpressionBody.results.txt @@ -0,0 +1,2 @@ +3 fun foo(n: Int) = n + 1 +3 fun foo(n: Int) = n + 1 diff --git a/idea/testData/slicer/inflow/funWithReturnExpressions.kt b/idea/testData/slicer/inflow/funWithReturnExpressions.kt new file mode 100644 index 00000000000..13acbc374e1 --- /dev/null +++ b/idea/testData/slicer/inflow/funWithReturnExpressions.kt @@ -0,0 +1,6 @@ +// FLOW: IN + +fun foo(n: Int): Int { + if (n > 0) return n + return -n +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/funWithReturnExpressions.results.txt b/idea/testData/slicer/inflow/funWithReturnExpressions.results.txt new file mode 100644 index 00000000000..74c52386b4c --- /dev/null +++ b/idea/testData/slicer/inflow/funWithReturnExpressions.results.txt @@ -0,0 +1,4 @@ +3 fun foo(n: Int): Int { +4 if (n > 0) return n +3 fun foo(n: Int): Int { +5 return -n diff --git a/idea/testData/slicer/inflow/ifExpression.kt b/idea/testData/slicer/inflow/ifExpression.kt new file mode 100644 index 00000000000..7dc1ffc7661 --- /dev/null +++ b/idea/testData/slicer/inflow/ifExpression.kt @@ -0,0 +1,5 @@ +// FLOW: IN + +fun test(m: Int, n: Int) { + val x = if (m > 1) n else 1 +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/ifExpression.results.txt b/idea/testData/slicer/inflow/ifExpression.results.txt new file mode 100644 index 00000000000..6e02fda5090 --- /dev/null +++ b/idea/testData/slicer/inflow/ifExpression.results.txt @@ -0,0 +1,5 @@ +4 val x = if (m > 1) n else 1 +4 val x = if (m > 1) n else 1 +4 val x = if (m > 1) n else 1 +3 fun test(m: Int, n: Int) { +4 val x = if (m > 1) n else 1 diff --git a/idea/testData/slicer/inflow/localVal.kt b/idea/testData/slicer/inflow/localVal.kt new file mode 100644 index 00000000000..8714f374aac --- /dev/null +++ b/idea/testData/slicer/inflow/localVal.kt @@ -0,0 +1,6 @@ +// FLOW: IN + +fun test(n: Int) { + val x = n + val y = x +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/localVal.results.txt b/idea/testData/slicer/inflow/localVal.results.txt new file mode 100644 index 00000000000..2dce24565b7 --- /dev/null +++ b/idea/testData/slicer/inflow/localVal.results.txt @@ -0,0 +1,3 @@ +4 val x = n +4 val x = n +3 fun test(n: Int) { diff --git a/idea/testData/slicer/inflow/localVar.kt b/idea/testData/slicer/inflow/localVar.kt new file mode 100644 index 00000000000..715f3e115c4 --- /dev/null +++ b/idea/testData/slicer/inflow/localVar.kt @@ -0,0 +1,7 @@ +// FLOW: IN + +fun test(n: Int) { + var x = n + val y = x + x = 0 +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/localVar.results.txt b/idea/testData/slicer/inflow/localVar.results.txt new file mode 100644 index 00000000000..cd93cf5884f --- /dev/null +++ b/idea/testData/slicer/inflow/localVar.results.txt @@ -0,0 +1,4 @@ +4 var x = n +4 var x = n +3 fun test(n: Int) { +6 x = 0 diff --git a/idea/testData/slicer/inflow/memberValWithInitializer.kt b/idea/testData/slicer/inflow/memberValWithInitializer.kt new file mode 100644 index 00000000000..bdd29eb172a --- /dev/null +++ b/idea/testData/slicer/inflow/memberValWithInitializer.kt @@ -0,0 +1,10 @@ +// FLOW: IN + +class A { + val x: Int = 1 + val y = x + + fun test() { + val y = x + } +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/memberValWithInitializer.results.txt b/idea/testData/slicer/inflow/memberValWithInitializer.results.txt new file mode 100644 index 00000000000..2a3db0d2579 --- /dev/null +++ b/idea/testData/slicer/inflow/memberValWithInitializer.results.txt @@ -0,0 +1,2 @@ +4 val x: Int = 1 +4 val x: Int = 1 diff --git a/idea/testData/slicer/inflow/memberValWithSplitInitializer.kt b/idea/testData/slicer/inflow/memberValWithSplitInitializer.kt new file mode 100644 index 00000000000..f83e839a3fe --- /dev/null +++ b/idea/testData/slicer/inflow/memberValWithSplitInitializer.kt @@ -0,0 +1,15 @@ +// FLOW: IN + +class A { + val x: Int + + init { + x = 1 + } + + val y = x + + fun test() { + val y = x + } +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/memberValWithSplitInitializer.results.txt b/idea/testData/slicer/inflow/memberValWithSplitInitializer.results.txt new file mode 100644 index 00000000000..d479a4d425d --- /dev/null +++ b/idea/testData/slicer/inflow/memberValWithSplitInitializer.results.txt @@ -0,0 +1,2 @@ +4 val x: Int +7 x = 1 diff --git a/idea/testData/slicer/inflow/memberVarWithInitializer.kt b/idea/testData/slicer/inflow/memberVarWithInitializer.kt new file mode 100644 index 00000000000..807702cda52 --- /dev/null +++ b/idea/testData/slicer/inflow/memberVarWithInitializer.kt @@ -0,0 +1,11 @@ +// FLOW: IN + +class A { + var x: Int = 1 + val y = x + + fun test() { + val y = x + x = 2 + } +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/memberVarWithInitializer.results.txt b/idea/testData/slicer/inflow/memberVarWithInitializer.results.txt new file mode 100644 index 00000000000..00109f3bc0b --- /dev/null +++ b/idea/testData/slicer/inflow/memberVarWithInitializer.results.txt @@ -0,0 +1,3 @@ +4 var x: Int = 1 +4 var x: Int = 1 +9 x = 2 diff --git a/idea/testData/slicer/inflow/memberVarWithSplitInitializer.kt b/idea/testData/slicer/inflow/memberVarWithSplitInitializer.kt new file mode 100644 index 00000000000..08970dde49a --- /dev/null +++ b/idea/testData/slicer/inflow/memberVarWithSplitInitializer.kt @@ -0,0 +1,16 @@ +// FLOW: IN + +class A { + var x: Int + + init { + x = 1 + } + + val y = x + + fun test() { + val y = x + x = 2 + } +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/memberVarWithSplitInitializer.results.txt b/idea/testData/slicer/inflow/memberVarWithSplitInitializer.results.txt new file mode 100644 index 00000000000..623b3cd1230 --- /dev/null +++ b/idea/testData/slicer/inflow/memberVarWithSplitInitializer.results.txt @@ -0,0 +1,3 @@ +4 var x: Int +7 x = 1 +14 x = 2 diff --git a/idea/testData/slicer/inflow/notNullAssertion.kt b/idea/testData/slicer/inflow/notNullAssertion.kt new file mode 100644 index 00000000000..6cafa2bc5fb --- /dev/null +++ b/idea/testData/slicer/inflow/notNullAssertion.kt @@ -0,0 +1,5 @@ +// FLOW: IN + +fun test(s: String?) { + val x = s!! +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/notNullAssertion.results.txt b/idea/testData/slicer/inflow/notNullAssertion.results.txt new file mode 100644 index 00000000000..3add28a423b --- /dev/null +++ b/idea/testData/slicer/inflow/notNullAssertion.results.txt @@ -0,0 +1,4 @@ +4 val x = s!! +4 val x = s!! +4 val x = s!! +3 fun test(s: String?) { diff --git a/idea/testData/slicer/inflow/primaryConstructorParameter.kt b/idea/testData/slicer/inflow/primaryConstructorParameter.kt new file mode 100644 index 00000000000..957941f898b --- /dev/null +++ b/idea/testData/slicer/inflow/primaryConstructorParameter.kt @@ -0,0 +1,17 @@ +// FLOW: IN + +open class A(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) +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/primaryConstructorParameter.results.txt b/idea/testData/slicer/inflow/primaryConstructorParameter.results.txt new file mode 100644 index 00000000000..692b92fa3bf --- /dev/null +++ b/idea/testData/slicer/inflow/primaryConstructorParameter.results.txt @@ -0,0 +1,11 @@ +3 open class A(n: Int, s: String = "???") +5 class B1: A(1) +6 class B2: A(1, "2") +7 class B3: A(1, s = "2") +8 class B4: A(n = 1, s = "2") +9 class B5: A(s = "2", n = 1) +12 A(1) +13 A(1, "2") +14 A(1, s = "2") +15 A(n = 1, s = "2") +16 A(s = "2", n = 1) diff --git a/idea/testData/slicer/inflow/primaryConstructorParameterWithDefault.kt b/idea/testData/slicer/inflow/primaryConstructorParameterWithDefault.kt new file mode 100644 index 00000000000..d178cccebcc --- /dev/null +++ b/idea/testData/slicer/inflow/primaryConstructorParameterWithDefault.kt @@ -0,0 +1,17 @@ +// FLOW: IN + +open class A(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) +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/primaryConstructorParameterWithDefault.results.txt b/idea/testData/slicer/inflow/primaryConstructorParameterWithDefault.results.txt new file mode 100644 index 00000000000..0b88fa77883 --- /dev/null +++ b/idea/testData/slicer/inflow/primaryConstructorParameterWithDefault.results.txt @@ -0,0 +1,10 @@ +3 open class A(n: Int, s: String = "???") +3 open class A(n: Int, s: String = "???") +6 class B2: A(1, "2") +7 class B3: A(1, s = "2") +8 class B4: A(n = 1, s = "2") +9 class B5: A(s = "2", n = 1) +13 A(1, "2") +14 A(1, s = "2") +15 A(n = 1, s = "2") +16 A(s = "2", n = 1) diff --git a/idea/testData/slicer/inflow/safeCast.kt b/idea/testData/slicer/inflow/safeCast.kt new file mode 100644 index 00000000000..f3f02945f15 --- /dev/null +++ b/idea/testData/slicer/inflow/safeCast.kt @@ -0,0 +1,5 @@ +// FLOW: IN + +fun test(o: Any) { + val x = o as? String +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/safeCast.results.txt b/idea/testData/slicer/inflow/safeCast.results.txt new file mode 100644 index 00000000000..672b68824c1 --- /dev/null +++ b/idea/testData/slicer/inflow/safeCast.results.txt @@ -0,0 +1,4 @@ +4 val x = o as? String +4 val x = o as? String +4 val x = o as? String +3 fun test(o: Any) { diff --git a/idea/testData/slicer/inflow/secondaryConstructorParameter.kt b/idea/testData/slicer/inflow/secondaryConstructorParameter.kt new file mode 100644 index 00000000000..dc870536ce4 --- /dev/null +++ b/idea/testData/slicer/inflow/secondaryConstructorParameter.kt @@ -0,0 +1,19 @@ +// FLOW: IN + +open class A { + constructor(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) +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/secondaryConstructorParameter.results.txt b/idea/testData/slicer/inflow/secondaryConstructorParameter.results.txt new file mode 100644 index 00000000000..7ccd9cdae69 --- /dev/null +++ b/idea/testData/slicer/inflow/secondaryConstructorParameter.results.txt @@ -0,0 +1,11 @@ +4 constructor(n: Int, s: String = "???") +7 class B1: A(1) +8 class B2: A(1, "2") +9 class B3: A(1, s = "2") +10 class B4: A(n = 1, s = "2") +11 class B5: A(s = "2", n = 1) +14 A(1) +15 A(1, "2") +16 A(1, s = "2") +17 A(n = 1, s = "2") +18 A(s = "2", n = 1) diff --git a/idea/testData/slicer/inflow/secondaryConstructorParameterWithDefault.kt b/idea/testData/slicer/inflow/secondaryConstructorParameterWithDefault.kt new file mode 100644 index 00000000000..32614031efe --- /dev/null +++ b/idea/testData/slicer/inflow/secondaryConstructorParameterWithDefault.kt @@ -0,0 +1,19 @@ +// FLOW: IN + +open class A { + constructor(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) +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/secondaryConstructorParameterWithDefault.results.txt b/idea/testData/slicer/inflow/secondaryConstructorParameterWithDefault.results.txt new file mode 100644 index 00000000000..a67d20eb31f --- /dev/null +++ b/idea/testData/slicer/inflow/secondaryConstructorParameterWithDefault.results.txt @@ -0,0 +1,10 @@ +4 constructor(n: Int, s: String = "???") +4 constructor(n: Int, s: String = "???") +8 class B2: A(1, "2") +9 class B3: A(1, s = "2") +10 class B4: A(n = 1, s = "2") +11 class B5: A(s = "2", n = 1) +15 A(1, "2") +16 A(1, s = "2") +17 A(n = 1, s = "2") +18 A(s = "2", n = 1) diff --git a/idea/testData/slicer/inflow/topLevelVal.kt b/idea/testData/slicer/inflow/topLevelVal.kt new file mode 100644 index 00000000000..0f561e24880 --- /dev/null +++ b/idea/testData/slicer/inflow/topLevelVal.kt @@ -0,0 +1,7 @@ +// FLOW: IN + +val foo: Int = 1 + +fun test() { + val x = foo +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/topLevelVal.results.txt b/idea/testData/slicer/inflow/topLevelVal.results.txt new file mode 100644 index 00000000000..dd4cc8d739e --- /dev/null +++ b/idea/testData/slicer/inflow/topLevelVal.results.txt @@ -0,0 +1,2 @@ +3 val foo: Int = 1 +3 val foo: Int = 1 diff --git a/idea/testData/slicer/inflow/topLevelVar.kt b/idea/testData/slicer/inflow/topLevelVar.kt new file mode 100644 index 00000000000..bebf78cb4cf --- /dev/null +++ b/idea/testData/slicer/inflow/topLevelVar.kt @@ -0,0 +1,8 @@ +// FLOW: IN + +var foo: Int = 1 + +fun test() { + val x = foo + foo = 2 +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/topLevelVar.results.txt b/idea/testData/slicer/inflow/topLevelVar.results.txt new file mode 100644 index 00000000000..cf8f5afec92 --- /dev/null +++ b/idea/testData/slicer/inflow/topLevelVar.results.txt @@ -0,0 +1,3 @@ +3 var foo: Int = 1 +3 var foo: Int = 1 +7 foo = 2 diff --git a/idea/testData/slicer/inflow/whenExpression.kt b/idea/testData/slicer/inflow/whenExpression.kt new file mode 100644 index 00000000000..0bd8e7bccff --- /dev/null +++ b/idea/testData/slicer/inflow/whenExpression.kt @@ -0,0 +1,9 @@ +// FLOW: IN + +fun test(m: Int, n: Int) { + val x = when (m) { + 1 -> 1 + 2 -> n + else -> 0 + } +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/whenExpression.results.txt b/idea/testData/slicer/inflow/whenExpression.results.txt new file mode 100644 index 00000000000..5d81dfb8b93 --- /dev/null +++ b/idea/testData/slicer/inflow/whenExpression.results.txt @@ -0,0 +1,6 @@ +4 val x = when (m) { +4 val x = when (m) { +5 1 -> 1 +6 2 -> n +3 fun test(m: Int, n: Int) { +7 else -> 0 diff --git a/idea/testData/slicer/outflow/callArgument.kt b/idea/testData/slicer/outflow/callArgument.kt new file mode 100644 index 00000000000..cb0f6547cce --- /dev/null +++ b/idea/testData/slicer/outflow/callArgument.kt @@ -0,0 +1,10 @@ +// FLOW: OUT +fun foo(n: Int) { + val y = n +} + +fun test() { + val x = 1 + + foo(x) +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/callArgument.results.txt b/idea/testData/slicer/outflow/callArgument.results.txt new file mode 100644 index 00000000000..beaeb0b4ccd --- /dev/null +++ b/idea/testData/slicer/outflow/callArgument.results.txt @@ -0,0 +1,4 @@ +7 val x = 1 +7 val x = 1 +2 fun foo(n: Int) { +3 val y = n diff --git a/idea/testData/slicer/outflow/cast.kt b/idea/testData/slicer/outflow/cast.kt new file mode 100644 index 00000000000..8b3d809b3a3 --- /dev/null +++ b/idea/testData/slicer/outflow/cast.kt @@ -0,0 +1,6 @@ +// FLOW: OUT + +fun test(o: Any) { + val x = o as String + val y = o as? String +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/cast.results.txt b/idea/testData/slicer/outflow/cast.results.txt new file mode 100644 index 00000000000..bdd23178117 --- /dev/null +++ b/idea/testData/slicer/outflow/cast.results.txt @@ -0,0 +1,5 @@ +3 fun test(o: Any) { +4 val x = o as String +4 val x = o as String +5 val y = o as? String +5 val y = o as? String diff --git a/idea/testData/slicer/outflow/funBodyExpression.kt b/idea/testData/slicer/outflow/funBodyExpression.kt new file mode 100644 index 00000000000..6cc6175d53c --- /dev/null +++ b/idea/testData/slicer/outflow/funBodyExpression.kt @@ -0,0 +1,7 @@ +// FLOW: OUT + +fun foo(n: Int): Int = n + +fun test() { + val x = foo(1) +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/funBodyExpression.results.txt b/idea/testData/slicer/outflow/funBodyExpression.results.txt new file mode 100644 index 00000000000..14dad29430b --- /dev/null +++ b/idea/testData/slicer/outflow/funBodyExpression.results.txt @@ -0,0 +1,4 @@ +3 fun foo(n: Int): Int = n +3 fun foo(n: Int): Int = n +6 val x = foo(1) +6 val x = foo(1) diff --git a/idea/testData/slicer/outflow/funParameterUsages.kt b/idea/testData/slicer/outflow/funParameterUsages.kt new file mode 100644 index 00000000000..55a8929e8c3 --- /dev/null +++ b/idea/testData/slicer/outflow/funParameterUsages.kt @@ -0,0 +1,14 @@ +// FLOW: OUT + +fun foo(n: Int) { + val x = n + + val y: Int + y = n + + bar(n) +} + +fun bar(m: Int) { + +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/funParameterUsages.results.txt b/idea/testData/slicer/outflow/funParameterUsages.results.txt new file mode 100644 index 00000000000..1234cc92deb --- /dev/null +++ b/idea/testData/slicer/outflow/funParameterUsages.results.txt @@ -0,0 +1,4 @@ +3 fun foo(n: Int) { +4 val x = n +6 val y: Int +12 fun bar(m: Int) { diff --git a/idea/testData/slicer/outflow/funReturnExpression.kt b/idea/testData/slicer/outflow/funReturnExpression.kt new file mode 100644 index 00000000000..8f27a710a15 --- /dev/null +++ b/idea/testData/slicer/outflow/funReturnExpression.kt @@ -0,0 +1,9 @@ +// FLOW: OUT + +fun foo(n: Int): Int { + return n +} + +fun test() { + val x = foo(1) +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/funReturnExpression.results.txt b/idea/testData/slicer/outflow/funReturnExpression.results.txt new file mode 100644 index 00000000000..26af5d4593e --- /dev/null +++ b/idea/testData/slicer/outflow/funReturnExpression.results.txt @@ -0,0 +1,4 @@ +4 return n +3 fun foo(n: Int): Int { +8 val x = foo(1) +8 val x = foo(1) diff --git a/idea/testData/slicer/outflow/functionCalls.kt b/idea/testData/slicer/outflow/functionCalls.kt new file mode 100644 index 00000000000..599a0afeb8c --- /dev/null +++ b/idea/testData/slicer/outflow/functionCalls.kt @@ -0,0 +1,9 @@ +// FLOW: OUT + +fun foo(n: Int) { + +} + +fun test(m: Int) { + val x = foo(1) +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/functionCalls.results.txt b/idea/testData/slicer/outflow/functionCalls.results.txt new file mode 100644 index 00000000000..73ede156dbf --- /dev/null +++ b/idea/testData/slicer/outflow/functionCalls.results.txt @@ -0,0 +1,3 @@ +3 fun foo(n: Int) { +8 val x = foo(1) +8 val x = foo(1) diff --git a/idea/testData/slicer/outflow/getFunCalls.kt b/idea/testData/slicer/outflow/getFunCalls.kt new file mode 100644 index 00000000000..bf89dfd568b --- /dev/null +++ b/idea/testData/slicer/outflow/getFunCalls.kt @@ -0,0 +1,9 @@ +// FLOW: OUT + +class A { + operator fun get(n: Int) = this +} + +fun test() { + val x = A()[2] +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/getFunCalls.results.txt b/idea/testData/slicer/outflow/getFunCalls.results.txt new file mode 100644 index 00000000000..0f73e0e67a0 --- /dev/null +++ b/idea/testData/slicer/outflow/getFunCalls.results.txt @@ -0,0 +1,3 @@ +4 operator fun get(n: Int) = this +8 val x = A()[2] +8 val x = A()[2] diff --git a/idea/testData/slicer/outflow/ifExpression.kt b/idea/testData/slicer/outflow/ifExpression.kt new file mode 100644 index 00000000000..819c6a382ed --- /dev/null +++ b/idea/testData/slicer/outflow/ifExpression.kt @@ -0,0 +1,5 @@ +// FLOW: OUT + +fun test(m: Int, n: Int) { + val x = if (m > 1) n else 1 +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/ifExpression.results.txt b/idea/testData/slicer/outflow/ifExpression.results.txt new file mode 100644 index 00000000000..5c41ffb9552 --- /dev/null +++ b/idea/testData/slicer/outflow/ifExpression.results.txt @@ -0,0 +1,2 @@ +3 fun test(m: Int, n: Int) { +4 val x = if (m > 1) n else 1 diff --git a/idea/testData/slicer/outflow/localVariableUsages.kt b/idea/testData/slicer/outflow/localVariableUsages.kt new file mode 100644 index 00000000000..c5678cba1c2 --- /dev/null +++ b/idea/testData/slicer/outflow/localVariableUsages.kt @@ -0,0 +1,14 @@ +// FLOW: OUT +fun foo(n: Int) { +} + +fun test() { + val x = 1 + + val y = x + + val z: Int + z = x + + foo(x) +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/localVariableUsages.results.txt b/idea/testData/slicer/outflow/localVariableUsages.results.txt new file mode 100644 index 00000000000..5ce6580cad3 --- /dev/null +++ b/idea/testData/slicer/outflow/localVariableUsages.results.txt @@ -0,0 +1,4 @@ +6 val x = 1 +8 val y = x +10 val z: Int +2 fun foo(n: Int) { diff --git a/idea/testData/slicer/outflow/memberPropertyUsages.kt b/idea/testData/slicer/outflow/memberPropertyUsages.kt new file mode 100644 index 00000000000..0d3f9dd86d9 --- /dev/null +++ b/idea/testData/slicer/outflow/memberPropertyUsages.kt @@ -0,0 +1,19 @@ +// FLOW: OUT + +class A { + val x = 1 + + val y = x + + val z: Int + + init { + z = x + + bar(x) + } + + fun bar(m: Int) { + + } +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/memberPropertyUsages.results.txt b/idea/testData/slicer/outflow/memberPropertyUsages.results.txt new file mode 100644 index 00000000000..c01e151d4b2 --- /dev/null +++ b/idea/testData/slicer/outflow/memberPropertyUsages.results.txt @@ -0,0 +1,4 @@ +4 val x = 1 +6 val y = x +8 val z: Int +16 fun bar(m: Int) { diff --git a/idea/testData/slicer/outflow/notNullAssertion.kt b/idea/testData/slicer/outflow/notNullAssertion.kt new file mode 100644 index 00000000000..507a6fc268d --- /dev/null +++ b/idea/testData/slicer/outflow/notNullAssertion.kt @@ -0,0 +1,5 @@ +// FLOW: OUT + +fun test(s: String?) { + val x = s!! +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/notNullAssertion.results.txt b/idea/testData/slicer/outflow/notNullAssertion.results.txt new file mode 100644 index 00000000000..f79d0917c28 --- /dev/null +++ b/idea/testData/slicer/outflow/notNullAssertion.results.txt @@ -0,0 +1,3 @@ +3 fun test(s: String?) { +4 val x = s!! +4 val x = s!! diff --git a/idea/testData/slicer/outflow/operatorFunCalls.kt b/idea/testData/slicer/outflow/operatorFunCalls.kt new file mode 100644 index 00000000000..278fe4b6133 --- /dev/null +++ b/idea/testData/slicer/outflow/operatorFunCalls.kt @@ -0,0 +1,9 @@ +// FLOW: OUT + +class A { + operator fun plus(n: Int) = this +} + +fun test() { + val x = A() + 2 +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/operatorFunCalls.results.txt b/idea/testData/slicer/outflow/operatorFunCalls.results.txt new file mode 100644 index 00000000000..e7599c59431 --- /dev/null +++ b/idea/testData/slicer/outflow/operatorFunCalls.results.txt @@ -0,0 +1,3 @@ +4 operator fun plus(n: Int) = this +8 val x = A() + 2 +8 val x = A() + 2 diff --git a/idea/testData/slicer/outflow/primaryConstructorCalls.kt b/idea/testData/slicer/outflow/primaryConstructorCalls.kt new file mode 100644 index 00000000000..29f68ff347e --- /dev/null +++ b/idea/testData/slicer/outflow/primaryConstructorCalls.kt @@ -0,0 +1,15 @@ +// FLOW: OUT + +open class A(n: Int) { + constructor() : this(1) +} + +class B : A(1) + +class C : A { + constructor() : super(1) +} + +fun test() { + val x = A(1) +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/primaryConstructorCalls.results.txt b/idea/testData/slicer/outflow/primaryConstructorCalls.results.txt new file mode 100644 index 00000000000..f2904bfa19d --- /dev/null +++ b/idea/testData/slicer/outflow/primaryConstructorCalls.results.txt @@ -0,0 +1,6 @@ +3 open class A(n: Int) { +7 class B : A(1) +14 val x = A(1) +14 val x = A(1) +4 constructor() : this(1) +10 constructor() : super(1) diff --git a/idea/testData/slicer/outflow/primaryConstructorParameterUsages.kt b/idea/testData/slicer/outflow/primaryConstructorParameterUsages.kt new file mode 100644 index 00000000000..01e67c7e4b9 --- /dev/null +++ b/idea/testData/slicer/outflow/primaryConstructorParameterUsages.kt @@ -0,0 +1,17 @@ +// FLOW: OUT + +class A(n: Int) { + val x = n + + val y: Int + + init { + y = n + + bar(n) + } + + fun bar(m: Int) { + + } +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/primaryConstructorParameterUsages.results.txt b/idea/testData/slicer/outflow/primaryConstructorParameterUsages.results.txt new file mode 100644 index 00000000000..3b09a51ee65 --- /dev/null +++ b/idea/testData/slicer/outflow/primaryConstructorParameterUsages.results.txt @@ -0,0 +1,4 @@ +3 class A(n: Int) { +4 val x = n +6 val y: Int +14 fun bar(m: Int) { diff --git a/idea/testData/slicer/outflow/secondaryConstructorCalls.kt b/idea/testData/slicer/outflow/secondaryConstructorCalls.kt new file mode 100644 index 00000000000..6cb6ce3eaab --- /dev/null +++ b/idea/testData/slicer/outflow/secondaryConstructorCalls.kt @@ -0,0 +1,17 @@ +// FLOW: OUT + +open class A { + constructor(n: Int) + + constructor() : this(1) +} + +class B : A(1) + +class C : A { + constructor() : super(1) +} + +fun test() { + val x = A(1) +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/secondaryConstructorCalls.results.txt b/idea/testData/slicer/outflow/secondaryConstructorCalls.results.txt new file mode 100644 index 00000000000..d22eccb6972 --- /dev/null +++ b/idea/testData/slicer/outflow/secondaryConstructorCalls.results.txt @@ -0,0 +1,6 @@ +4 constructor(n: Int) +9 class B : A(1) +16 val x = A(1) +16 val x = A(1) +6 constructor() : this(1) +12 constructor() : super(1) diff --git a/idea/testData/slicer/outflow/topLevelPropertyUsages.kt b/idea/testData/slicer/outflow/topLevelPropertyUsages.kt new file mode 100644 index 00000000000..4b4415b421b --- /dev/null +++ b/idea/testData/slicer/outflow/topLevelPropertyUsages.kt @@ -0,0 +1,21 @@ +// FLOW: OUT + +val x = 1 + +val y = x + +fun test() { + val y = x + + val z: Int + + init { + z = x + + bar(x) + } +} + +fun bar(m: Int) { + +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/topLevelPropertyUsages.results.txt b/idea/testData/slicer/outflow/topLevelPropertyUsages.results.txt new file mode 100644 index 00000000000..87c304cf28a --- /dev/null +++ b/idea/testData/slicer/outflow/topLevelPropertyUsages.results.txt @@ -0,0 +1,5 @@ +3 val x = 1 +5 val y = x +8 val y = x +10 val z: Int +19 fun bar(m: Int) { diff --git a/idea/testData/slicer/outflow/whenExpression.kt b/idea/testData/slicer/outflow/whenExpression.kt new file mode 100644 index 00000000000..2ac47440811 --- /dev/null +++ b/idea/testData/slicer/outflow/whenExpression.kt @@ -0,0 +1,9 @@ +// FLOW: OUT + +fun test(m: Int, n: Int) { + val x = when (m) { + 1 -> 1 + 2 -> n + else -> 0 + } +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/whenExpression.results.txt b/idea/testData/slicer/outflow/whenExpression.results.txt new file mode 100644 index 00000000000..53f7c7dada2 --- /dev/null +++ b/idea/testData/slicer/outflow/whenExpression.results.txt @@ -0,0 +1,2 @@ +3 fun test(m: Int, n: Int) { +4 val x = when (m) { diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/AbstractSlicerTest.kt b/idea/tests/org/jetbrains/kotlin/idea/slicer/AbstractSlicerTest.kt new file mode 100644 index 00000000000..0ac64381702 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/AbstractSlicerTest.kt @@ -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 { + 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() + + fun TextChunk.render(): String { + var text = text + if (attributes.fontType == Font.BOLD) { + text = "$text" + } + 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(""), "") + } + + 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)) + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java new file mode 100644 index 00000000000..ce6a4737e23 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java @@ -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); + } +}