[groovy] dfa: allow to analyze only instructions reachable from root

After 'for' updates (IDEA-188433) classic 'for' variables are no longer parameters and they are not treated as
initialized by default, thus making GrUnassignedVariableAccessTest#testForLoopWithNestedEndlessLoop fail:
```
for (int x = 1; x < 10; x++) {
  for (;;) {}
}
```
`x++` in the above code became highlighted as read-before-write warning despite it being unreachable from root. Skipping
such unreachable instructions in read-before-write analysis helps to avoid introducing unnecessary warnings.
This commit is contained in:
Daniil Ovchinnikov
2018-07-03 22:13:05 +03:00
parent 0a39033363
commit f0a5659981
5 changed files with 38 additions and 88 deletions
@@ -1,37 +1,29 @@
/*
* Copyright 2000-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.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
@file:JvmName("OrderUtil")
package org.jetbrains.plugins.groovy.lang.psi.controlFlow
import com.intellij.util.ArrayUtil.EMPTY_INT_ARRAY
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.InstructionImpl
import java.util.*
private val fakeRoot = InstructionImpl(null)
fun reversedPostOrder(flow: Array<Instruction>): IntArray = postOrder(flow).reversedArray()
@JvmOverloads
fun reversedPostOrder(flow: Array<Instruction>, reachable: Boolean = false): IntArray = postOrder(flow, reachable).reversedArray()
fun postOrder(flow: Array<Instruction>): IntArray {
val N = flow.size
val result = IntArray(N)
fun postOrder(flow: Array<Instruction>, reachable: Boolean): IntArray {
val n = flow.size
if (n == 0) return EMPTY_INT_ARRAY
val result = IntArray(n) { -1 }
var resultIndex = 0
val visited = BooleanArray(N)
val visited = BooleanArray(n)
val stack: Deque<Pair<Instruction, Iterator<Instruction>>> = LinkedList()
stack.push(fakeRoot to flow.iterator())
val rootIterator = if (reachable) listOf(flow[0]).iterator() else flow.iterator()
stack.push(fakeRoot to rootIterator)
while (!stack.isEmpty()) {
val (instruction, iterator) = stack.peek()
@@ -51,8 +43,13 @@ fun postOrder(flow: Array<Instruction>): IntArray {
}
}
assert(resultIndex == N)
return result
if (reachable) {
assert(resultIndex <= n)
}
else {
assert(resultIndex == n)
}
return if (resultIndex == n) result else result.take(resultIndex).toIntArray()
}
private inline fun <T> Iterator<T>.firstOrNull(predicate: (T) -> Boolean): T? {
@@ -1,18 +1,4 @@
/*
* Copyright 2000-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.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.groovy.lang.psi.dataFlow;
import com.intellij.openapi.progress.ProgressManager;
@@ -84,7 +70,7 @@ public class DFAEngine<E> {
final List<E> info = new ArrayList<>(Collections.nCopies(n, myDfa.initial()));
final CallEnvironment env = new MyCallEnvironment(n);
final WorkList workList = new WorkList(getFlowOrder());
final WorkList workList = new WorkList(n, getFlowOrder());
while (!workList.isEmpty()) {
ProgressManager.checkCanceled();
@@ -108,10 +94,10 @@ public class DFAEngine<E> {
@NotNull
private int[] getFlowOrder() {
if (myDfa.isForward()) {
return reversedPostOrder(myFlow);
return reversedPostOrder(myFlow, myDfa.isReachable());
}
else {
return postOrder(myFlow);
return postOrder(myFlow, myDfa.isReachable());
}
}
@@ -1,18 +1,4 @@
/*
* Copyright 2000-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.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.groovy.lang.psi.dataFlow;
import org.jetbrains.annotations.NotNull;
@@ -31,4 +17,11 @@ public interface DfaInstance<E> {
default boolean isForward() {
return true;
}
/**
* @return {@code true} if this instance expects only instructions reachable from root
*/
default boolean isReachable() {
return false;
}
}
@@ -1,23 +1,9 @@
/*
* Copyright 2000-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.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.groovy.lang.psi.dataFlow
import java.util.*
internal class WorkList(order: IntArray) {
internal class WorkList(flowSize: Int, order: IntArray) {
private val mySize: Int = order.size
/**
@@ -27,7 +13,7 @@ internal class WorkList(order: IntArray) {
/**
* Mapping: instruction number -> index in [myOrder] array
*/
private val myInstructionToOrder: IntArray = IntArray(mySize)
private val myInstructionToOrder: IntArray = IntArray(flowSize)
/**
* Mapping: index -> whether instruction number needs to be processed
*
@@ -1,18 +1,4 @@
/*
* Copyright 2000-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.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.groovy.lang.psi.dataFlow.readWrite
import gnu.trove.TObjectIntHashMap
@@ -41,4 +27,6 @@ class ReadBeforeWriteInstance(val nameIndex: TObjectIntHashMap<String>, val only
}
override fun initial(): ReadBeforeWriteState = ReadBeforeWriteState.bottom
override fun isReachable(): Boolean = true
}