refactoring & KT-607 Val reassignment is not marked as an error
This commit is contained in:
@@ -31,7 +31,7 @@ public class JetControlFlowGraphTraverser<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void collectInformationFromInstructionGraph(
|
public void collectInformationFromInstructionGraph(
|
||||||
InstructionsMergeStrategy<D> instructionsMergeStrategy,
|
InstructionDataMergeStrategy<D> instructionDataMergeStrategy,
|
||||||
D initialDataValue,
|
D initialDataValue,
|
||||||
D initialDataValueForEnterInstruction,
|
D initialDataValueForEnterInstruction,
|
||||||
boolean straightDirection) {
|
boolean straightDirection) {
|
||||||
@@ -43,7 +43,7 @@ public class JetControlFlowGraphTraverser<D> {
|
|||||||
changed[0] = true;
|
changed[0] = true;
|
||||||
while (changed[0]) {
|
while (changed[0]) {
|
||||||
changed[0] = false;
|
changed[0] = false;
|
||||||
traverseSubGraph(pseudocode, instructionsMergeStrategy, Collections.<Instruction>emptyList(), straightDirection, changed, false);
|
traverseSubGraph(pseudocode, instructionDataMergeStrategy, Collections.<Instruction>emptyList(), straightDirection, changed, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ public class JetControlFlowGraphTraverser<D> {
|
|||||||
|
|
||||||
private void traverseSubGraph(
|
private void traverseSubGraph(
|
||||||
Pseudocode pseudocode,
|
Pseudocode pseudocode,
|
||||||
InstructionsMergeStrategy<D> instructionsMergeStrategy,
|
InstructionDataMergeStrategy<D> instructionDataMergeStrategy,
|
||||||
Collection<Instruction> previousSubGraphInstructions,
|
Collection<Instruction> previousSubGraphInstructions,
|
||||||
boolean straightDirection,
|
boolean straightDirection,
|
||||||
boolean[] changed,
|
boolean[] changed,
|
||||||
@@ -87,7 +87,7 @@ public class JetControlFlowGraphTraverser<D> {
|
|||||||
|
|
||||||
if (lookInside && instruction instanceof LocalDeclarationInstruction) {
|
if (lookInside && instruction instanceof LocalDeclarationInstruction) {
|
||||||
Pseudocode subroutinePseudocode = ((LocalDeclarationInstruction) instruction).getBody();
|
Pseudocode subroutinePseudocode = ((LocalDeclarationInstruction) instruction).getBody();
|
||||||
traverseSubGraph(subroutinePseudocode, instructionsMergeStrategy, previousInstructions, straightDirection, changed, true);
|
traverseSubGraph(subroutinePseudocode, instructionDataMergeStrategy, previousInstructions, straightDirection, changed, true);
|
||||||
}
|
}
|
||||||
Pair<D, D> previousDataValue = dataMap.get(instruction);
|
Pair<D, D> previousDataValue = dataMap.get(instruction);
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ public class JetControlFlowGraphTraverser<D> {
|
|||||||
incomingEdgesData.add(previousData.getSecond());
|
incomingEdgesData.add(previousData.getSecond());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Pair<D, D> mergedData = instructionsMergeStrategy.execute(instruction, incomingEdgesData);
|
Pair<D, D> mergedData = instructionDataMergeStrategy.execute(instruction, incomingEdgesData);
|
||||||
if (!mergedData.equals(previousDataValue)) {
|
if (!mergedData.equals(previousDataValue)) {
|
||||||
changed[0] = true;
|
changed[0] = true;
|
||||||
dataMap.put(instruction, mergedData);
|
dataMap.put(instruction, mergedData);
|
||||||
@@ -132,7 +132,7 @@ public class JetControlFlowGraphTraverser<D> {
|
|||||||
return dataMap.get(pseudocode.getSinkInstruction()).getFirst();
|
return dataMap.get(pseudocode.getSinkInstruction()).getFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InstructionsMergeStrategy<D> {
|
interface InstructionDataMergeStrategy<D> {
|
||||||
Pair<D, D> execute(Instruction instruction, @NotNull Collection<D> incomingEdgesData);
|
Pair<D, D> execute(Instruction instruction, @NotNull Collection<D> incomingEdgesData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+191
-151
@@ -187,176 +187,204 @@ public class JetFlowInformationProvider {
|
|||||||
return JetPsiUtil.findRootExpressions(unreachableElements);
|
return JetPsiUtil.findRootExpressions(unreachableElements);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markUninitializedVariables(@NotNull JetElement subroutine, final boolean inAnonymousInitializers, final boolean inLocalDeclaration) {
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Uninitialized variables analysis
|
||||||
|
|
||||||
|
public void markUninitializedVariables(@NotNull JetElement subroutine, final boolean processLocalDeclaration) {
|
||||||
final Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
final Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
||||||
assert pseudocode != null;
|
assert pseudocode != null;
|
||||||
|
|
||||||
JetControlFlowGraphTraverser<Map<VariableDescriptor, InitializationPoints>> traverser = JetControlFlowGraphTraverser.create(pseudocode, false);
|
JetControlFlowGraphTraverser<Map<VariableDescriptor, VariableInitializers>> traverser = JetControlFlowGraphTraverser.create(pseudocode, false);
|
||||||
|
|
||||||
JetControlFlowGraphTraverser.InstructionsMergeStrategy<Map<VariableDescriptor, InitializationPoints>> instructionsMergeStrategy =
|
|
||||||
new JetControlFlowGraphTraverser.InstructionsMergeStrategy<Map<VariableDescriptor, InitializationPoints>>() {
|
|
||||||
@Override
|
|
||||||
public Pair<Map<VariableDescriptor, InitializationPoints>, Map<VariableDescriptor, InitializationPoints>> execute(
|
|
||||||
Instruction instruction,
|
|
||||||
@NotNull Collection<Map<VariableDescriptor, InitializationPoints>> incomingEdgesData) {
|
|
||||||
|
|
||||||
Set<VariableDescriptor> variablesInScope = Sets.newHashSet();
|
|
||||||
for (Map<VariableDescriptor, InitializationPoints> edgePointsMap : incomingEdgesData) {
|
|
||||||
variablesInScope.addAll(edgePointsMap.keySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<VariableDescriptor, InitializationPoints> enterInstructionPointsMap = Maps.newHashMap();
|
|
||||||
for (VariableDescriptor variable : variablesInScope) {
|
|
||||||
Set<InitializationPoints> edgesDataForVariable = Sets.newHashSet();
|
|
||||||
for (Map<VariableDescriptor, InitializationPoints> edgePointsMap : incomingEdgesData) {
|
|
||||||
InitializationPoints points = edgePointsMap.get(variable);
|
|
||||||
if (points != null) {
|
|
||||||
edgesDataForVariable.add(points);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
enterInstructionPointsMap.put(variable, new InitializationPoints(edgesDataForVariable));
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<VariableDescriptor, InitializationPoints> exitInstructionPointsMap = Maps.newHashMap(enterInstructionPointsMap);
|
|
||||||
if (instruction instanceof WriteValueInstruction) {
|
|
||||||
VariableDescriptor variable = extractVariableDescriptorIfAny(instruction, false);
|
|
||||||
InitializationPoints initializationAtThisPoint = new InitializationPoints(((WriteValueInstruction) instruction).getElement());
|
|
||||||
exitInstructionPointsMap.put(variable, initializationAtThisPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Pair.create(enterInstructionPointsMap, exitInstructionPointsMap);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Collection<VariableDescriptor> usedVariables = collectUsedVariables(pseudocode);
|
Collection<VariableDescriptor> usedVariables = collectUsedVariables(pseudocode);
|
||||||
final Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
|
final Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
|
||||||
Map<VariableDescriptor, InitializationPoints> initialMapForStartInstruction = prepareInitialMapForStartInstruction(usedVariables, declaredVariables);
|
Map<VariableDescriptor, VariableInitializers> initialMapForStartInstruction = prepareInitialMapForStartInstruction(usedVariables, declaredVariables);
|
||||||
|
|
||||||
traverser.collectInformationFromInstructionGraph(instructionsMergeStrategy,
|
JetControlFlowGraphTraverser.InstructionDataMergeStrategy<Map<VariableDescriptor, VariableInitializers>> variableInitializersMergeStrategy =
|
||||||
Collections.<VariableDescriptor, InitializationPoints>emptyMap(),
|
new JetControlFlowGraphTraverser.InstructionDataMergeStrategy<Map<VariableDescriptor, VariableInitializers>>() {
|
||||||
initialMapForStartInstruction,
|
@Override
|
||||||
true);
|
public Pair<Map<VariableDescriptor, VariableInitializers>, Map<VariableDescriptor, VariableInitializers>> execute(
|
||||||
|
Instruction instruction,
|
||||||
|
@NotNull Collection<Map<VariableDescriptor, VariableInitializers>> incomingEdgesData) {
|
||||||
|
|
||||||
|
Map<VariableDescriptor, VariableInitializers> enterInstructionData = mergeIncomingEdgesData(incomingEdgesData);
|
||||||
|
Map<VariableDescriptor, VariableInitializers> exitInstructionData = addVariableInitializerFromCurrentInstructionIfAny(instruction, enterInstructionData);
|
||||||
|
return Pair.create(enterInstructionData, exitInstructionData);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
traverser.collectInformationFromInstructionGraph(variableInitializersMergeStrategy, Collections.<VariableDescriptor, VariableInitializers>emptyMap(), initialMapForStartInstruction, true);
|
||||||
|
|
||||||
final Collection<VariableDescriptor> varWithUninitializedErrorGenerated = Sets.newHashSet();
|
final Collection<VariableDescriptor> varWithUninitializedErrorGenerated = Sets.newHashSet();
|
||||||
final Collection<VariableDescriptor> varWithValReassignErrorGenerated = Sets.newHashSet();
|
final Collection<VariableDescriptor> varWithValReassignErrorGenerated = Sets.newHashSet();
|
||||||
traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Map<VariableDescriptor, InitializationPoints>>() {
|
final boolean processClassOrObject = subroutine instanceof JetClassOrObject;
|
||||||
|
traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableInitializers>>() {
|
||||||
@Override
|
@Override
|
||||||
public void execute(Instruction instruction, @Nullable Map<VariableDescriptor, InitializationPoints> enterData, @Nullable Map<VariableDescriptor, InitializationPoints> exitData) {
|
public void execute(Instruction instruction, @Nullable Map<VariableDescriptor, VariableInitializers> enterData, @Nullable Map<VariableDescriptor, VariableInitializers> exitData) {
|
||||||
assert enterData != null && exitData != null;
|
assert enterData != null && exitData != null;
|
||||||
if (instruction instanceof ReadValueInstruction) {
|
if (instruction instanceof ReadValueInstruction) {
|
||||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
||||||
JetElement element = ((ReadValueInstruction) instruction).getElement();
|
if (variableDescriptor != null && declaredVariables.contains(variableDescriptor)) {
|
||||||
if (element instanceof JetSimpleNameExpression && variableDescriptor != null && declaredVariables.contains(variableDescriptor)) {
|
checkIsInitialized(variableDescriptor, ((ReadValueInstruction) instruction).getElement(), exitData.get(variableDescriptor), varWithUninitializedErrorGenerated);
|
||||||
|
|
||||||
InitializationPoints exitInitializationPoints = exitData.get(variableDescriptor);
|
|
||||||
assert exitInitializationPoints != null;
|
|
||||||
|
|
||||||
boolean isInitialized = exitInitializationPoints.isInitialized();
|
|
||||||
if (variableDescriptor instanceof PropertyDescriptor) {
|
|
||||||
if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)) {
|
|
||||||
isInitialized = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!inLocalDeclaration && !isInitialized && !varWithUninitializedErrorGenerated.contains(variableDescriptor)) {
|
|
||||||
varWithUninitializedErrorGenerated.add(variableDescriptor);
|
|
||||||
trace.report(Errors.UNINITIALIZED_VARIABLE.on((JetSimpleNameExpression) element, variableDescriptor));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (instruction instanceof WriteValueInstruction) {
|
else if (instruction instanceof WriteValueInstruction) {
|
||||||
JetElement element = ((WriteValueInstruction) instruction).getlValue();
|
|
||||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true);
|
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true);
|
||||||
if (element instanceof JetExpression && variableDescriptor != null) {
|
JetElement element = ((WriteValueInstruction) instruction).getlValue();
|
||||||
InitializationPoints enterInitializationPoints = enterData.get(variableDescriptor);
|
if (variableDescriptor != null && element instanceof JetExpression) {
|
||||||
assert enterInitializationPoints != null;
|
if (!processLocalDeclaration) { // error has been generated before while processing outer function of this local declaration
|
||||||
InitializationPoints exitInitializationPoints = exitData.get(variableDescriptor);
|
checkValReassignment(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), varWithValReassignErrorGenerated);
|
||||||
assert exitInitializationPoints != null;
|
|
||||||
Set<JetElement> possiblePoints = enterInitializationPoints.getPossiblePoints();
|
|
||||||
boolean hasInitializer = !possiblePoints.isEmpty() || enterInitializationPoints.isInitialized();
|
|
||||||
if (possiblePoints.size() == 1) {
|
|
||||||
JetElement initializer = possiblePoints.iterator().next();
|
|
||||||
if (initializer instanceof JetProperty && initializer == element.getParent()) {
|
|
||||||
hasInitializer = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
JetExpression expression = (JetExpression) element;
|
if (processClassOrObject) {
|
||||||
if (!inLocalDeclaration && hasInitializer && !variableDescriptor.isVar() && !varWithValReassignErrorGenerated.contains(variableDescriptor)) {
|
checkInitializationUsingBackingField(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), exitData.get(variableDescriptor));
|
||||||
PsiElement psiElement = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor);
|
|
||||||
JetProperty property = psiElement instanceof JetProperty ? (JetProperty) psiElement : null;
|
|
||||||
varWithValReassignErrorGenerated.add(variableDescriptor);
|
|
||||||
boolean hasReassignMethodReturningUnit = false;
|
|
||||||
JetSimpleNameExpression operationReference = null;
|
|
||||||
PsiElement parent = expression.getParent();
|
|
||||||
if (parent instanceof JetBinaryExpression) {
|
|
||||||
operationReference = ((JetBinaryExpression) parent).getOperationReference();
|
|
||||||
}
|
|
||||||
else if (parent instanceof JetUnaryExpression) {
|
|
||||||
operationReference = ((JetUnaryExpression) parent).getOperationSign();
|
|
||||||
}
|
|
||||||
if (operationReference != null) {
|
|
||||||
DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, operationReference);
|
|
||||||
if (descriptor instanceof FunctionDescriptor) {
|
|
||||||
if (JetStandardClasses.isUnit(((FunctionDescriptor) descriptor).getReturnType())) {
|
|
||||||
hasReassignMethodReturningUnit = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!hasReassignMethodReturningUnit) {
|
|
||||||
trace.report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor, property == null ? new JetProperty[0] : new JetProperty[]{property}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (inAnonymousInitializers && variableDescriptor instanceof PropertyDescriptor && !enterInitializationPoints.isInitialized() &&
|
|
||||||
exitInitializationPoints.isInitialized()) {
|
|
||||||
JetExpression variable = expression;
|
|
||||||
if (expression instanceof JetDotQualifiedExpression) {
|
|
||||||
if (((JetDotQualifiedExpression) expression).getReceiverExpression() instanceof JetThisExpression) {
|
|
||||||
variable = ((JetDotQualifiedExpression) expression).getSelectorExpression();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (variable instanceof JetSimpleNameExpression) {
|
|
||||||
JetSimpleNameExpression simpleNameExpression = (JetSimpleNameExpression) variable;
|
|
||||||
if (simpleNameExpression.getReferencedNameElementType() != JetTokens.FIELD_IDENTIFIER) {
|
|
||||||
trace.report(Errors.INITIALIZATION_USING_BACKING_FIELD.on(simpleNameExpression, expression, variableDescriptor));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Map<VariableDescriptor, InitializationPoints> lastInfo = traverser.getResultInfo();
|
recordInitializedVariables(declaredVariables, traverser.getResultInfo());
|
||||||
for (Map.Entry<VariableDescriptor, InitializationPoints> entry : lastInfo.entrySet()) {
|
analyzeLocalDeclarations(processLocalDeclaration, pseudocode);
|
||||||
VariableDescriptor variable = entry.getKey();
|
}
|
||||||
if (variable instanceof PropertyDescriptor && declaredVariables.contains(variable)) {
|
|
||||||
InitializationPoints initializationPoints = entry.getValue();
|
private void checkIsInitialized(@NotNull VariableDescriptor variableDescriptor, @NotNull JetElement element, @NotNull VariableInitializers variableInitializers, @NotNull Collection<VariableDescriptor> varWithUninitializedErrorGenerated) {
|
||||||
trace.record(BindingContext.IS_INITIALIZED, (PropertyDescriptor) variable, initializationPoints.isInitialized());
|
if (!(element instanceof JetSimpleNameExpression)) return;
|
||||||
|
|
||||||
|
boolean isInitialized = variableInitializers.isInitialized();
|
||||||
|
if (variableDescriptor instanceof PropertyDescriptor) {
|
||||||
|
if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)) {
|
||||||
|
isInitialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Instruction instruction : pseudocode.getInstructions()) {
|
if (!isInitialized && !varWithUninitializedErrorGenerated.contains(variableDescriptor)) {
|
||||||
if (instruction instanceof LocalDeclarationInstruction) {
|
varWithUninitializedErrorGenerated.add(variableDescriptor);
|
||||||
JetElement element = ((LocalDeclarationInstruction) instruction).getElement();
|
trace.report(Errors.UNINITIALIZED_VARIABLE.on((JetSimpleNameExpression) element, variableDescriptor));
|
||||||
markUninitializedVariables(element, false, inLocalDeclaration);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkValReassignment(@NotNull VariableDescriptor variableDescriptor, @NotNull JetExpression expression, @NotNull VariableInitializers enterInitializers, @NotNull Collection<VariableDescriptor> varWithValReassignErrorGenerated) {
|
||||||
|
boolean isInitializedNotHere = enterInitializers.isInitialized();
|
||||||
|
Set<JetElement> possibleLocalInitializers = enterInitializers.getPossibleLocalInitializers();
|
||||||
|
if (possibleLocalInitializers.size() == 1) {
|
||||||
|
JetElement initializer = possibleLocalInitializers.iterator().next();
|
||||||
|
if (initializer instanceof JetProperty && initializer == expression.getParent()) {
|
||||||
|
isInitializedNotHere = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boolean hasBackingField = true;
|
||||||
|
if (variableDescriptor instanceof PropertyDescriptor) {
|
||||||
|
hasBackingField = trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor);
|
||||||
|
}
|
||||||
|
if ((isInitializedNotHere || !hasBackingField) && !variableDescriptor.isVar() && !varWithValReassignErrorGenerated.contains(variableDescriptor)) {
|
||||||
|
varWithValReassignErrorGenerated.add(variableDescriptor);
|
||||||
|
|
||||||
|
boolean hasReassignMethodReturningUnit = false;
|
||||||
|
JetSimpleNameExpression operationReference = null;
|
||||||
|
PsiElement parent = expression.getParent();
|
||||||
|
if (parent instanceof JetBinaryExpression) {
|
||||||
|
operationReference = ((JetBinaryExpression) parent).getOperationReference();
|
||||||
|
}
|
||||||
|
else if (parent instanceof JetUnaryExpression) {
|
||||||
|
operationReference = ((JetUnaryExpression) parent).getOperationSign();
|
||||||
|
}
|
||||||
|
if (operationReference != null) {
|
||||||
|
DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, operationReference);
|
||||||
|
if (descriptor instanceof FunctionDescriptor) {
|
||||||
|
if (JetStandardClasses.isUnit(((FunctionDescriptor) descriptor).getReturnType())) {
|
||||||
|
hasReassignMethodReturningUnit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasReassignMethodReturningUnit) {
|
||||||
|
trace.report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkInitializationUsingBackingField(@NotNull VariableDescriptor variableDescriptor, @NotNull JetExpression expression, @NotNull VariableInitializers enterInitializers, @NotNull VariableInitializers exitInitializers) {
|
||||||
|
if (variableDescriptor instanceof PropertyDescriptor && !enterInitializers.isInitialized() && exitInitializers.isInitialized()) {
|
||||||
|
JetExpression variable = expression;
|
||||||
|
if (expression instanceof JetDotQualifiedExpression) {
|
||||||
|
if (((JetDotQualifiedExpression) expression).getReceiverExpression() instanceof JetThisExpression) {
|
||||||
|
variable = ((JetDotQualifiedExpression) expression).getSelectorExpression();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (variable instanceof JetSimpleNameExpression) {
|
||||||
|
JetSimpleNameExpression simpleNameExpression = (JetSimpleNameExpression) variable;
|
||||||
|
if (simpleNameExpression.getReferencedNameElementType() != JetTokens.FIELD_IDENTIFIER) {
|
||||||
|
trace.report(Errors.INITIALIZATION_USING_BACKING_FIELD.on(simpleNameExpression, expression, variableDescriptor));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<VariableDescriptor, InitializationPoints> prepareInitialMapForStartInstruction(Collection<VariableDescriptor> usedVariables, Collection<VariableDescriptor> declaredVariables) {
|
private void recordInitializedVariables(Collection<VariableDescriptor> declaredVariables, Map<VariableDescriptor, VariableInitializers> resultInfo) {
|
||||||
Map<VariableDescriptor, InitializationPoints> initialMapForStartInstruction = Maps.newHashMap();
|
for (Map.Entry<VariableDescriptor, VariableInitializers> entry : resultInfo.entrySet()) {
|
||||||
InitializationPoints initialPointsForDeclaredVariable = new InitializationPoints(false);
|
VariableDescriptor variable = entry.getKey();
|
||||||
InitializationPoints initialPointsForExternalVariable = new InitializationPoints(true);
|
if (variable instanceof PropertyDescriptor && declaredVariables.contains(variable)) {
|
||||||
|
VariableInitializers initializers = entry.getValue();
|
||||||
|
trace.record(BindingContext.IS_INITIALIZED, (PropertyDescriptor) variable, initializers.isInitialized());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void analyzeLocalDeclarations(boolean processLocalDeclaration, Pseudocode pseudocode) {
|
||||||
|
for (Instruction instruction : pseudocode.getInstructions()) {
|
||||||
|
if (instruction instanceof LocalDeclarationInstruction) {
|
||||||
|
JetElement element = ((LocalDeclarationInstruction) instruction).getElement();
|
||||||
|
markUninitializedVariables(element, processLocalDeclaration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<VariableDescriptor, VariableInitializers> addVariableInitializerFromCurrentInstructionIfAny(Instruction instruction, Map<VariableDescriptor, VariableInitializers> enterInstructionData) {
|
||||||
|
Map<VariableDescriptor, VariableInitializers> exitInstructionData = Maps.newHashMap(enterInstructionData);
|
||||||
|
if (instruction instanceof WriteValueInstruction) {
|
||||||
|
VariableDescriptor variable = extractVariableDescriptorIfAny(instruction, false);
|
||||||
|
VariableInitializers initializationAtThisElement = new VariableInitializers(((WriteValueInstruction) instruction).getElement());
|
||||||
|
exitInstructionData.put(variable, initializationAtThisElement);
|
||||||
|
}
|
||||||
|
return exitInstructionData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<VariableDescriptor, VariableInitializers> mergeIncomingEdgesData(Collection<Map<VariableDescriptor, VariableInitializers>> incomingEdgesData) {
|
||||||
|
Set<VariableDescriptor> variablesInScope = Sets.newHashSet();
|
||||||
|
for (Map<VariableDescriptor, VariableInitializers> edgeData : incomingEdgesData) {
|
||||||
|
variablesInScope.addAll(edgeData.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<VariableDescriptor, VariableInitializers> enterInstructionData = Maps.newHashMap();
|
||||||
|
for (VariableDescriptor variable : variablesInScope) {
|
||||||
|
Set<VariableInitializers> edgesDataForVariable = Sets.newHashSet();
|
||||||
|
for (Map<VariableDescriptor, VariableInitializers> edgeData : incomingEdgesData) {
|
||||||
|
VariableInitializers initializers = edgeData.get(variable);
|
||||||
|
if (initializers != null) {
|
||||||
|
edgesDataForVariable.add(initializers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enterInstructionData.put(variable, new VariableInitializers(edgesDataForVariable));
|
||||||
|
}
|
||||||
|
return enterInstructionData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<VariableDescriptor, VariableInitializers> prepareInitialMapForStartInstruction(Collection<VariableDescriptor> usedVariables, Collection<VariableDescriptor> declaredVariables) {
|
||||||
|
Map<VariableDescriptor, VariableInitializers> initialMapForStartInstruction = Maps.newHashMap();
|
||||||
|
VariableInitializers isInitializedForExternalVariable = new VariableInitializers(true);
|
||||||
|
VariableInitializers isNotInitializedForDeclaredVariable = new VariableInitializers(false);
|
||||||
|
|
||||||
for (VariableDescriptor variable : usedVariables) {
|
for (VariableDescriptor variable : usedVariables) {
|
||||||
if (declaredVariables.contains(variable)) {
|
if (declaredVariables.contains(variable)) {
|
||||||
initialMapForStartInstruction.put(variable, initialPointsForDeclaredVariable);
|
initialMapForStartInstruction.put(variable, isNotInitializedForDeclaredVariable);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
initialMapForStartInstruction.put(variable, initialPointsForExternalVariable);
|
initialMapForStartInstruction.put(variable, isInitializedForExternalVariable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return initialMapForStartInstruction;
|
return initialMapForStartInstruction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void markNotOnlyInvokedFunctionVariables(@NotNull JetElement subroutine, List<? extends VariableDescriptor> variables) {
|
public void markNotOnlyInvokedFunctionVariables(@NotNull JetElement subroutine, List<? extends VariableDescriptor> variables) {
|
||||||
final List<VariableDescriptor> functionVariables = Lists.newArrayList();
|
final List<VariableDescriptor> functionVariables = Lists.newArrayList();
|
||||||
for (VariableDescriptor variable : variables) {
|
for (VariableDescriptor variable : variables) {
|
||||||
@@ -384,7 +412,10 @@ public class JetFlowInformationProvider {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// "Unused variable" & "unused value" analyses
|
||||||
|
|
||||||
public void markUnusedVariables(@NotNull JetElement subroutine) {
|
public void markUnusedVariables(@NotNull JetElement subroutine) {
|
||||||
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
||||||
assert pseudocode != null;
|
assert pseudocode != null;
|
||||||
@@ -403,10 +434,11 @@ public class JetFlowInformationProvider {
|
|||||||
for (VariableDescriptor declaredVariable : declaredVariables) {
|
for (VariableDescriptor declaredVariable : declaredVariables) {
|
||||||
if (!usedVariables.contains(declaredVariable)) {
|
if (!usedVariables.contains(declaredVariable)) {
|
||||||
PsiElement element = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, declaredVariable);
|
PsiElement element = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, declaredVariable);
|
||||||
if (element instanceof JetProperty && JetPsiUtil.isLocal((JetProperty) element)) {
|
//todo
|
||||||
PsiElement nameIdentifier = ((JetProperty) element).getNameIdentifier();
|
if (element instanceof JetProperty && JetPsiUtil.isLocal((JetNamedDeclaration) element)) {
|
||||||
|
PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier();
|
||||||
PsiElement elementToMark = nameIdentifier != null ? nameIdentifier : element;
|
PsiElement elementToMark = nameIdentifier != null ? nameIdentifier : element;
|
||||||
trace.report(Errors.UNUSED_VARIABLE.on((JetProperty)element, elementToMark, declaredVariable));
|
trace.report(Errors.UNUSED_VARIABLE.on((JetNamedDeclaration)element, elementToMark, declaredVariable));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -416,7 +448,7 @@ public class JetFlowInformationProvider {
|
|||||||
|
|
||||||
private void markUnusedValues(@NotNull JetElement subroutine, Pseudocode pseudocode, final Collection<VariableDescriptor> declaredVariables) {
|
private void markUnusedValues(@NotNull JetElement subroutine, Pseudocode pseudocode, final Collection<VariableDescriptor> declaredVariables) {
|
||||||
JetControlFlowGraphTraverser<Set<VariableDescriptor>> traverser = JetControlFlowGraphTraverser.create(pseudocode, true);
|
JetControlFlowGraphTraverser<Set<VariableDescriptor>> traverser = JetControlFlowGraphTraverser.create(pseudocode, true);
|
||||||
traverser.collectInformationFromInstructionGraph(new JetControlFlowGraphTraverser.InstructionsMergeStrategy<Set<VariableDescriptor>>() {
|
traverser.collectInformationFromInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataMergeStrategy<Set<VariableDescriptor>>() {
|
||||||
@Override
|
@Override
|
||||||
public Pair<Set<VariableDescriptor>, Set<VariableDescriptor>> execute(Instruction instruction, @NotNull Collection<Set<VariableDescriptor>> incomingEdgesData) {
|
public Pair<Set<VariableDescriptor>, Set<VariableDescriptor>> execute(Instruction instruction, @NotNull Collection<Set<VariableDescriptor>> incomingEdgesData) {
|
||||||
Set<VariableDescriptor> enterResult = Sets.newHashSet();
|
Set<VariableDescriptor> enterResult = Sets.newHashSet();
|
||||||
@@ -448,8 +480,10 @@ public class JetFlowInformationProvider {
|
|||||||
if (variableDescriptor != null && declaredVariables.contains(variableDescriptor)) {
|
if (variableDescriptor != null && declaredVariables.contains(variableDescriptor)) {
|
||||||
if (!enterData.contains(variableDescriptor)) {
|
if (!enterData.contains(variableDescriptor)) {
|
||||||
PsiElement variableDeclarationElement = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor);
|
PsiElement variableDeclarationElement = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor);
|
||||||
assert variableDeclarationElement instanceof JetProperty || variableDeclarationElement instanceof JetParameter;
|
// assert variableDeclarationElement instanceof JetProperty || variableDeclarationElement instanceof JetParameter;
|
||||||
boolean isLocal = !(variableDeclarationElement instanceof JetProperty) || JetPsiUtil.isLocal((JetProperty) variableDeclarationElement);
|
// boolean isLocal = !(variableDeclarationElement instanceof JetProperty) || JetPsiUtil.isLocal((JetProperty) variableDeclarationElement);
|
||||||
|
assert variableDeclarationElement instanceof JetDeclaration;
|
||||||
|
boolean isLocal = JetPsiUtil.isLocal((JetDeclaration) variableDeclarationElement);
|
||||||
if (isLocal) {
|
if (isLocal) {
|
||||||
JetElement element = ((WriteValueInstruction) instruction).getElement();
|
JetElement element = ((WriteValueInstruction) instruction).getElement();
|
||||||
if (element instanceof JetBinaryExpression && ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) {
|
if (element instanceof JetBinaryExpression && ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) {
|
||||||
@@ -472,6 +506,9 @@ public class JetFlowInformationProvider {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Util methods
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private VariableDescriptor extractVariableDescriptorIfAny(Instruction instruction, boolean onlyReference) {
|
private VariableDescriptor extractVariableDescriptorIfAny(Instruction instruction, boolean onlyReference) {
|
||||||
VariableDescriptor variableDescriptor = null;
|
VariableDescriptor variableDescriptor = null;
|
||||||
@@ -521,31 +558,34 @@ public class JetFlowInformationProvider {
|
|||||||
return declaredVariables;
|
return declaredVariables;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class InitializationPoints {
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
private Set<JetElement> possiblePoints = Sets.newHashSet();
|
// Local class for uninitialized variables analysis
|
||||||
|
|
||||||
|
private static class VariableInitializers {
|
||||||
|
private Set<JetElement> possibleLocalInitializers = Sets.newHashSet();
|
||||||
private boolean isInitialized;
|
private boolean isInitialized;
|
||||||
|
|
||||||
public InitializationPoints(boolean isInitialized) {
|
public VariableInitializers(boolean isInitialized) {
|
||||||
this.isInitialized = isInitialized;
|
this.isInitialized = isInitialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InitializationPoints(JetElement element) {
|
public VariableInitializers(JetElement element) {
|
||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
possiblePoints.add(element);
|
possibleLocalInitializers.add(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
public InitializationPoints(Set<InitializationPoints> edgesData) {
|
public VariableInitializers(Set<VariableInitializers> edgesData) {
|
||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
for (InitializationPoints edgeData : edgesData) {
|
for (VariableInitializers edgeData : edgesData) {
|
||||||
if (!edgeData.isInitialized) {
|
if (!edgeData.isInitialized) {
|
||||||
isInitialized = false;
|
isInitialized = false;
|
||||||
}
|
}
|
||||||
possiblePoints.addAll(edgeData.possiblePoints);
|
possibleLocalInitializers.addAll(edgeData.possibleLocalInitializers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<JetElement> getPossiblePoints() {
|
public Set<JetElement> getPossibleLocalInitializers() {
|
||||||
return possiblePoints;
|
return possibleLocalInitializers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInitialized() {
|
public boolean isInitialized() {
|
||||||
@@ -555,12 +595,12 @@ public class JetFlowInformationProvider {
|
|||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (!(o instanceof InitializationPoints)) return false;
|
if (!(o instanceof VariableInitializers)) return false;
|
||||||
|
|
||||||
InitializationPoints that = (InitializationPoints) o;
|
VariableInitializers that = (VariableInitializers) o;
|
||||||
|
|
||||||
if (isInitialized != that.isInitialized) return false;
|
if (isInitialized != that.isInitialized) return false;
|
||||||
if (possiblePoints != null ? !possiblePoints.equals(that.possiblePoints) : that.possiblePoints != null) {
|
if (possibleLocalInitializers != null ? !possibleLocalInitializers.equals(that.possibleLocalInitializers) : that.possibleLocalInitializers != null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,7 +609,7 @@ public class JetFlowInformationProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result = possiblePoints != null ? possiblePoints.hashCode() : 0;
|
int result = possibleLocalInitializers != null ? possibleLocalInitializers.hashCode() : 0;
|
||||||
result = 31 * result + (isInitialized ? 1 : 0);
|
result = 31 * result + (isInitialized ? 1 : 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,9 +104,9 @@ public class JetPsiUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isLocal(@NotNull JetProperty property) {
|
public static boolean isLocal(@NotNull JetDeclaration declaration) {
|
||||||
JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(property, JetClassOrObject.class);
|
JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(declaration, JetClassOrObject.class);
|
||||||
JetDeclarationWithBody function = PsiTreeUtil.getParentOfType(property, JetDeclarationWithBody.class);
|
JetDeclarationWithBody function = PsiTreeUtil.getParentOfType(declaration, JetDeclarationWithBody.class);
|
||||||
if (function != null && PsiTreeUtil.isAncestor(classOrObject, function, false)) {
|
if (function != null && PsiTreeUtil.isAncestor(classOrObject, function, false)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import static org.jetbrains.jet.lang.resolve.BindingContext.DECLARATION_TO_DESCR
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @author abreslav
|
* @author abreslav
|
||||||
|
* @author svtk
|
||||||
*/
|
*/
|
||||||
public class BindingContextUtils {
|
public class BindingContextUtils {
|
||||||
private BindingContextUtils() {
|
private BindingContextUtils() {
|
||||||
|
|||||||
@@ -19,12 +19,12 @@ import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
|
|||||||
public class ControlFlowAnalyzer {
|
public class ControlFlowAnalyzer {
|
||||||
private TopDownAnalysisContext context;
|
private TopDownAnalysisContext context;
|
||||||
private final JetControlFlowDataTraceFactory flowDataTraceFactory;
|
private final JetControlFlowDataTraceFactory flowDataTraceFactory;
|
||||||
private final boolean inLocalDeclaration;
|
private final boolean processLocalDeclaration;
|
||||||
|
|
||||||
public ControlFlowAnalyzer(TopDownAnalysisContext context, JetControlFlowDataTraceFactory flowDataTraceFactory, boolean inLocalDeclaration) {
|
public ControlFlowAnalyzer(TopDownAnalysisContext context, JetControlFlowDataTraceFactory flowDataTraceFactory, boolean processLocalDeclaration) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.flowDataTraceFactory = flowDataTraceFactory;
|
this.flowDataTraceFactory = flowDataTraceFactory;
|
||||||
this.inLocalDeclaration = inLocalDeclaration;
|
this.processLocalDeclaration = processLocalDeclaration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process() {
|
public void process() {
|
||||||
@@ -53,7 +53,7 @@ public class ControlFlowAnalyzer {
|
|||||||
|
|
||||||
private void checkClassOrObject(JetClassOrObject klass) {
|
private void checkClassOrObject(JetClassOrObject klass) {
|
||||||
JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider((JetDeclaration) klass, (JetExpression) klass, flowDataTraceFactory, context.getTrace());
|
JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider((JetDeclaration) klass, (JetExpression) klass, flowDataTraceFactory, context.getTrace());
|
||||||
flowInformationProvider.markUninitializedVariables((JetElement) klass, true, inLocalDeclaration);
|
flowInformationProvider.markUninitializedVariables((JetElement) klass, processLocalDeclaration);
|
||||||
|
|
||||||
List<JetDeclaration> declarations = klass.getDeclarations();
|
List<JetDeclaration> declarations = klass.getDeclarations();
|
||||||
for (JetDeclaration declaration : declarations) {
|
for (JetDeclaration declaration : declarations) {
|
||||||
@@ -82,7 +82,7 @@ public class ControlFlowAnalyzer {
|
|||||||
|
|
||||||
flowInformationProvider.checkDefiniteReturn(function, expectedReturnType);
|
flowInformationProvider.checkDefiniteReturn(function, expectedReturnType);
|
||||||
|
|
||||||
flowInformationProvider.markUninitializedVariables(function.asElement(), false, inLocalDeclaration);
|
flowInformationProvider.markUninitializedVariables(function.asElement(), processLocalDeclaration);
|
||||||
|
|
||||||
flowInformationProvider.markUnusedVariables(function.asElement());
|
flowInformationProvider.markUnusedVariables(function.asElement());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public class TopDownAnalyzer {
|
|||||||
NamespaceLike owner,
|
NamespaceLike owner,
|
||||||
Collection<? extends JetDeclaration> declarations,
|
Collection<? extends JetDeclaration> declarations,
|
||||||
JetControlFlowDataTraceFactory flowDataTraceFactory,
|
JetControlFlowDataTraceFactory flowDataTraceFactory,
|
||||||
boolean declaredLocally) {
|
boolean processLocalDeclaration) {
|
||||||
// context.enableDebugOutput();
|
// context.enableDebugOutput();
|
||||||
context.debug("Enter");
|
context.debug("Enter");
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ public class TopDownAnalyzer {
|
|||||||
new OverloadResolver(context).process();
|
new OverloadResolver(context).process();
|
||||||
if (!context.analyzingBootstrapLibrary()) {
|
if (!context.analyzingBootstrapLibrary()) {
|
||||||
new BodyResolver(context).resolveBehaviorDeclarationBodies();
|
new BodyResolver(context).resolveBehaviorDeclarationBodies();
|
||||||
new ControlFlowAnalyzer(context, flowDataTraceFactory, declaredLocally).process();
|
new ControlFlowAnalyzer(context, flowDataTraceFactory, processLocalDeclaration).process();
|
||||||
new DeclarationsChecker(context).process();
|
new DeclarationsChecker(context).process();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -137,8 +137,8 @@ class AnonymousInitializers(var a: String, val b: String) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
<!VAL_REASSIGNMENT!>$i<!> = 13
|
<!VAL_REASSIGNMENT!>$i<!> = 13
|
||||||
<!NO_BACKING_FIELD!>$j<!> = 30
|
<!NO_BACKING_FIELD, VAL_REASSIGNMENT!>$j<!> = 30
|
||||||
<!VAL_REASSIGNMENT!>j<!> = 34
|
j = 34
|
||||||
}
|
}
|
||||||
|
|
||||||
val k: String
|
val k: String
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
//KT-607 Val reassignment is not marked as an error
|
||||||
|
|
||||||
|
namespace kt607
|
||||||
|
|
||||||
|
fun foo(a: A) {
|
||||||
|
val o = object {
|
||||||
|
val y : Int
|
||||||
|
get() = 42
|
||||||
|
}
|
||||||
|
|
||||||
|
<!VAL_REASSIGNMENT!>a.z<!> = 23
|
||||||
|
<!VAL_REASSIGNMENT!>o.y<!> = 11 //Should be an error here
|
||||||
|
}
|
||||||
|
|
||||||
|
class A() {
|
||||||
|
val z : Int
|
||||||
|
get() = 3
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user