mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
fighting too complex methods: merge states which differ only in unknown variables
This commit is contained in:
+3
-2
@@ -161,10 +161,11 @@ public class DataFlowRunner {
|
||||
|
||||
if (instruction instanceof BranchingInstruction) {
|
||||
BranchingInstruction branching = (BranchingInstruction)instruction;
|
||||
if (processedStates.get(branching).contains(instructionState.getMemoryState())) {
|
||||
Collection<DfaMemoryState> processed = processedStates.get(branching);
|
||||
if (processed.contains(instructionState.getMemoryState())) {
|
||||
continue;
|
||||
}
|
||||
if (processedStates.get(branching).size() > MAX_STATES_PER_BRANCH) {
|
||||
if (processed.size() > MAX_STATES_PER_BRANCH) {
|
||||
LOG.debug("Too complex because too many different possible states");
|
||||
return RunnerResult.TOO_COMPLEX; // Too complex :(
|
||||
}
|
||||
|
||||
+3
@@ -102,6 +102,9 @@ class StateQueue {
|
||||
if (nextStates == null) {
|
||||
nextStates = merger.mergeByType();
|
||||
}
|
||||
if (nextStates == null) {
|
||||
nextStates = merger.mergeByUnknowns();
|
||||
}
|
||||
if (nextStates == null) break;
|
||||
memoryStates = nextStates;
|
||||
}
|
||||
|
||||
+12
-5
@@ -136,11 +136,18 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return (((getNonTrivialEqClasses().hashCode() * 31 +
|
||||
getDistinctClassPairs().hashCode()) * 31 +
|
||||
myStack.hashCode()) * 31 +
|
||||
myUnknownVariables.hashCode()) * 31 +
|
||||
myVariableStates.hashCode();
|
||||
return getPartialHashCode(true);
|
||||
}
|
||||
|
||||
int getPartialHashCode(boolean unknowns) {
|
||||
int hash = ((getNonTrivialEqClasses().hashCode() * 31 +
|
||||
getDistinctClassPairs().hashCode()) * 31 +
|
||||
myStack.hashCode()) * 31 +
|
||||
myVariableStates.hashCode();
|
||||
if (unknowns) {
|
||||
hash = hash * 31 + myUnknownVariables.hashCode();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"HardCodedStringLiteral"})
|
||||
|
||||
@@ -38,7 +38,6 @@ import java.util.Set;
|
||||
class StateMerger {
|
||||
private final List<DfaMemoryStateImpl> myStates;
|
||||
private final MultiMap<UnorderedPair<DfaValue>,DfaMemoryStateImpl> myStatesByEq = new MultiMap<UnorderedPair<DfaValue>, DfaMemoryStateImpl>();
|
||||
private final MultiMap<Pair<DfaVariableValue, DfaPsiType>,DfaMemoryStateImpl> myStatesByInstanceof = new MultiMap<Pair<DfaVariableValue, DfaPsiType>, DfaMemoryStateImpl>();
|
||||
private final Map<DfaMemoryStateImpl, Map<DfaVariableValue, DfaConstValue>> myVarValues = ContainerUtil.newIdentityHashMap();
|
||||
|
||||
public StateMerger(List<DfaMemoryStateImpl> states) {
|
||||
@@ -54,12 +53,6 @@ class StateMerger {
|
||||
}
|
||||
}
|
||||
myVarValues.put(state, varValues);
|
||||
|
||||
for (DfaVariableValue value : state.getChangedVariable()) {
|
||||
for (DfaPsiType instanceofValue : state.getVariableState(value).myInstanceofValues) {
|
||||
myStatesByInstanceof.putValue(Pair.create(value, instanceofValue), state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +75,8 @@ class StateMerger {
|
||||
DfaMemoryStateImpl copy = copyWithoutVar(state, var).createCopy();
|
||||
|
||||
complementaryStates.add(state);
|
||||
postProcessMergedState(var, copy, complementaryStates);
|
||||
mergeNullableState(var, copy, complementaryStates);
|
||||
mergeUnknowns(copy, complementaryStates);
|
||||
return getMergeResult(copy, complementaryStates);
|
||||
}
|
||||
|
||||
@@ -90,13 +84,17 @@ class StateMerger {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void postProcessMergedState(DfaVariableValue var,
|
||||
DfaMemoryStateImpl mergedState,
|
||||
Collection<DfaMemoryStateImpl> complementaryStates) {
|
||||
private static void mergeUnknowns(DfaMemoryStateImpl mergedState, Collection<DfaMemoryStateImpl> complementaryStates) {
|
||||
for (DfaMemoryStateImpl removedState : complementaryStates) {
|
||||
for (DfaVariableValue unknownVar : removedState.getUnknownVariables()) {
|
||||
mergedState.doFlush(unknownVar, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static void mergeNullableState(DfaVariableValue var,
|
||||
DfaMemoryStateImpl mergedState,
|
||||
Collection<DfaMemoryStateImpl> complementaryStates) {
|
||||
for (DfaMemoryStateImpl removedState : complementaryStates) {
|
||||
if (removedState.getVariableState(var).isNullable()) {
|
||||
mergedState.setVariableState(var, mergedState.getVariableState(var).withNullability(Nullness.NULLABLE));
|
||||
}
|
||||
@@ -115,8 +113,50 @@ class StateMerger {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<DfaMemoryStateImpl> mergeByUnknowns() {
|
||||
MultiMap<Integer, DfaMemoryStateImpl> byHash = new MultiMap<Integer, DfaMemoryStateImpl>();
|
||||
for (DfaMemoryStateImpl state : myStates) {
|
||||
ProgressManager.checkCanceled();
|
||||
byHash.putValue(state.getPartialHashCode(false), state);
|
||||
}
|
||||
|
||||
for (Integer key : byHash.keySet()) {
|
||||
Collection<DfaMemoryStateImpl> similarStates = byHash.get(key);
|
||||
if (similarStates.size() < 2) continue;
|
||||
|
||||
for (final DfaMemoryStateImpl state1 : similarStates) {
|
||||
ProgressManager.checkCanceled();
|
||||
List<DfaMemoryStateImpl> complementary = ContainerUtil.filter(similarStates, new Condition<DfaMemoryStateImpl>() {
|
||||
@Override
|
||||
public boolean value(DfaMemoryStateImpl state2) {
|
||||
return state1.equalsSuperficially(state2) && state1.equalsByRelations(state2) && state1.equalsByVariableStates(state2);
|
||||
}
|
||||
});
|
||||
if (complementary.size() > 1) {
|
||||
DfaMemoryStateImpl copy = state1.createCopy();
|
||||
mergeUnknowns(copy, complementary);
|
||||
return getMergeResult(copy, ContainerUtil.newHashSet(complementary));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<DfaMemoryStateImpl> mergeByType() {
|
||||
MultiMap<Pair<DfaVariableValue, DfaPsiType>,DfaMemoryStateImpl> byInstanceof = new MultiMap<Pair<DfaVariableValue, DfaPsiType>, DfaMemoryStateImpl>();
|
||||
for (final DfaMemoryStateImpl state : myStates) {
|
||||
ProgressManager.checkCanceled();
|
||||
for (DfaVariableValue value : state.getChangedVariable()) {
|
||||
for (DfaPsiType instanceofValue : state.getVariableState(value).myInstanceofValues) {
|
||||
byInstanceof.putValue(Pair.create(value, instanceofValue), state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (final DfaMemoryStateImpl state : myStates) {
|
||||
ProgressManager.checkCanceled();
|
||||
|
||||
@@ -124,7 +164,7 @@ class StateMerger {
|
||||
for (final DfaPsiType notInstanceof : state.getVariableState(var).myNotInstanceofValues) {
|
||||
final DfaVariableState varStateWithoutType = getVarStateWithoutType(state, var, notInstanceof);
|
||||
List<DfaMemoryStateImpl> complementaryStates = ContainerUtil.filter(
|
||||
myStatesByInstanceof.get(Pair.create(var, notInstanceof)),
|
||||
byInstanceof.get(Pair.create(var, notInstanceof)),
|
||||
new Condition<DfaMemoryStateImpl>() {
|
||||
@Override
|
||||
public boolean value(DfaMemoryStateImpl another) {
|
||||
@@ -142,7 +182,8 @@ class StateMerger {
|
||||
copy.setVariableState(var, varStateWithoutType);
|
||||
|
||||
complementaryStates.add(state);
|
||||
postProcessMergedState(var, copy, complementaryStates);
|
||||
mergeNullableState(var, copy, complementaryStates);
|
||||
mergeUnknowns(copy, complementaryStates);
|
||||
return getMergeResult(copy, ContainerUtil.newHashSet(complementaryStates));
|
||||
}
|
||||
}
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class Some {
|
||||
String field1;
|
||||
String field2;
|
||||
String field3;
|
||||
String field4;
|
||||
String field5;
|
||||
String field6;
|
||||
String field7;
|
||||
String field8;
|
||||
String field9;
|
||||
|
||||
void a(String[] lines) {
|
||||
for (String line : lines) {
|
||||
if (line.startsWith("1")) field1 = someString();
|
||||
else if (line.startsWith("2")) field2 = someString();
|
||||
else if (line.startsWith("3")) field3 = someString();
|
||||
else if (line.startsWith("4")) field4 = someString();
|
||||
else if (line.startsWith("5")) field5 = someString();
|
||||
else if (line.startsWith("6")) field6 = someString();
|
||||
else if (line.startsWith("7")) field7 = someString();
|
||||
else if (line.startsWith("8")) field8 = someString();
|
||||
else if (line.startsWith("9")) field9 = someString();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull String someString() { return ""; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -303,6 +303,8 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase {
|
||||
public void testManySequentialInstanceofsNotComplex() { doTest(); }
|
||||
public void testLongDisjunctionsNotComplex() { doTest(); }
|
||||
public void testWhileNotComplex() { doTest(); }
|
||||
public void testManyDisjunctiveFieldAssignmentsInLoopNotComplex() { doTest(); }
|
||||
|
||||
public void testVariablesDiverge() { doTest(); }
|
||||
|
||||
public void _testNullCheckBeforeInstanceof() { doTest(); }
|
||||
|
||||
Reference in New Issue
Block a user