Optimize CFG for cases of simple variables
Parameters/vals with an immediate initializer (which we assume is a rather common situation) do not require any kind of complicated CFA - Unused vals can be simply determined by linear traversal of the pseudocode - Definite assignment is a bit more complicated: a read-instruction of val can be considered as a safe if it's located *after* the first write in the pseudocode. It works almost always beside the case with do/while (see the test changed). This case will be fixed in the further commits The test for kt897.kt will also be fixed further, all other changes might be considered as minor as they mostly change diagnostics for already red code
This commit is contained in:
@@ -26,16 +26,35 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.WriteValueInstructi
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.VariableDeclarationInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.Edges
|
||||
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.VariableDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingContextUtils.variableDescriptorForDeclaration
|
||||
import java.util.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
private typealias ImmutableSet<T> = javaslang.collection.Set<T>
|
||||
private typealias ImmutableHashSet<T> = javaslang.collection.HashSet<T>
|
||||
|
||||
class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingContext: BindingContext) {
|
||||
private val pseudocodeVariableDataCollector = PseudocodeVariableDataCollector(bindingContext, pseudocode)
|
||||
|
||||
private val declaredVariablesForDeclaration = hashMapOf<Pseudocode, Set<VariableDescriptor>>()
|
||||
private class VariablesForDeclaration(
|
||||
val valsWithTrivialInitializer: Set<VariableDescriptor>,
|
||||
val nonTrivialVariables: Set<VariableDescriptor>
|
||||
) {
|
||||
val allVars =
|
||||
if (nonTrivialVariables.isEmpty())
|
||||
valsWithTrivialInitializer
|
||||
else
|
||||
LinkedHashSet(valsWithTrivialInitializer).also { it.addAll(nonTrivialVariables) }
|
||||
}
|
||||
|
||||
private val declaredVariablesForDeclaration = hashMapOf<Pseudocode, VariablesForDeclaration>()
|
||||
private val rootVariables by lazy(LazyThreadSafetyMode.NONE) {
|
||||
getAllDeclaredVariables(pseudocode, includeInsideLocalDeclarations = true)
|
||||
}
|
||||
|
||||
val variableInitializers: Map<Instruction, Edges<ReadOnlyInitControlFlowInfo>> by lazy {
|
||||
computeVariableInitializers()
|
||||
@@ -44,49 +63,87 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
|
||||
val blockScopeVariableInfo: BlockScopeVariableInfo
|
||||
get() = pseudocodeVariableDataCollector.blockScopeVariableInfo
|
||||
|
||||
fun getDeclaredVariables(pseudocode: Pseudocode, includeInsideLocalDeclarations: Boolean): Set<VariableDescriptor> {
|
||||
fun getDeclaredVariables(pseudocode: Pseudocode, includeInsideLocalDeclarations: Boolean): Set<VariableDescriptor> =
|
||||
getAllDeclaredVariables(pseudocode, includeInsideLocalDeclarations).allVars
|
||||
|
||||
private fun getAllDeclaredVariables(pseudocode: Pseudocode, includeInsideLocalDeclarations: Boolean): VariablesForDeclaration {
|
||||
if (!includeInsideLocalDeclarations) {
|
||||
return getUpperLevelDeclaredVariables(pseudocode)
|
||||
}
|
||||
val declaredVariables = linkedSetOf<VariableDescriptor>()
|
||||
declaredVariables.addAll(getUpperLevelDeclaredVariables(pseudocode))
|
||||
val nonTrivialVariables = linkedSetOf<VariableDescriptor>()
|
||||
val valsWithTrivialInitializer = linkedSetOf<VariableDescriptor>()
|
||||
addVariablesFromPseudocode(pseudocode, nonTrivialVariables, valsWithTrivialInitializer)
|
||||
|
||||
for (localFunctionDeclarationInstruction in pseudocode.localDeclarations) {
|
||||
val localPseudocode = localFunctionDeclarationInstruction.body
|
||||
declaredVariables.addAll(getUpperLevelDeclaredVariables(localPseudocode))
|
||||
addVariablesFromPseudocode(localPseudocode, nonTrivialVariables, valsWithTrivialInitializer)
|
||||
}
|
||||
return declaredVariables
|
||||
return VariablesForDeclaration(valsWithTrivialInitializer, nonTrivialVariables)
|
||||
}
|
||||
|
||||
private fun getUpperLevelDeclaredVariables(pseudocode: Pseudocode): Set<VariableDescriptor> {
|
||||
var declaredVariables = declaredVariablesForDeclaration[pseudocode]
|
||||
if (declaredVariables == null) {
|
||||
declaredVariables = computeDeclaredVariablesForPseudocode(pseudocode)
|
||||
declaredVariablesForDeclaration.put(pseudocode, declaredVariables)
|
||||
private fun addVariablesFromPseudocode(
|
||||
pseudocode: Pseudocode,
|
||||
nonTrivialVariables: MutableSet<VariableDescriptor>,
|
||||
valsWithTrivialInitializer: MutableSet<VariableDescriptor>
|
||||
) {
|
||||
getUpperLevelDeclaredVariables(pseudocode).let {
|
||||
nonTrivialVariables.addAll(it.nonTrivialVariables)
|
||||
valsWithTrivialInitializer.addAll(it.valsWithTrivialInitializer)
|
||||
}
|
||||
return declaredVariables
|
||||
}
|
||||
|
||||
private fun computeDeclaredVariablesForPseudocode(pseudocode: Pseudocode): Set<VariableDescriptor> {
|
||||
val declaredVariables = linkedSetOf<VariableDescriptor>()
|
||||
private fun getUpperLevelDeclaredVariables(pseudocode: Pseudocode) = declaredVariablesForDeclaration.getOrPut(pseudocode) {
|
||||
computeDeclaredVariablesForPseudocode(pseudocode)
|
||||
}
|
||||
|
||||
private fun computeDeclaredVariablesForPseudocode(pseudocode: Pseudocode): VariablesForDeclaration {
|
||||
val valsWithTrivialInitializer = linkedSetOf<VariableDescriptor>()
|
||||
val nonTrivialVariables = linkedSetOf<VariableDescriptor>()
|
||||
for (instruction in pseudocode.instructions) {
|
||||
if (instruction is VariableDeclarationInstruction) {
|
||||
val variableDeclarationElement = instruction.variableDeclarationElement
|
||||
val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement)
|
||||
variableDescriptorForDeclaration(descriptor)?.let {
|
||||
declaredVariables.add(it)
|
||||
val descriptor =
|
||||
variableDescriptorForDeclaration(
|
||||
bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement)
|
||||
) ?: continue
|
||||
|
||||
if (isValWithTrivialInitializer(variableDeclarationElement, descriptor)) {
|
||||
valsWithTrivialInitializer.add(descriptor)
|
||||
}
|
||||
else {
|
||||
nonTrivialVariables.add(descriptor)
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableSet(declaredVariables)
|
||||
|
||||
return VariablesForDeclaration(valsWithTrivialInitializer, nonTrivialVariables)
|
||||
}
|
||||
|
||||
private fun isValWithTrivialInitializer(variableDeclarationElement: KtDeclaration, descriptor: VariableDescriptor) =
|
||||
variableDeclarationElement is KtParameter || variableDeclarationElement is KtObjectDeclaration ||
|
||||
variableDeclarationElement.safeAs<KtVariableDeclaration>()?.isVariableWithTrivialInitializer(descriptor) == true
|
||||
|
||||
private fun KtVariableDeclaration.isVariableWithTrivialInitializer(descriptor: VariableDescriptor): Boolean {
|
||||
if (descriptor.isPropertyWithoutBackingField()) return true
|
||||
if (isVar) return false
|
||||
return initializer != null || safeAs<KtProperty>()?.delegate != null || this is KtDestructuringDeclarationEntry
|
||||
}
|
||||
|
||||
private fun VariableDescriptor.isPropertyWithoutBackingField(): Boolean {
|
||||
if (this !is PropertyDescriptor) return false
|
||||
return bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, this) != true
|
||||
}
|
||||
|
||||
// variable initializers
|
||||
|
||||
private fun computeVariableInitializers(): Map<Instruction, Edges<InitControlFlowInfo>> {
|
||||
private fun computeVariableInitializers(): Map<Instruction, Edges<ReadOnlyInitControlFlowInfo>> {
|
||||
|
||||
val blockScopeVariableInfo = pseudocodeVariableDataCollector.blockScopeVariableInfo
|
||||
|
||||
val resultForValsWithTrivialInitializer = computeInitInfoForTrivialVals()
|
||||
|
||||
if (rootVariables.nonTrivialVariables.isEmpty()) return resultForValsWithTrivialInitializer
|
||||
|
||||
return pseudocodeVariableDataCollector.collectData(TraversalOrder.FORWARD, InitControlFlowInfo()) {
|
||||
instruction: Instruction, incomingEdgesData: Collection<InitControlFlowInfo> ->
|
||||
|
||||
@@ -94,6 +151,88 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
|
||||
val exitInstructionData = addVariableInitStateFromCurrentInstructionIfAny(
|
||||
instruction, enterInstructionData, blockScopeVariableInfo)
|
||||
Edges(enterInstructionData, exitInstructionData)
|
||||
}.mapValues {
|
||||
(instruction, edges) ->
|
||||
val trivialEdges = resultForValsWithTrivialInitializer[instruction]!!
|
||||
Edges(trivialEdges.incoming.replaceDelegate(edges.incoming), trivialEdges.outgoing.replaceDelegate(edges.outgoing))
|
||||
}
|
||||
}
|
||||
|
||||
private fun computeInitInfoForTrivialVals(): Map<Instruction, Edges<ReadOnlyInitControlFlowInfoImpl>> {
|
||||
val result = hashMapOf<Instruction, Edges<ReadOnlyInitControlFlowInfoImpl>>()
|
||||
var declaredSet = ImmutableHashSet.empty<VariableDescriptor>()
|
||||
var initSet = ImmutableHashSet.empty<VariableDescriptor>()
|
||||
pseudocode.traverse(TraversalOrder.FORWARD) { instruction ->
|
||||
val enterState = ReadOnlyInitControlFlowInfoImpl(declaredSet, initSet, null)
|
||||
when (instruction) {
|
||||
is VariableDeclarationInstruction ->
|
||||
extractValWithTrivialInitializer(instruction)?.let {
|
||||
variableDescriptor ->
|
||||
declaredSet = declaredSet.add(variableDescriptor)
|
||||
}
|
||||
is WriteValueInstruction -> {
|
||||
val variableDescriptor = extractValWithTrivialInitializer(instruction)
|
||||
if (variableDescriptor != null && instruction.isTrivialInitializer()) {
|
||||
initSet = initSet.add(variableDescriptor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val afterState = ReadOnlyInitControlFlowInfoImpl(declaredSet, initSet, null)
|
||||
|
||||
result[instruction] = Edges(enterState, afterState)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun WriteValueInstruction.isTrivialInitializer() =
|
||||
element is KtVariableDeclaration || element is KtParameter
|
||||
|
||||
private inner class ReadOnlyInitControlFlowInfoImpl(
|
||||
val declaredSet: ImmutableSet<VariableDescriptor>,
|
||||
val initSet: ImmutableSet<VariableDescriptor>,
|
||||
private val delegate: ReadOnlyInitControlFlowInfo?
|
||||
) : ReadOnlyInitControlFlowInfo {
|
||||
override fun getOrNull(variableDescriptor: VariableDescriptor): VariableControlFlowState? {
|
||||
if (variableDescriptor in declaredSet) {
|
||||
return VariableControlFlowState.create(isInitialized = variableDescriptor in initSet, isDeclared = true)
|
||||
}
|
||||
return delegate?.getOrNull(variableDescriptor)
|
||||
}
|
||||
|
||||
override fun checkDefiniteInitializationInWhen(merge: ReadOnlyInitControlFlowInfo): Boolean =
|
||||
delegate?.checkDefiniteInitializationInWhen(merge) ?: false
|
||||
|
||||
fun replaceDelegate(newDelegate: ReadOnlyInitControlFlowInfo): ReadOnlyInitControlFlowInfo =
|
||||
ReadOnlyInitControlFlowInfoImpl(declaredSet, initSet, newDelegate)
|
||||
|
||||
override fun asMap(): ImmutableMap<VariableDescriptor, VariableControlFlowState> {
|
||||
val initial = delegate?.asMap() ?: ImmutableHashMap.empty()
|
||||
|
||||
return declaredSet.fold(initial) {
|
||||
acc, variableDescriptor ->
|
||||
acc.put(variableDescriptor, getOrNull(variableDescriptor)!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as ReadOnlyInitControlFlowInfoImpl
|
||||
|
||||
if (declaredSet != other.declaredSet) return false
|
||||
if (initSet != other.initSet) return false
|
||||
if (delegate != other.delegate) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = declaredSet.hashCode()
|
||||
result = 31 * result + initSet.hashCode()
|
||||
result = 31 * result + (delegate?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +254,10 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
|
||||
if (instruction !is WriteValueInstruction && instruction !is VariableDeclarationInstruction) {
|
||||
return enterInstructionData
|
||||
}
|
||||
val variable = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, bindingContext) ?: return enterInstructionData
|
||||
val variable =
|
||||
PseudocodeUtil.extractVariableDescriptorIfAny(instruction, bindingContext)
|
||||
?.takeIf { it in rootVariables.nonTrivialVariables }
|
||||
?: return enterInstructionData
|
||||
var exitInstructionData = enterInstructionData
|
||||
if (instruction is WriteValueInstruction) {
|
||||
// if writing to already initialized object
|
||||
@@ -129,10 +271,10 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
|
||||
}
|
||||
else {
|
||||
// instruction instanceof VariableDeclarationInstruction
|
||||
var enterInitState: VariableControlFlowState? = enterInstructionData.getOrNull(variable)
|
||||
if (enterInitState == null) {
|
||||
enterInitState = getDefaultValueForInitializers(variable, instruction, blockScopeVariableInfo)
|
||||
}
|
||||
val enterInitState =
|
||||
enterInstructionData.getOrNull(variable)
|
||||
?: getDefaultValueForInitializers(variable, instruction, blockScopeVariableInfo)
|
||||
|
||||
if (!enterInitState.mayBeInitialized() || !enterInitState.isDeclared) {
|
||||
val variableDeclarationInfo = VariableControlFlowState.create(enterInitState.initState, isDeclared = true)
|
||||
exitInstructionData = exitInstructionData.put(variable, variableDeclarationInfo, enterInitState)
|
||||
@@ -144,45 +286,127 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
|
||||
// variable use
|
||||
|
||||
val variableUseStatusData: Map<Instruction, Edges<ReadOnlyUseControlFlowInfo>>
|
||||
get() = pseudocodeVariableDataCollector.collectData(TraversalOrder.BACKWARD, UseControlFlowInfo()) {
|
||||
instruction: Instruction, incomingEdgesData: Collection<UseControlFlowInfo> ->
|
||||
get() {
|
||||
val resultForTrivialVals = computeUseInfoForTrivialVals()
|
||||
if (rootVariables.nonTrivialVariables.isEmpty()) return resultForTrivialVals
|
||||
|
||||
val enterResult: UseControlFlowInfo = if (incomingEdgesData.size == 1) {
|
||||
incomingEdgesData.single()
|
||||
}
|
||||
else {
|
||||
incomingEdgesData.fold(UseControlFlowInfo()) { result, edgeData ->
|
||||
edgeData.iterator().fold(result) { subResult, (variableDescriptor, variableUseState) ->
|
||||
subResult.put(variableDescriptor, variableUseState.merge(subResult.getOrNull(variableDescriptor)))
|
||||
return pseudocodeVariableDataCollector.collectData(TraversalOrder.BACKWARD, UseControlFlowInfo()) { instruction: Instruction, incomingEdgesData: Collection<UseControlFlowInfo> ->
|
||||
|
||||
val enterResult: UseControlFlowInfo = if (incomingEdgesData.size == 1) {
|
||||
incomingEdgesData.single()
|
||||
}
|
||||
else {
|
||||
incomingEdgesData.fold(UseControlFlowInfo()) { result, edgeData ->
|
||||
edgeData.iterator().fold(result) { subResult, (variableDescriptor, variableUseState) ->
|
||||
subResult.put(variableDescriptor, variableUseState.merge(subResult.getOrNull(variableDescriptor)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val variableDescriptor = PseudocodeUtil.extractVariableDescriptorFromReference(instruction, bindingContext)
|
||||
if (variableDescriptor == null || instruction !is ReadValueInstruction && instruction !is WriteValueInstruction) {
|
||||
Edges(enterResult, enterResult)
|
||||
}
|
||||
else {
|
||||
val exitResult =
|
||||
if (instruction is ReadValueInstruction) {
|
||||
enterResult.put(variableDescriptor, VariableUseState.READ)
|
||||
}
|
||||
else {
|
||||
var variableUseState: VariableUseState? = enterResult.getOrNull(variableDescriptor)
|
||||
if (variableUseState == null) {
|
||||
variableUseState = VariableUseState.UNUSED
|
||||
}
|
||||
when (variableUseState) {
|
||||
VariableUseState.UNUSED, VariableUseState.ONLY_WRITTEN_NEVER_READ ->
|
||||
enterResult.put(variableDescriptor, VariableUseState.ONLY_WRITTEN_NEVER_READ)
|
||||
VariableUseState.WRITTEN_AFTER_READ, VariableUseState.READ ->
|
||||
enterResult.put(variableDescriptor, VariableUseState.WRITTEN_AFTER_READ)
|
||||
}
|
||||
}
|
||||
Edges(enterResult, exitResult)
|
||||
val variableDescriptor =
|
||||
PseudocodeUtil.extractVariableDescriptorFromReference(instruction, bindingContext)
|
||||
?.takeIf { it in rootVariables.nonTrivialVariables }
|
||||
if (variableDescriptor == null || instruction !is ReadValueInstruction && instruction !is WriteValueInstruction) {
|
||||
Edges(enterResult, enterResult)
|
||||
}
|
||||
else {
|
||||
val exitResult =
|
||||
if (instruction is ReadValueInstruction) {
|
||||
enterResult.put(variableDescriptor, VariableUseState.READ)
|
||||
}
|
||||
else {
|
||||
var variableUseState: VariableUseState? = enterResult.getOrNull(variableDescriptor)
|
||||
if (variableUseState == null) {
|
||||
variableUseState = VariableUseState.UNUSED
|
||||
}
|
||||
when (variableUseState) {
|
||||
VariableUseState.UNUSED, VariableUseState.ONLY_WRITTEN_NEVER_READ ->
|
||||
enterResult.put(variableDescriptor, VariableUseState.ONLY_WRITTEN_NEVER_READ)
|
||||
VariableUseState.WRITTEN_AFTER_READ, VariableUseState.READ ->
|
||||
enterResult.put(variableDescriptor, VariableUseState.WRITTEN_AFTER_READ)
|
||||
}
|
||||
}
|
||||
Edges(enterResult, exitResult)
|
||||
}
|
||||
}.mapValues {
|
||||
(instruction, edges) ->
|
||||
val edgeForTrivialVals = resultForTrivialVals[instruction]!!
|
||||
|
||||
Edges(
|
||||
edgeForTrivialVals.incoming.replaceDelegate(edges.incoming),
|
||||
edgeForTrivialVals.outgoing.replaceDelegate(edges.outgoing)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun computeUseInfoForTrivialVals(): Map<Instruction, Edges<ReadOnlyUseControlFlowInfoImpl>> {
|
||||
val used = hashSetOf<VariableDescriptor>()
|
||||
|
||||
pseudocode.traverse(TraversalOrder.FORWARD) { instruction ->
|
||||
if (instruction is ReadValueInstruction) {
|
||||
extractValWithTrivialInitializer(instruction)?.let {
|
||||
used.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val constantUseInfo = ReadOnlyUseControlFlowInfoImpl(used, null)
|
||||
val constantEdges = Edges(constantUseInfo, constantUseInfo)
|
||||
val result = hashMapOf<Instruction, Edges<ReadOnlyUseControlFlowInfoImpl>>()
|
||||
|
||||
pseudocode.traverse(TraversalOrder.FORWARD) { instruction ->
|
||||
result[instruction] = constantEdges
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun extractValWithTrivialInitializer(instruction: Instruction): VariableDescriptor? {
|
||||
return PseudocodeUtil.extractVariableDescriptorIfAny(instruction, bindingContext)?.takeIf {
|
||||
it in rootVariables.valsWithTrivialInitializer
|
||||
}
|
||||
}
|
||||
|
||||
private inner class ReadOnlyUseControlFlowInfoImpl(
|
||||
val used: Set<VariableDescriptor>,
|
||||
val delegate: ReadOnlyUseControlFlowInfo?
|
||||
) : ReadOnlyUseControlFlowInfo {
|
||||
override fun getOrNull(variableDescriptor: VariableDescriptor): VariableUseState? {
|
||||
if (variableDescriptor in used) return VariableUseState.READ
|
||||
return delegate?.getOrNull(variableDescriptor)
|
||||
}
|
||||
|
||||
fun replaceDelegate(newDelegate: ReadOnlyUseControlFlowInfo): ReadOnlyUseControlFlowInfo =
|
||||
ReadOnlyUseControlFlowInfoImpl(used, newDelegate)
|
||||
|
||||
override fun asMap(): ImmutableMap<VariableDescriptor, VariableUseState> {
|
||||
val initial = delegate?.asMap() ?: ImmutableHashMap.empty()
|
||||
|
||||
return used.fold(initial) {
|
||||
acc, variableDescriptor ->
|
||||
acc.put(variableDescriptor, getOrNull(variableDescriptor)!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as ReadOnlyUseControlFlowInfoImpl
|
||||
|
||||
if (used != other.used) return false
|
||||
if (delegate != other.delegate) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = used.hashCode()
|
||||
result = 31 * result + (delegate?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
|
||||
@@ -6,4 +6,4 @@ abstract class Abst {
|
||||
abstract fun foo(x: Int = <!UNINITIALIZED_PARAMETER!>y<!>, y: Int = x)
|
||||
}
|
||||
|
||||
<!NON_MEMBER_FUNCTION_NO_BODY!>fun extraDiagnostics(<!UNUSED_PARAMETER!>x<!>: Int = <!UNINITIALIZED_PARAMETER!>y<!>, <!UNUSED_PARAMETER!>y<!>: Int)<!>
|
||||
<!NON_MEMBER_FUNCTION_NO_BODY!>fun extraDiagnostics(<!UNUSED_PARAMETER!>x<!>: Int = <!UNINITIALIZED_PARAMETER!>y<!>, y: Int)<!>
|
||||
|
||||
@@ -6,10 +6,10 @@ fun bar(x : Int = <!TYPE_MISMATCH!>""<!>, y : Int = x, <!UNUSED_PARAMETER!>z<!>
|
||||
|
||||
// KT-371 Resolve default parameters for constructors
|
||||
|
||||
class A(x : Int = <!UNINITIALIZED_PARAMETER!>y<!>, <!UNUSED_PARAMETER!>y<!> : Int = x) { // None of the references is resolved, no types checked
|
||||
fun foo(<!UNUSED_PARAMETER!>bool<!>: Boolean, a: Int = <!TYPE_MISMATCH, UNINITIALIZED_PARAMETER!>b<!>, <!UNUSED_PARAMETER!>b<!>: String = <!TYPE_MISMATCH!>a<!>) {}
|
||||
class A(x : Int = <!UNINITIALIZED_PARAMETER!>y<!>, y : Int = x) { // None of the references is resolved, no types checked
|
||||
fun foo(<!UNUSED_PARAMETER!>bool<!>: Boolean, a: Int = <!TYPE_MISMATCH, UNINITIALIZED_PARAMETER!>b<!>, b: String = <!TYPE_MISMATCH!>a<!>) {}
|
||||
}
|
||||
|
||||
val z = 3
|
||||
|
||||
fun foo(x: Int = <!UNINITIALIZED_PARAMETER!>y<!>, y: Int = x, <!UNUSED_PARAMETER!>i<!> : Int = z): Int = x + y
|
||||
fun foo(x: Int = <!UNINITIALIZED_PARAMETER!>y<!>, y: Int = x, <!UNUSED_PARAMETER!>i<!> : Int = z): Int = x + y
|
||||
|
||||
+3
-3
@@ -51,7 +51,7 @@ fun cannotBe() {
|
||||
<!VARIABLE_EXPECTED!>5<!> = 34
|
||||
}
|
||||
|
||||
fun canBe(i0: Int, j: Int) {
|
||||
fun canBe(i0: Int, <!UNUSED_PARAMETER!>j<!>: Int) {
|
||||
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>i<!> = i0
|
||||
<!UNUSED_VALUE!>(label@ i) =<!> 34
|
||||
|
||||
@@ -61,7 +61,7 @@ fun canBe(i0: Int, j: Int) {
|
||||
(l@ a.a) = 3894
|
||||
}
|
||||
|
||||
fun canBe2(j: Int) {
|
||||
fun canBe2(<!UNUSED_PARAMETER!>j<!>: Int) {
|
||||
<!UNUSED_VALUE!>(label@ <!VAL_REASSIGNMENT!>j<!>) =<!> 34
|
||||
}
|
||||
|
||||
@@ -140,4 +140,4 @@ fun Array<Int>.checkThis() {
|
||||
|
||||
abstract class Ab {
|
||||
abstract fun getArray() : Array<Int>
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+5
-5
@@ -53,7 +53,7 @@ fun t2() {
|
||||
|
||||
class A() {}
|
||||
|
||||
fun t4(a: A) {
|
||||
fun t4(<!UNUSED_PARAMETER!>a<!>: A) {
|
||||
<!UNUSED_VALUE!><!VAL_REASSIGNMENT!>a<!> =<!> A()
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ fun t4(a: A) {
|
||||
// reassigned vals
|
||||
|
||||
fun t1() {
|
||||
val <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>a<!> : Int = 1
|
||||
val <!UNUSED_VARIABLE!>a<!> : Int = 1
|
||||
<!UNUSED_VALUE!><!VAL_REASSIGNMENT!>a<!> =<!> 2
|
||||
|
||||
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>b<!> : Int = 1
|
||||
@@ -83,7 +83,7 @@ enum class ProtocolState {
|
||||
fun t3() {
|
||||
val x: ProtocolState = ProtocolState.WAITING
|
||||
<!VAL_REASSIGNMENT!>x<!> = x.signal()
|
||||
<!UNUSED_VALUE!>x =<!> x.signal() //repeat for x
|
||||
x = x.signal() //repeat for x
|
||||
}
|
||||
|
||||
fun t4() {
|
||||
@@ -187,7 +187,7 @@ class AnonymousInitializers(var a: String, val b: String) {
|
||||
}
|
||||
}
|
||||
|
||||
fun reassignFunParams(a: Int) {
|
||||
fun reassignFunParams(<!UNUSED_PARAMETER!>a<!>: Int) {
|
||||
<!UNUSED_VALUE!><!VAL_REASSIGNMENT!>a<!> =<!> 1
|
||||
}
|
||||
|
||||
@@ -345,4 +345,4 @@ fun test(m : M) {
|
||||
fun test1(m : M) {
|
||||
<!VAL_REASSIGNMENT!>m.x<!>++
|
||||
m.y--
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,5 +2,5 @@ fun test(cond1: Boolean) {
|
||||
do {
|
||||
if (cond1) continue
|
||||
val cond2 = false
|
||||
} while (<!UNINITIALIZED_VARIABLE!>cond2<!>) // cond2 may be not defined here
|
||||
} while (cond2) // cond2 may be not defined here
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ package kt897
|
||||
|
||||
class A() {
|
||||
init {
|
||||
<!INITIALIZATION_BEFORE_DECLARATION!>i<!> = 11
|
||||
i = 11
|
||||
}
|
||||
val i : Int? = null // must be an error
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ class A {
|
||||
}
|
||||
|
||||
fun foo(list: List<A>) {
|
||||
for (<!VAL_OR_VAR_ON_LOOP_PARAMETER!>var<!> (<!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>c1<!>, <!UNUSED_VARIABLE!>c2<!>, c3) in list) {
|
||||
for (<!VAL_OR_VAR_ON_LOOP_PARAMETER!>var<!> (<!UNUSED_VARIABLE!>c1<!>, <!UNUSED_VARIABLE!>c2<!>, c3) in list) {
|
||||
<!UNUSED_VALUE!><!VAL_REASSIGNMENT!>c1<!> =<!> 1
|
||||
c3 + 1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user