refactoring & KT-607 Val reassignment is not marked as an error

This commit is contained in:
svtk
2011-11-25 15:14:38 +04:00
parent 2fcc18f08b
commit c3447c3576
8 changed files with 228 additions and 169 deletions
@@ -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);
} }
@@ -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 Map<VariableDescriptor, InitializationPoints> prepareInitialMapForStartInstruction(Collection<VariableDescriptor> usedVariables, Collection<VariableDescriptor> declaredVariables) { private void checkInitializationUsingBackingField(@NotNull VariableDescriptor variableDescriptor, @NotNull JetExpression expression, @NotNull VariableInitializers enterInitializers, @NotNull VariableInitializers exitInitializers) {
Map<VariableDescriptor, InitializationPoints> initialMapForStartInstruction = Maps.newHashMap(); if (variableDescriptor instanceof PropertyDescriptor && !enterInitializers.isInitialized() && exitInitializers.isInitialized()) {
InitializationPoints initialPointsForDeclaredVariable = new InitializationPoints(false); JetExpression variable = expression;
InitializationPoints initialPointsForExternalVariable = new InitializationPoints(true); 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 void recordInitializedVariables(Collection<VariableDescriptor> declaredVariables, Map<VariableDescriptor, VariableInitializers> resultInfo) {
for (Map.Entry<VariableDescriptor, VariableInitializers> entry : resultInfo.entrySet()) {
VariableDescriptor variable = entry.getKey();
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) {
@@ -385,6 +413,9 @@ 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();
} }
@@ -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
}