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(
InstructionsMergeStrategy<D> instructionsMergeStrategy,
InstructionDataMergeStrategy<D> instructionDataMergeStrategy,
D initialDataValue,
D initialDataValueForEnterInstruction,
boolean straightDirection) {
@@ -43,7 +43,7 @@ public class JetControlFlowGraphTraverser<D> {
changed[0] = true;
while (changed[0]) {
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(
Pseudocode pseudocode,
InstructionsMergeStrategy<D> instructionsMergeStrategy,
InstructionDataMergeStrategy<D> instructionDataMergeStrategy,
Collection<Instruction> previousSubGraphInstructions,
boolean straightDirection,
boolean[] changed,
@@ -87,7 +87,7 @@ public class JetControlFlowGraphTraverser<D> {
if (lookInside && instruction instanceof LocalDeclarationInstruction) {
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);
@@ -99,7 +99,7 @@ public class JetControlFlowGraphTraverser<D> {
incomingEdgesData.add(previousData.getSecond());
}
}
Pair<D, D> mergedData = instructionsMergeStrategy.execute(instruction, incomingEdgesData);
Pair<D, D> mergedData = instructionDataMergeStrategy.execute(instruction, incomingEdgesData);
if (!mergedData.equals(previousDataValue)) {
changed[0] = true;
dataMap.put(instruction, mergedData);
@@ -132,7 +132,7 @@ public class JetControlFlowGraphTraverser<D> {
return dataMap.get(pseudocode.getSinkInstruction()).getFirst();
}
interface InstructionsMergeStrategy<D> {
interface InstructionDataMergeStrategy<D> {
Pair<D, D> execute(Instruction instruction, @NotNull Collection<D> incomingEdgesData);
}
@@ -187,176 +187,204 @@ public class JetFlowInformationProvider {
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);
assert pseudocode != null;
JetControlFlowGraphTraverser<Map<VariableDescriptor, InitializationPoints>> 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);
}
};
JetControlFlowGraphTraverser<Map<VariableDescriptor, VariableInitializers>> traverser = JetControlFlowGraphTraverser.create(pseudocode, false);
Collection<VariableDescriptor> usedVariables = collectUsedVariables(pseudocode);
final Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
Map<VariableDescriptor, InitializationPoints> initialMapForStartInstruction = prepareInitialMapForStartInstruction(usedVariables, declaredVariables);
traverser.collectInformationFromInstructionGraph(instructionsMergeStrategy,
Collections.<VariableDescriptor, InitializationPoints>emptyMap(),
initialMapForStartInstruction,
true);
Map<VariableDescriptor, VariableInitializers> initialMapForStartInstruction = prepareInitialMapForStartInstruction(usedVariables, declaredVariables);
JetControlFlowGraphTraverser.InstructionDataMergeStrategy<Map<VariableDescriptor, VariableInitializers>> variableInitializersMergeStrategy =
new JetControlFlowGraphTraverser.InstructionDataMergeStrategy<Map<VariableDescriptor, VariableInitializers>>() {
@Override
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> 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
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;
if (instruction instanceof ReadValueInstruction) {
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
JetElement element = ((ReadValueInstruction) instruction).getElement();
if (element instanceof JetSimpleNameExpression && variableDescriptor != null && declaredVariables.contains(variableDescriptor)) {
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));
}
if (variableDescriptor != null && declaredVariables.contains(variableDescriptor)) {
checkIsInitialized(variableDescriptor, ((ReadValueInstruction) instruction).getElement(), exitData.get(variableDescriptor), varWithUninitializedErrorGenerated);
}
}
else if (instruction instanceof WriteValueInstruction) {
JetElement element = ((WriteValueInstruction) instruction).getlValue();
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true);
if (element instanceof JetExpression && variableDescriptor != null) {
InitializationPoints enterInitializationPoints = enterData.get(variableDescriptor);
assert enterInitializationPoints != null;
InitializationPoints exitInitializationPoints = exitData.get(variableDescriptor);
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;
}
JetElement element = ((WriteValueInstruction) instruction).getlValue();
if (variableDescriptor != null && element instanceof JetExpression) {
if (!processLocalDeclaration) { // error has been generated before while processing outer function of this local declaration
checkValReassignment(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), varWithValReassignErrorGenerated);
}
JetExpression expression = (JetExpression) element;
if (!inLocalDeclaration && hasInitializer && !variableDescriptor.isVar() && !varWithValReassignErrorGenerated.contains(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));
}
}
if (processClassOrObject) {
checkInitializationUsingBackingField(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), exitData.get(variableDescriptor));
}
}
}
}
});
Map<VariableDescriptor, InitializationPoints> lastInfo = traverser.getResultInfo();
for (Map.Entry<VariableDescriptor, InitializationPoints> entry : lastInfo.entrySet()) {
VariableDescriptor variable = entry.getKey();
if (variable instanceof PropertyDescriptor && declaredVariables.contains(variable)) {
InitializationPoints initializationPoints = entry.getValue();
trace.record(BindingContext.IS_INITIALIZED, (PropertyDescriptor) variable, initializationPoints.isInitialized());
recordInitializedVariables(declaredVariables, traverser.getResultInfo());
analyzeLocalDeclarations(processLocalDeclaration, pseudocode);
}
private void checkIsInitialized(@NotNull VariableDescriptor variableDescriptor, @NotNull JetElement element, @NotNull VariableInitializers variableInitializers, @NotNull Collection<VariableDescriptor> varWithUninitializedErrorGenerated) {
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 (instruction instanceof LocalDeclarationInstruction) {
JetElement element = ((LocalDeclarationInstruction) instruction).getElement();
markUninitializedVariables(element, false, inLocalDeclaration);
if (!isInitialized && !varWithUninitializedErrorGenerated.contains(variableDescriptor)) {
varWithUninitializedErrorGenerated.add(variableDescriptor);
trace.report(Errors.UNINITIALIZED_VARIABLE.on((JetSimpleNameExpression) element, variableDescriptor));
}
}
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) {
Map<VariableDescriptor, InitializationPoints> initialMapForStartInstruction = Maps.newHashMap();
InitializationPoints initialPointsForDeclaredVariable = new InitializationPoints(false);
InitializationPoints initialPointsForExternalVariable = new InitializationPoints(true);
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) {
if (declaredVariables.contains(variable)) {
initialMapForStartInstruction.put(variable, initialPointsForDeclaredVariable);
initialMapForStartInstruction.put(variable, isNotInitializedForDeclaredVariable);
}
else {
initialMapForStartInstruction.put(variable, initialPointsForExternalVariable);
initialMapForStartInstruction.put(variable, isInitializedForExternalVariable);
}
}
return initialMapForStartInstruction;
}
////////////////////////////////////////////////////////////////////////////////
public void markNotOnlyInvokedFunctionVariables(@NotNull JetElement subroutine, List<? extends VariableDescriptor> variables) {
final List<VariableDescriptor> functionVariables = Lists.newArrayList();
for (VariableDescriptor variable : variables) {
@@ -384,7 +412,10 @@ public class JetFlowInformationProvider {
}
});
}
////////////////////////////////////////////////////////////////////////////////
// "Unused variable" & "unused value" analyses
public void markUnusedVariables(@NotNull JetElement subroutine) {
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
assert pseudocode != null;
@@ -403,10 +434,11 @@ public class JetFlowInformationProvider {
for (VariableDescriptor declaredVariable : declaredVariables) {
if (!usedVariables.contains(declaredVariable)) {
PsiElement element = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, declaredVariable);
if (element instanceof JetProperty && JetPsiUtil.isLocal((JetProperty) element)) {
PsiElement nameIdentifier = ((JetProperty) element).getNameIdentifier();
//todo
if (element instanceof JetProperty && JetPsiUtil.isLocal((JetNamedDeclaration) element)) {
PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier();
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) {
JetControlFlowGraphTraverser<Set<VariableDescriptor>> traverser = JetControlFlowGraphTraverser.create(pseudocode, true);
traverser.collectInformationFromInstructionGraph(new JetControlFlowGraphTraverser.InstructionsMergeStrategy<Set<VariableDescriptor>>() {
traverser.collectInformationFromInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataMergeStrategy<Set<VariableDescriptor>>() {
@Override
public Pair<Set<VariableDescriptor>, Set<VariableDescriptor>> execute(Instruction instruction, @NotNull Collection<Set<VariableDescriptor>> incomingEdgesData) {
Set<VariableDescriptor> enterResult = Sets.newHashSet();
@@ -448,8 +480,10 @@ public class JetFlowInformationProvider {
if (variableDescriptor != null && declaredVariables.contains(variableDescriptor)) {
if (!enterData.contains(variableDescriptor)) {
PsiElement variableDeclarationElement = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor);
assert variableDeclarationElement instanceof JetProperty || variableDeclarationElement instanceof JetParameter;
boolean isLocal = !(variableDeclarationElement instanceof JetProperty) || JetPsiUtil.isLocal((JetProperty) variableDeclarationElement);
// assert variableDeclarationElement instanceof JetProperty || variableDeclarationElement instanceof JetParameter;
// boolean isLocal = !(variableDeclarationElement instanceof JetProperty) || JetPsiUtil.isLocal((JetProperty) variableDeclarationElement);
assert variableDeclarationElement instanceof JetDeclaration;
boolean isLocal = JetPsiUtil.isLocal((JetDeclaration) variableDeclarationElement);
if (isLocal) {
JetElement element = ((WriteValueInstruction) instruction).getElement();
if (element instanceof JetBinaryExpression && ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) {
@@ -472,6 +506,9 @@ public class JetFlowInformationProvider {
});
}
////////////////////////////////////////////////////////////////////////////////
// Util methods
@Nullable
private VariableDescriptor extractVariableDescriptorIfAny(Instruction instruction, boolean onlyReference) {
VariableDescriptor variableDescriptor = null;
@@ -521,31 +558,34 @@ public class JetFlowInformationProvider {
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;
public InitializationPoints(boolean isInitialized) {
public VariableInitializers(boolean isInitialized) {
this.isInitialized = isInitialized;
}
public InitializationPoints(JetElement element) {
public VariableInitializers(JetElement element) {
isInitialized = true;
possiblePoints.add(element);
possibleLocalInitializers.add(element);
}
public InitializationPoints(Set<InitializationPoints> edgesData) {
public VariableInitializers(Set<VariableInitializers> edgesData) {
isInitialized = true;
for (InitializationPoints edgeData : edgesData) {
for (VariableInitializers edgeData : edgesData) {
if (!edgeData.isInitialized) {
isInitialized = false;
}
possiblePoints.addAll(edgeData.possiblePoints);
possibleLocalInitializers.addAll(edgeData.possibleLocalInitializers);
}
}
public Set<JetElement> getPossiblePoints() {
return possiblePoints;
public Set<JetElement> getPossibleLocalInitializers() {
return possibleLocalInitializers;
}
public boolean isInitialized() {
@@ -555,12 +595,12 @@ public class JetFlowInformationProvider {
@Override
public boolean equals(Object o) {
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 (possiblePoints != null ? !possiblePoints.equals(that.possiblePoints) : that.possiblePoints != null) {
if (possibleLocalInitializers != null ? !possibleLocalInitializers.equals(that.possibleLocalInitializers) : that.possibleLocalInitializers != null) {
return false;
}
@@ -569,7 +609,7 @@ public class JetFlowInformationProvider {
@Override
public int hashCode() {
int result = possiblePoints != null ? possiblePoints.hashCode() : 0;
int result = possibleLocalInitializers != null ? possibleLocalInitializers.hashCode() : 0;
result = 31 * result + (isInitialized ? 1 : 0);
return result;
}
@@ -104,9 +104,9 @@ public class JetPsiUtil {
}
}
public static boolean isLocal(@NotNull JetProperty property) {
JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(property, JetClassOrObject.class);
JetDeclarationWithBody function = PsiTreeUtil.getParentOfType(property, JetDeclarationWithBody.class);
public static boolean isLocal(@NotNull JetDeclaration declaration) {
JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(declaration, JetClassOrObject.class);
JetDeclarationWithBody function = PsiTreeUtil.getParentOfType(declaration, JetDeclarationWithBody.class);
if (function != null && PsiTreeUtil.isAncestor(classOrObject, function, false)) {
return true;
}
@@ -13,6 +13,7 @@ import static org.jetbrains.jet.lang.resolve.BindingContext.DECLARATION_TO_DESCR
/**
* @author abreslav
* @author svtk
*/
public class BindingContextUtils {
private BindingContextUtils() {
@@ -19,12 +19,12 @@ import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
public class ControlFlowAnalyzer {
private TopDownAnalysisContext context;
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.flowDataTraceFactory = flowDataTraceFactory;
this.inLocalDeclaration = inLocalDeclaration;
this.processLocalDeclaration = processLocalDeclaration;
}
public void process() {
@@ -53,7 +53,7 @@ public class ControlFlowAnalyzer {
private void checkClassOrObject(JetClassOrObject klass) {
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();
for (JetDeclaration declaration : declarations) {
@@ -82,7 +82,7 @@ public class ControlFlowAnalyzer {
flowInformationProvider.checkDefiniteReturn(function, expectedReturnType);
flowInformationProvider.markUninitializedVariables(function.asElement(), false, inLocalDeclaration);
flowInformationProvider.markUninitializedVariables(function.asElement(), processLocalDeclaration);
flowInformationProvider.markUnusedVariables(function.asElement());
}
@@ -53,7 +53,7 @@ public class TopDownAnalyzer {
NamespaceLike owner,
Collection<? extends JetDeclaration> declarations,
JetControlFlowDataTraceFactory flowDataTraceFactory,
boolean declaredLocally) {
boolean processLocalDeclaration) {
// context.enableDebugOutput();
context.debug("Enter");
@@ -64,7 +64,7 @@ public class TopDownAnalyzer {
new OverloadResolver(context).process();
if (!context.analyzingBootstrapLibrary()) {
new BodyResolver(context).resolveBehaviorDeclarationBodies();
new ControlFlowAnalyzer(context, flowDataTraceFactory, declaredLocally).process();
new ControlFlowAnalyzer(context, flowDataTraceFactory, processLocalDeclaration).process();
new DeclarationsChecker(context).process();
}
@@ -137,8 +137,8 @@ class AnonymousInitializers(var a: String, val b: String) {
{
<!VAL_REASSIGNMENT!>$i<!> = 13
<!NO_BACKING_FIELD!>$j<!> = 30
<!VAL_REASSIGNMENT!>j<!> = 34
<!NO_BACKING_FIELD, VAL_REASSIGNMENT!>$j<!> = 30
j = 34
}
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
}