|
|
|
@@ -8,10 +8,13 @@ import org.jetbrains.annotations.Nullable;
|
|
|
|
|
import org.jetbrains.jet.lang.cfg.pseudocode.*;
|
|
|
|
|
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
|
|
|
|
import org.jetbrains.jet.lang.descriptors.LocalVariableDescriptor;
|
|
|
|
|
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
|
|
|
|
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
|
|
|
|
import org.jetbrains.jet.lang.diagnostics.Errors;
|
|
|
|
|
import org.jetbrains.jet.lang.psi.*;
|
|
|
|
|
import org.jetbrains.jet.lang.resolve.BindingContext;
|
|
|
|
|
import org.jetbrains.jet.lang.resolve.BindingTrace;
|
|
|
|
|
import org.jetbrains.jet.lang.types.JetStandardClasses;
|
|
|
|
|
|
|
|
|
|
import java.util.*;
|
|
|
|
|
|
|
|
|
@@ -256,103 +259,25 @@ public class JetFlowInformationProvider {
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
private <D> Map<Instruction, D> traverseInstructionGraphUntilFactsStabilization(
|
|
|
|
|
Pseudocode pseudocode,
|
|
|
|
|
InstructionsMergeHandler<D> instructionsMergeHandler,
|
|
|
|
|
D initialDataValue,
|
|
|
|
|
D initialDataValueForEnterInstruction,
|
|
|
|
|
boolean straightDirection) {
|
|
|
|
|
Map<Instruction, D> dataMap = Maps.newHashMap();
|
|
|
|
|
initializeDataMap(dataMap, pseudocode, initialDataValue);
|
|
|
|
|
dataMap.put(pseudocode.getEnterInstruction(), initialDataValueForEnterInstruction);
|
|
|
|
|
|
|
|
|
|
boolean[] changed = new boolean[1];
|
|
|
|
|
changed[0] = true;
|
|
|
|
|
while (changed[0]) {
|
|
|
|
|
changed[0] = false;
|
|
|
|
|
traverseSubGraph(pseudocode, instructionsMergeHandler, Collections.<Instruction>emptyList(), straightDirection, dataMap, changed, false);
|
|
|
|
|
}
|
|
|
|
|
return dataMap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private <D> void initializeDataMap(
|
|
|
|
|
Map<Instruction, D> dataMap,
|
|
|
|
|
Pseudocode pseudocode,
|
|
|
|
|
D initialDataValue) {
|
|
|
|
|
List<Instruction> instructions = pseudocode.getInstructions();
|
|
|
|
|
for (Instruction instruction : instructions) {
|
|
|
|
|
dataMap.put(instruction, initialDataValue);
|
|
|
|
|
if (instruction instanceof LocalDeclarationInstruction) {
|
|
|
|
|
initializeDataMap(dataMap, ((LocalDeclarationInstruction) instruction).getBody(), initialDataValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private <D> void traverseSubGraph(
|
|
|
|
|
Pseudocode pseudocode,
|
|
|
|
|
InstructionsMergeHandler<D> instructionsMergeHandler,
|
|
|
|
|
Collection<Instruction> previousSubGraphInstructions,
|
|
|
|
|
boolean straightDirection,
|
|
|
|
|
Map<Instruction, D> dataMap,
|
|
|
|
|
boolean[] changed,
|
|
|
|
|
boolean isLocal) {
|
|
|
|
|
List<Instruction> instructions = pseudocode.getInstructions();
|
|
|
|
|
SubroutineEnterInstruction enterInstruction = pseudocode.getEnterInstruction();
|
|
|
|
|
for (Instruction instruction : instructions) {
|
|
|
|
|
if (!isLocal && instruction instanceof SubroutineEnterInstruction) continue;
|
|
|
|
|
|
|
|
|
|
Collection<Instruction> allPreviousInstructions;
|
|
|
|
|
Collection<Instruction> previousInstructions = straightDirection
|
|
|
|
|
? instruction.getPreviousInstructions()
|
|
|
|
|
: instruction.getNextInstructions();
|
|
|
|
|
|
|
|
|
|
if (instruction == enterInstruction && !previousSubGraphInstructions.isEmpty()) {
|
|
|
|
|
allPreviousInstructions = Lists.newArrayList(previousInstructions);
|
|
|
|
|
allPreviousInstructions.addAll(previousSubGraphInstructions);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
allPreviousInstructions = previousInstructions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (instruction instanceof LocalDeclarationInstruction) {
|
|
|
|
|
Pseudocode subroutinePseudocode = ((LocalDeclarationInstruction) instruction).getBody();
|
|
|
|
|
traverseSubGraph(subroutinePseudocode, instructionsMergeHandler, previousInstructions, straightDirection, dataMap, changed, true);
|
|
|
|
|
}
|
|
|
|
|
D previousDataValue = dataMap.get(instruction);
|
|
|
|
|
|
|
|
|
|
Collection<D> incomingEdgesData = Sets.newHashSet();
|
|
|
|
|
|
|
|
|
|
for (Instruction previousInstruction : allPreviousInstructions) {
|
|
|
|
|
incomingEdgesData.add(dataMap.get(previousInstruction));
|
|
|
|
|
}
|
|
|
|
|
D mergedData = instructionsMergeHandler.merge(instruction, incomingEdgesData);
|
|
|
|
|
if (!mergedData.equals(previousDataValue)) {
|
|
|
|
|
changed[0] = true;
|
|
|
|
|
dataMap.put(instruction, mergedData);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void markUninitializedVariables(@NotNull JetElement subroutine) {
|
|
|
|
|
public void markUninitializedVariables(@NotNull JetElement subroutine, List<? extends VariableDescriptor> initializedVariables) {
|
|
|
|
|
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
|
|
|
|
assert pseudocode != null;
|
|
|
|
|
|
|
|
|
|
Collection<Instruction> instructions = pseudocode.getInstructions();
|
|
|
|
|
InstructionsMergeHandler<Map<LocalVariableDescriptor, InitializationPoints>> instructionsMergeHandler = new InstructionsMergeHandler<Map<LocalVariableDescriptor, InitializationPoints>>() {
|
|
|
|
|
JetControlFlowGraphTraverser.InstructionsMergeStrategy<Map<VariableDescriptor, InitializationPoints>> instructionsMergeStrategy = new JetControlFlowGraphTraverser.InstructionsMergeStrategy<Map<VariableDescriptor, InitializationPoints>>() {
|
|
|
|
|
@Override
|
|
|
|
|
public Map<LocalVariableDescriptor, InitializationPoints> merge(
|
|
|
|
|
public Map<VariableDescriptor, InitializationPoints> execute(
|
|
|
|
|
Instruction instruction,
|
|
|
|
|
Collection<Map<LocalVariableDescriptor, InitializationPoints>> incomingEdgesData) {
|
|
|
|
|
Collection<Map<VariableDescriptor, InitializationPoints>> incomingEdgesData) {
|
|
|
|
|
|
|
|
|
|
Set<LocalVariableDescriptor> variablesInScope = Sets.newHashSet();
|
|
|
|
|
for (Map<LocalVariableDescriptor, InitializationPoints> edgePointsMap : incomingEdgesData) {
|
|
|
|
|
Set<VariableDescriptor> variablesInScope = Sets.newHashSet();
|
|
|
|
|
for (Map<VariableDescriptor, InitializationPoints> edgePointsMap : incomingEdgesData) {
|
|
|
|
|
variablesInScope.addAll(edgePointsMap.keySet());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Map<LocalVariableDescriptor, InitializationPoints> pointsMap = Maps.newHashMap();
|
|
|
|
|
for (LocalVariableDescriptor variable : variablesInScope) {
|
|
|
|
|
Map<VariableDescriptor, InitializationPoints> pointsMap = Maps.newHashMap();
|
|
|
|
|
for (VariableDescriptor variable : variablesInScope) {
|
|
|
|
|
Set<InitializationPoints> edgesDataForVariable = Sets.newHashSet();
|
|
|
|
|
for (Map<LocalVariableDescriptor, InitializationPoints> edgePointsMap : incomingEdgesData) {
|
|
|
|
|
for (Map<VariableDescriptor, InitializationPoints> edgePointsMap : incomingEdgesData) {
|
|
|
|
|
InitializationPoints points = edgePointsMap.get(variable);
|
|
|
|
|
if (points != null) {
|
|
|
|
|
edgesDataForVariable.add(points);
|
|
|
|
@@ -362,81 +287,95 @@ public class JetFlowInformationProvider {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (instruction instanceof WriteValueInstruction) {
|
|
|
|
|
LocalVariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction);
|
|
|
|
|
// DeclarationDescriptor descriptor = null;
|
|
|
|
|
// JetElement lValue = ((WriteValueInstruction) instruction).getlValue();
|
|
|
|
|
// if (lValue instanceof JetProperty || lValue instanceof JetParameter) {
|
|
|
|
|
// descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, lValue);
|
|
|
|
|
// }
|
|
|
|
|
// else if (lValue instanceof JetSimpleNameExpression) {
|
|
|
|
|
// descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) lValue);
|
|
|
|
|
// }
|
|
|
|
|
// if (descriptor instanceof LocalVariableDescriptor) {
|
|
|
|
|
// LocalVariableDescriptor variableDescriptor = (LocalVariableDescriptor) descriptor;
|
|
|
|
|
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction);
|
|
|
|
|
InitializationPoints initializationAtThisPoint = new InitializationPoints(((WriteValueInstruction) instruction).getElement());
|
|
|
|
|
pointsMap.put(variableDescriptor, initializationAtThisPoint);
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pointsMap;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Set<LocalVariableDescriptor> localVariables = collectAllLocalVariables(pseudocode);
|
|
|
|
|
Map<LocalVariableDescriptor, InitializationPoints> initialMapForStartInstruction = Maps.newHashMap();
|
|
|
|
|
InitializationPoints initialPoints = new InitializationPoints();
|
|
|
|
|
for (LocalVariableDescriptor variable : localVariables) {
|
|
|
|
|
initialMapForStartInstruction.put(variable, initialPoints);
|
|
|
|
|
Set<VariableDescriptor> localVariables = collectAllLocalVariables(pseudocode);
|
|
|
|
|
Map<VariableDescriptor, InitializationPoints> initialMapForStartInstruction = Maps.newHashMap();
|
|
|
|
|
InitializationPoints initialPointsForLocalVariable = new InitializationPoints(true);
|
|
|
|
|
for (VariableDescriptor variable : localVariables) {
|
|
|
|
|
initialMapForStartInstruction.put(variable, initialPointsForLocalVariable);
|
|
|
|
|
}
|
|
|
|
|
Map<Instruction, Map<LocalVariableDescriptor, InitializationPoints>> dataMap =
|
|
|
|
|
traverseInstructionGraphUntilFactsStabilization(pseudocode, instructionsMergeHandler, Collections.<LocalVariableDescriptor, InitializationPoints>emptyMap(), initialMapForStartInstruction, true);
|
|
|
|
|
InitializationPoints initialPointsForParameter = new InitializationPoints(false);
|
|
|
|
|
for (VariableDescriptor initializedVariable : initializedVariables) {
|
|
|
|
|
initialMapForStartInstruction.put(initializedVariable, initialPointsForParameter);
|
|
|
|
|
}
|
|
|
|
|
final Map<Instruction, Map<VariableDescriptor, InitializationPoints>> dataMap =
|
|
|
|
|
JetControlFlowGraphTraverser.traverseInstructionGraphUntilFactsStabilization(pseudocode, instructionsMergeStrategy, Collections.<VariableDescriptor, InitializationPoints>emptyMap(), initialMapForStartInstruction, true);
|
|
|
|
|
|
|
|
|
|
InstructionDataAnalyzer<Map<LocalVariableDescriptor, InitializationPoints>> instructionDataAnalyzer = new InstructionDataAnalyzer<Map<LocalVariableDescriptor, InitializationPoints>>() {
|
|
|
|
|
JetControlFlowGraphTraverser.traverseAndAnalyzeInstructionGraph(pseudocode, new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy() {
|
|
|
|
|
@Override
|
|
|
|
|
public void analyze(Instruction instruction, Map<Instruction, Map<LocalVariableDescriptor, InitializationPoints>> dataMap) {
|
|
|
|
|
Map<LocalVariableDescriptor, InitializationPoints> variablesData = dataMap.get(instruction);
|
|
|
|
|
public void execute(Instruction instruction) {
|
|
|
|
|
Map<VariableDescriptor, InitializationPoints> variablesData = dataMap.get(instruction);
|
|
|
|
|
if (instruction instanceof ReadValueInstruction) {
|
|
|
|
|
LocalVariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction);
|
|
|
|
|
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction);
|
|
|
|
|
JetElement element = ((ReadValueInstruction) instruction).getElement();
|
|
|
|
|
if (element instanceof JetSimpleNameExpression && variableDescriptor != null) {
|
|
|
|
|
// DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) element);
|
|
|
|
|
// if (descriptor instanceof LocalVariableDescriptor) {
|
|
|
|
|
if (element instanceof JetSimpleNameExpression && variableDescriptor instanceof LocalVariableDescriptor) {
|
|
|
|
|
InitializationPoints initializationPoints = variablesData.get(variableDescriptor);
|
|
|
|
|
assert initializationPoints != null;
|
|
|
|
|
if (initializationPoints.canBeUninitialized) {
|
|
|
|
|
trace.report(Errors.UNINITIALIZED_VARIABLE.on((JetSimpleNameExpression) element, variableDescriptor));
|
|
|
|
|
}
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (instruction instanceof WriteValueInstruction) {
|
|
|
|
|
JetElement element = ((WriteValueInstruction) instruction).getlValue();
|
|
|
|
|
LocalVariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction);
|
|
|
|
|
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction);
|
|
|
|
|
if (element instanceof JetSimpleNameExpression && variableDescriptor != null) {
|
|
|
|
|
// DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) element);
|
|
|
|
|
// if (descriptor instanceof LocalVariableDescriptor) {
|
|
|
|
|
InitializationPoints initializationPoints = variablesData.get(variableDescriptor);
|
|
|
|
|
assert initializationPoints != null;
|
|
|
|
|
if (initializationPoints.hasPossibleInitializers() && !variableDescriptor.isVar()) {
|
|
|
|
|
trace.report(Errors.VAL_REASSIGNMENT.on((JetSimpleNameExpression) element, variableDescriptor));
|
|
|
|
|
}
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
traverseAndAnalyzeInstructionGraph(instructions, dataMap, instructionDataAnalyzer);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void markNotOnlyInvokedFunctionVariables(@NotNull JetElement subroutine, List<? extends VariableDescriptor> variables) {
|
|
|
|
|
final List<VariableDescriptor> functionVariables = Lists.newArrayList();
|
|
|
|
|
for (VariableDescriptor variable : variables) {
|
|
|
|
|
if (JetStandardClasses.isFunctionType(variable.getReturnType())) {
|
|
|
|
|
functionVariables.add(variable);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
|
|
|
|
assert pseudocode != null;
|
|
|
|
|
|
|
|
|
|
JetControlFlowGraphTraverser.traverseAndAnalyzeInstructionGraph(pseudocode, new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy() {
|
|
|
|
|
@Override
|
|
|
|
|
public void execute(Instruction instruction) {
|
|
|
|
|
if (instruction instanceof ReadValueInstruction) {
|
|
|
|
|
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction);
|
|
|
|
|
if (variableDescriptor != null && functionVariables.contains(variableDescriptor)) {
|
|
|
|
|
//check that we only invoke this variable
|
|
|
|
|
JetElement element = ((ReadValueInstruction) instruction).getElement();
|
|
|
|
|
if (element instanceof JetSimpleNameExpression && !(element.getParent() instanceof JetCallExpression)) {
|
|
|
|
|
trace.report(Errors.FUNCTION_PARAMETERS_OF_INLINE_FUNCTION.on((JetSimpleNameExpression)element, variableDescriptor));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
|
private LocalVariableDescriptor extractVariableDescriptorIfAny(Instruction instruction) {
|
|
|
|
|
LocalVariableDescriptor variableDescriptor = null;
|
|
|
|
|
private VariableDescriptor extractVariableDescriptorIfAny(Instruction instruction) {
|
|
|
|
|
VariableDescriptor variableDescriptor = null;
|
|
|
|
|
if (instruction instanceof ReadValueInstruction) {
|
|
|
|
|
JetElement element = ((ReadValueInstruction) instruction).getElement();
|
|
|
|
|
if (element instanceof JetSimpleNameExpression) {
|
|
|
|
|
DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) element);
|
|
|
|
|
if (descriptor instanceof LocalVariableDescriptor) {
|
|
|
|
|
variableDescriptor = (LocalVariableDescriptor) descriptor;
|
|
|
|
|
if (descriptor instanceof VariableDescriptor) {
|
|
|
|
|
variableDescriptor = (VariableDescriptor) descriptor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -449,51 +388,34 @@ public class JetFlowInformationProvider {
|
|
|
|
|
else if (lValue instanceof JetSimpleNameExpression) {
|
|
|
|
|
descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) lValue);
|
|
|
|
|
}
|
|
|
|
|
if (descriptor instanceof LocalVariableDescriptor) {
|
|
|
|
|
variableDescriptor = (LocalVariableDescriptor) descriptor;
|
|
|
|
|
if (descriptor instanceof VariableDescriptor) {
|
|
|
|
|
variableDescriptor = (VariableDescriptor) descriptor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return variableDescriptor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Set<LocalVariableDescriptor> collectAllLocalVariables(Pseudocode pseudocode) {
|
|
|
|
|
final Set<LocalVariableDescriptor> localVariables = Sets.newHashSet();
|
|
|
|
|
InstructionDataAnalyzer<Void> analyzer = new InstructionDataAnalyzer<Void>() {
|
|
|
|
|
private Set<VariableDescriptor> collectAllLocalVariables(Pseudocode pseudocode) {
|
|
|
|
|
final Set<VariableDescriptor> localVariables = Sets.newHashSet();
|
|
|
|
|
JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy analyzeStrategy = new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy() {
|
|
|
|
|
@Override
|
|
|
|
|
public void analyze(Instruction instruction, Map<Instruction, Void> dataMap) {
|
|
|
|
|
LocalVariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction);
|
|
|
|
|
public void execute(Instruction instruction) {
|
|
|
|
|
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction);
|
|
|
|
|
if (variableDescriptor != null) {
|
|
|
|
|
localVariables.add(variableDescriptor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
traverseAndAnalyzeInstructionGraph(pseudocode.getInstructions(), Collections.<Instruction, Void>emptyMap(), analyzer);
|
|
|
|
|
JetControlFlowGraphTraverser.traverseAndAnalyzeInstructionGraph(pseudocode, analyzeStrategy);
|
|
|
|
|
return localVariables;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private <D> void traverseAndAnalyzeInstructionGraph(Collection<Instruction> instructions, Map<Instruction, D> dataMap, InstructionDataAnalyzer<D> instructionDataAnalyzer) {
|
|
|
|
|
for (Instruction instruction : instructions) {
|
|
|
|
|
if (instruction instanceof LocalDeclarationInstruction) {
|
|
|
|
|
traverseAndAnalyzeInstructionGraph(((LocalDeclarationInstruction) instruction).getBody().getInstructions(), dataMap, instructionDataAnalyzer);
|
|
|
|
|
}
|
|
|
|
|
instructionDataAnalyzer.analyze(instruction, dataMap);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface InstructionsMergeHandler<D> {
|
|
|
|
|
D merge(Instruction instruction, Collection<D> incomingEdgesData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface InstructionDataAnalyzer<D> {
|
|
|
|
|
void analyze(Instruction instruction, Map<Instruction, D> dataMap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static class InitializationPoints {
|
|
|
|
|
private Set<JetElement> possiblePoints = Sets.newHashSet();
|
|
|
|
|
private boolean canBeUninitialized;
|
|
|
|
|
|
|
|
|
|
public InitializationPoints() {
|
|
|
|
|
canBeUninitialized = true;
|
|
|
|
|
public InitializationPoints(boolean canBeUninitialized) {
|
|
|
|
|
this.canBeUninitialized = canBeUninitialized;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public InitializationPoints(JetElement element) {
|
|
|
|
|