KT-897 Don't allow assignment to a property before it is defined
This commit is contained in:
+17
-17
@@ -1,6 +1,5 @@
|
|||||||
package org.jetbrains.jet.lang.cfg;
|
package org.jetbrains.jet.lang.cfg;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
@@ -23,24 +22,25 @@ public class JetControlFlowGraphTraverser<D> {
|
|||||||
private final boolean straightDirection;
|
private final boolean straightDirection;
|
||||||
private final Map<Instruction, Pair<D, D>> dataMap = Maps.newLinkedHashMap();
|
private final Map<Instruction, Pair<D, D>> dataMap = Maps.newLinkedHashMap();
|
||||||
|
|
||||||
public static <D> JetControlFlowGraphTraverser<D> create(Pseudocode pseudocode, boolean lookInside, boolean straightDirection) {
|
public static <D> JetControlFlowGraphTraverser<D> create(@NotNull Pseudocode pseudocode, boolean lookInside, boolean straightDirection) {
|
||||||
return new JetControlFlowGraphTraverser<D>(pseudocode, lookInside, straightDirection);
|
return new JetControlFlowGraphTraverser<D>(pseudocode, lookInside, straightDirection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private JetControlFlowGraphTraverser(Pseudocode pseudocode, boolean lookInside, boolean straightDirection) {
|
private JetControlFlowGraphTraverser(@NotNull Pseudocode pseudocode, boolean lookInside, boolean straightDirection) {
|
||||||
this.pseudocode = pseudocode;
|
this.pseudocode = pseudocode;
|
||||||
this.lookInside = lookInside;
|
this.lookInside = lookInside;
|
||||||
this.straightDirection = straightDirection;
|
this.straightDirection = straightDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Instruction getStartInstruction(Pseudocode pseudocode) {
|
@NotNull
|
||||||
|
private Instruction getStartInstruction(@NotNull Pseudocode pseudocode) {
|
||||||
return straightDirection ? pseudocode.getEnterInstruction() : pseudocode.getSinkInstruction();
|
return straightDirection ? pseudocode.getEnterInstruction() : pseudocode.getSinkInstruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void collectInformationFromInstructionGraph(
|
public void collectInformationFromInstructionGraph(
|
||||||
InstructionDataMergeStrategy<D> instructionDataMergeStrategy,
|
@NotNull D initialDataValue,
|
||||||
D initialDataValue,
|
@NotNull D initialDataValueForEnterInstruction,
|
||||||
D initialDataValueForEnterInstruction) {
|
@NotNull InstructionDataMergeStrategy<D> instructionDataMergeStrategy) {
|
||||||
initializeDataMap(pseudocode, initialDataValue);
|
initializeDataMap(pseudocode, initialDataValue);
|
||||||
dataMap.put(getStartInstruction(pseudocode),
|
dataMap.put(getStartInstruction(pseudocode),
|
||||||
Pair.create(initialDataValueForEnterInstruction, initialDataValueForEnterInstruction));
|
Pair.create(initialDataValueForEnterInstruction, initialDataValueForEnterInstruction));
|
||||||
@@ -54,8 +54,8 @@ public class JetControlFlowGraphTraverser<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initializeDataMap(
|
private void initializeDataMap(
|
||||||
Pseudocode pseudocode,
|
@NotNull Pseudocode pseudocode,
|
||||||
D initialDataValue) {
|
@NotNull D initialDataValue) {
|
||||||
List<Instruction> instructions = pseudocode.getInstructions();
|
List<Instruction> instructions = pseudocode.getInstructions();
|
||||||
Pair<D, D> initialPair = Pair.create(initialDataValue, initialDataValue);
|
Pair<D, D> initialPair = Pair.create(initialDataValue, initialDataValue);
|
||||||
for (Instruction instruction : instructions) {
|
for (Instruction instruction : instructions) {
|
||||||
@@ -67,9 +67,9 @@ public class JetControlFlowGraphTraverser<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void traverseSubGraph(
|
private void traverseSubGraph(
|
||||||
Pseudocode pseudocode,
|
@NotNull Pseudocode pseudocode,
|
||||||
InstructionDataMergeStrategy<D> instructionDataMergeStrategy,
|
@NotNull InstructionDataMergeStrategy<D> instructionDataMergeStrategy,
|
||||||
Collection<Instruction> previousSubGraphInstructions,
|
@NotNull Collection<Instruction> previousSubGraphInstructions,
|
||||||
boolean[] changed,
|
boolean[] changed,
|
||||||
boolean isLocal) {
|
boolean isLocal) {
|
||||||
List<Instruction> instructions = pseudocode.getInstructions();
|
List<Instruction> instructions = pseudocode.getInstructions();
|
||||||
@@ -127,13 +127,13 @@ public class JetControlFlowGraphTraverser<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void traverseAndAnalyzeInstructionGraph(
|
public void traverseAndAnalyzeInstructionGraph(
|
||||||
InstructionDataAnalyzeStrategy<D> instructionDataAnalyzeStrategy) {
|
@NotNull InstructionDataAnalyzeStrategy<D> instructionDataAnalyzeStrategy) {
|
||||||
traverseAndAnalyzeInstructionGraph(pseudocode, instructionDataAnalyzeStrategy);
|
traverseAndAnalyzeInstructionGraph(pseudocode, instructionDataAnalyzeStrategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void traverseAndAnalyzeInstructionGraph(
|
private void traverseAndAnalyzeInstructionGraph(
|
||||||
Pseudocode pseudocode,
|
@NotNull Pseudocode pseudocode,
|
||||||
InstructionDataAnalyzeStrategy<D> instructionDataAnalyzeStrategy) {
|
@NotNull InstructionDataAnalyzeStrategy<D> instructionDataAnalyzeStrategy) {
|
||||||
List<Instruction> instructions = pseudocode.getInstructions();
|
List<Instruction> instructions = pseudocode.getInstructions();
|
||||||
if (!straightDirection) {
|
if (!straightDirection) {
|
||||||
instructions = Lists.newArrayList(instructions);
|
instructions = Lists.newArrayList(instructions);
|
||||||
@@ -156,10 +156,10 @@ public class JetControlFlowGraphTraverser<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface InstructionDataMergeStrategy<D> {
|
interface InstructionDataMergeStrategy<D> {
|
||||||
Pair<D, D> execute(Instruction instruction, @NotNull Collection<D> incomingEdgesData);
|
Pair<D, D> execute(@NotNull Instruction instruction, @NotNull Collection<D> incomingEdgesData);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InstructionDataAnalyzeStrategy<D> {
|
interface InstructionDataAnalyzeStrategy<D> {
|
||||||
void execute(Instruction instruction, @Nullable D enterData, @Nullable D exitData);
|
void execute(@NotNull Instruction instruction, @Nullable D enterData, @Nullable D exitData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,27 +202,25 @@ public class JetFlowInformationProvider {
|
|||||||
final Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
|
final Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
|
||||||
Map<VariableDescriptor, VariableInitializers> initialMapForStartInstruction = prepareInitialMapForStartInstruction(usedVariables, declaredVariables);
|
Map<VariableDescriptor, VariableInitializers> initialMapForStartInstruction = prepareInitialMapForStartInstruction(usedVariables, declaredVariables);
|
||||||
|
|
||||||
JetControlFlowGraphTraverser.InstructionDataMergeStrategy<Map<VariableDescriptor, VariableInitializers>> variableInitializersMergeStrategy =
|
traverser.collectInformationFromInstructionGraph(Collections.<VariableDescriptor, VariableInitializers>emptyMap(), initialMapForStartInstruction,
|
||||||
new JetControlFlowGraphTraverser.InstructionDataMergeStrategy<Map<VariableDescriptor, VariableInitializers>>() {
|
new JetControlFlowGraphTraverser.InstructionDataMergeStrategy<Map<VariableDescriptor, VariableInitializers>>() {
|
||||||
@Override
|
@Override
|
||||||
public Pair<Map<VariableDescriptor, VariableInitializers>, Map<VariableDescriptor, VariableInitializers>> execute(
|
public Pair<Map<VariableDescriptor, VariableInitializers>, Map<VariableDescriptor, VariableInitializers>> execute(
|
||||||
Instruction instruction,
|
@NotNull Instruction instruction,
|
||||||
@NotNull Collection<Map<VariableDescriptor, VariableInitializers>> incomingEdgesData) {
|
@NotNull Collection<Map<VariableDescriptor, VariableInitializers>> incomingEdgesData) {
|
||||||
|
|
||||||
Map<VariableDescriptor, VariableInitializers> enterInstructionData = mergeIncomingEdgesData(incomingEdgesData);
|
Map<VariableDescriptor, VariableInitializers> enterInstructionData = mergeIncomingEdgesData(incomingEdgesData);
|
||||||
Map<VariableDescriptor, VariableInitializers> exitInstructionData = addVariableInitializerFromCurrentInstructionIfAny(instruction, enterInstructionData);
|
Map<VariableDescriptor, VariableInitializers> exitInstructionData = addVariableInitializerFromCurrentInstructionIfAny(instruction, enterInstructionData);
|
||||||
return Pair.create(enterInstructionData, exitInstructionData);
|
return Pair.create(enterInstructionData, exitInstructionData);
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
traverser.collectInformationFromInstructionGraph(variableInitializersMergeStrategy, Collections.<VariableDescriptor, VariableInitializers>emptyMap(), initialMapForStartInstruction);
|
|
||||||
|
|
||||||
final Collection<VariableDescriptor> varWithUninitializedErrorGenerated = Sets.newHashSet();
|
final Collection<VariableDescriptor> varWithUninitializedErrorGenerated = Sets.newHashSet();
|
||||||
final Collection<VariableDescriptor> varWithValReassignErrorGenerated = Sets.newHashSet();
|
final Collection<VariableDescriptor> varWithValReassignErrorGenerated = Sets.newHashSet();
|
||||||
final boolean processClassOrObject = subroutine instanceof JetClassOrObject;
|
final boolean processClassOrObject = subroutine instanceof JetClassOrObject;
|
||||||
traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableInitializers>>() {
|
traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableInitializers>>() {
|
||||||
@Override
|
@Override
|
||||||
public void execute(Instruction instruction, @Nullable Map<VariableDescriptor, VariableInitializers> enterData, @Nullable Map<VariableDescriptor, VariableInitializers> exitData) {
|
public void execute(@NotNull Instruction instruction, @Nullable Map<VariableDescriptor, VariableInitializers> enterData, @Nullable Map<VariableDescriptor, VariableInitializers> exitData) {
|
||||||
assert enterData != null && exitData != null;
|
assert enterData != null && exitData != null;
|
||||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true);
|
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true);
|
||||||
if (variableDescriptor == null) return;
|
if (variableDescriptor == null) return;
|
||||||
@@ -237,9 +235,12 @@ public class JetFlowInformationProvider {
|
|||||||
JetElement element = ((WriteValueInstruction) instruction).getlValue();
|
JetElement element = ((WriteValueInstruction) instruction).getlValue();
|
||||||
boolean error = checkBackingField(variableDescriptor, element);
|
boolean error = checkBackingField(variableDescriptor, element);
|
||||||
if (!(element instanceof JetExpression)) return;
|
if (!(element instanceof JetExpression)) return;
|
||||||
if (!error && !processLocalDeclaration) { // error has been generated before while processing outer function of this local declaration
|
if (!error && !processLocalDeclaration) { // error has been generated before, while processing outer function of this local declaration
|
||||||
error = checkValReassignment(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), varWithValReassignErrorGenerated);
|
error = checkValReassignment(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), varWithValReassignErrorGenerated);
|
||||||
}
|
}
|
||||||
|
if (!error) {
|
||||||
|
error = checkAssignmentBeforeDeclaration(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), exitData.get(variableDescriptor));
|
||||||
|
}
|
||||||
if (!error && processClassOrObject) {
|
if (!error && processClassOrObject) {
|
||||||
checkInitializationUsingBackingField(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), exitData.get(variableDescriptor));
|
checkInitializationUsingBackingField(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), exitData.get(variableDescriptor));
|
||||||
}
|
}
|
||||||
@@ -312,13 +313,21 @@ public class JetFlowInformationProvider {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkInitializationUsingBackingField(@NotNull VariableDescriptor variableDescriptor, @NotNull JetExpression expression, @NotNull VariableInitializers enterInitializers, @NotNull VariableInitializers exitInitializers) {
|
private boolean checkAssignmentBeforeDeclaration(@NotNull VariableDescriptor variableDescriptor, @NotNull JetExpression expression, @NotNull VariableInitializers enterInitializers, @NotNull VariableInitializers exitInitializers) {
|
||||||
|
if (!enterInitializers.isDeclared() && !exitInitializers.isDeclared() && !enterInitializers.isInitialized() && exitInitializers.isInitialized()) {
|
||||||
|
trace.report(Errors.INITIALIZATION_BEFORE_DECLARATION.on(expression, variableDescriptor));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkInitializationUsingBackingField(@NotNull VariableDescriptor variableDescriptor, @NotNull JetExpression expression, @NotNull VariableInitializers enterInitializers, @NotNull VariableInitializers exitInitializers) {
|
||||||
if (variableDescriptor instanceof PropertyDescriptor && !enterInitializers.isInitialized() && exitInitializers.isInitialized()) {
|
if (variableDescriptor instanceof PropertyDescriptor && !enterInitializers.isInitialized() && exitInitializers.isInitialized()) {
|
||||||
if (!variableDescriptor.isVar()) return;
|
if (!variableDescriptor.isVar()) return false;
|
||||||
if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)) return;
|
if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)) return false;
|
||||||
PsiElement property = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor);
|
PsiElement property = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor);
|
||||||
assert property instanceof JetProperty;
|
assert property instanceof JetProperty;
|
||||||
if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.FINAL && ((JetProperty) property).getSetter() == null) return;
|
if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.FINAL && ((JetProperty) property).getSetter() == null) return false;
|
||||||
JetExpression variable = expression;
|
JetExpression variable = expression;
|
||||||
if (expression instanceof JetDotQualifiedExpression) {
|
if (expression instanceof JetDotQualifiedExpression) {
|
||||||
if (((JetDotQualifiedExpression) expression).getReceiverExpression() instanceof JetThisExpression) {
|
if (((JetDotQualifiedExpression) expression).getReceiverExpression() instanceof JetThisExpression) {
|
||||||
@@ -334,9 +343,11 @@ public class JetFlowInformationProvider {
|
|||||||
else {
|
else {
|
||||||
trace.report(Errors.INITIALIZATION_USING_BACKING_FIELD_CUSTOM_SETTER.on(simpleNameExpression, expression, variableDescriptor));
|
trace.report(Errors.INITIALIZATION_USING_BACKING_FIELD_CUSTOM_SETTER.on(simpleNameExpression, expression, variableDescriptor));
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkBackingField(@NotNull VariableDescriptor variableDescriptor, @NotNull JetElement element) {
|
private boolean checkBackingField(@NotNull VariableDescriptor variableDescriptor, @NotNull JetElement element) {
|
||||||
@@ -415,6 +426,20 @@ public class JetFlowInformationProvider {
|
|||||||
VariableInitializers initializationAtThisElement = new VariableInitializers(((WriteValueInstruction) instruction).getElement());
|
VariableInitializers initializationAtThisElement = new VariableInitializers(((WriteValueInstruction) instruction).getElement());
|
||||||
exitInstructionData.put(variable, initializationAtThisElement);
|
exitInstructionData.put(variable, initializationAtThisElement);
|
||||||
}
|
}
|
||||||
|
else if (instruction instanceof VariableDeclarationInstruction) {
|
||||||
|
VariableDescriptor variable = extractVariableDescriptorIfAny(instruction, false);
|
||||||
|
VariableInitializers enterInitializers = enterInstructionData.get(variable);
|
||||||
|
if (enterInitializers == null || !enterInitializers.isInitialized()) {
|
||||||
|
JetElement element = ((VariableDeclarationInstruction) instruction).getElement();
|
||||||
|
if (element instanceof JetProperty) {
|
||||||
|
JetProperty property = (JetProperty) element;
|
||||||
|
if (property.getInitializer() == null) {
|
||||||
|
VariableInitializers variableDeclarationInfo = new VariableInitializers(false, true);
|
||||||
|
exitInstructionData.put(variable, variableDeclarationInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return exitInstructionData;
|
return exitInstructionData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,7 +494,7 @@ public class JetFlowInformationProvider {
|
|||||||
|
|
||||||
JetControlFlowGraphTraverser.<Void>create(pseudocode, true, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
JetControlFlowGraphTraverser.<Void>create(pseudocode, true, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public void execute(Instruction instruction, Void enterData, Void exitData) {
|
public void execute(@NotNull Instruction instruction, Void enterData, Void exitData) {
|
||||||
if (instruction instanceof ReadValueInstruction) {
|
if (instruction instanceof ReadValueInstruction) {
|
||||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
||||||
if (variableDescriptor != null && functionVariables.contains(variableDescriptor)) {
|
if (variableDescriptor != null && functionVariables.contains(variableDescriptor)) {
|
||||||
@@ -493,9 +518,9 @@ public class JetFlowInformationProvider {
|
|||||||
JetControlFlowGraphTraverser<Map<VariableDescriptor, VariableStatus>> traverser = JetControlFlowGraphTraverser.create(pseudocode, true, false);
|
JetControlFlowGraphTraverser<Map<VariableDescriptor, VariableStatus>> traverser = JetControlFlowGraphTraverser.create(pseudocode, true, false);
|
||||||
final Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
|
final Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
|
||||||
Map<VariableDescriptor, VariableStatus> sinkInstructionData = Maps.newHashMap();
|
Map<VariableDescriptor, VariableStatus> sinkInstructionData = Maps.newHashMap();
|
||||||
traverser.collectInformationFromInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataMergeStrategy<Map<VariableDescriptor, VariableStatus>>() {
|
traverser.collectInformationFromInstructionGraph(Collections.<VariableDescriptor, VariableStatus>emptyMap(), sinkInstructionData, new JetControlFlowGraphTraverser.InstructionDataMergeStrategy<Map<VariableDescriptor, VariableStatus>>() {
|
||||||
@Override
|
@Override
|
||||||
public Pair<Map<VariableDescriptor, VariableStatus>, Map<VariableDescriptor, VariableStatus>> execute(Instruction instruction, @NotNull Collection<Map<VariableDescriptor, VariableStatus>> incomingEdgesData) {
|
public Pair<Map<VariableDescriptor, VariableStatus>, Map<VariableDescriptor, VariableStatus>> execute(@NotNull Instruction instruction, @NotNull Collection<Map<VariableDescriptor, VariableStatus>> incomingEdgesData) {
|
||||||
Map<VariableDescriptor, VariableStatus> enterResult = Maps.newHashMap();
|
Map<VariableDescriptor, VariableStatus> enterResult = Maps.newHashMap();
|
||||||
for (Map<VariableDescriptor, VariableStatus> edgeData : incomingEdgesData) {
|
for (Map<VariableDescriptor, VariableStatus> edgeData : incomingEdgesData) {
|
||||||
for (Map.Entry<VariableDescriptor, VariableStatus> entry : edgeData.entrySet()) {
|
for (Map.Entry<VariableDescriptor, VariableStatus> entry : edgeData.entrySet()) {
|
||||||
@@ -526,10 +551,10 @@ public class JetFlowInformationProvider {
|
|||||||
}
|
}
|
||||||
return new Pair<Map<VariableDescriptor, VariableStatus>, Map<VariableDescriptor, VariableStatus>>(enterResult, exitResult);
|
return new Pair<Map<VariableDescriptor, VariableStatus>, Map<VariableDescriptor, VariableStatus>>(enterResult, exitResult);
|
||||||
}
|
}
|
||||||
}, Collections.<VariableDescriptor, VariableStatus>emptyMap(), sinkInstructionData);
|
});
|
||||||
traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableStatus>>() {
|
traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableStatus>>() {
|
||||||
@Override
|
@Override
|
||||||
public void execute(Instruction instruction, @Nullable Map<VariableDescriptor, VariableStatus> enterData, @Nullable Map<VariableDescriptor, VariableStatus> exitData) {
|
public void execute(@NotNull Instruction instruction, @Nullable Map<VariableDescriptor, VariableStatus> enterData, @Nullable Map<VariableDescriptor, VariableStatus> exitData) {
|
||||||
assert enterData != null && exitData != null;
|
assert enterData != null && exitData != null;
|
||||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
||||||
if (variableDescriptor == null || !declaredVariables.contains(variableDescriptor) ||
|
if (variableDescriptor == null || !declaredVariables.contains(variableDescriptor) ||
|
||||||
@@ -630,7 +655,7 @@ public class JetFlowInformationProvider {
|
|||||||
final Set<VariableDescriptor> usedVariables = Sets.newHashSet();
|
final Set<VariableDescriptor> usedVariables = Sets.newHashSet();
|
||||||
JetControlFlowGraphTraverser.<Void>create(pseudocode, true, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
JetControlFlowGraphTraverser.<Void>create(pseudocode, true, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public void execute(Instruction instruction, Void enterData, Void exitData) {
|
public void execute(@NotNull Instruction instruction, Void enterData, Void exitData) {
|
||||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
||||||
if (variableDescriptor != null) {
|
if (variableDescriptor != null) {
|
||||||
usedVariables.add(variableDescriptor);
|
usedVariables.add(variableDescriptor);
|
||||||
@@ -647,7 +672,7 @@ public class JetFlowInformationProvider {
|
|||||||
final Set<VariableDescriptor> declaredVariables = Sets.newHashSet();
|
final Set<VariableDescriptor> declaredVariables = Sets.newHashSet();
|
||||||
JetControlFlowGraphTraverser.<Void>create(pseudocode, false, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
JetControlFlowGraphTraverser.<Void>create(pseudocode, false, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public void execute(Instruction instruction, @Nullable Void enterData, @Nullable Void exitData) {
|
public void execute(@NotNull Instruction instruction, @Nullable Void enterData, @Nullable Void exitData) {
|
||||||
if (instruction instanceof VariableDeclarationInstruction) {
|
if (instruction instanceof VariableDeclarationInstruction) {
|
||||||
JetDeclaration variableDeclarationElement = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
|
JetDeclaration variableDeclarationElement = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
|
||||||
DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement);
|
DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement);
|
||||||
@@ -667,22 +692,33 @@ public class JetFlowInformationProvider {
|
|||||||
private static class VariableInitializers {
|
private static class VariableInitializers {
|
||||||
private Set<JetElement> possibleLocalInitializers = Sets.newHashSet();
|
private Set<JetElement> possibleLocalInitializers = Sets.newHashSet();
|
||||||
private boolean isInitialized;
|
private boolean isInitialized;
|
||||||
|
private boolean isDeclared;
|
||||||
|
|
||||||
public VariableInitializers(boolean isInitialized) {
|
public VariableInitializers(boolean isInitialized) {
|
||||||
|
this(isInitialized, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableInitializers(boolean isInitialized, boolean isDeclared) {
|
||||||
this.isInitialized = isInitialized;
|
this.isInitialized = isInitialized;
|
||||||
|
this.isDeclared = isDeclared;
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableInitializers(JetElement element) {
|
public VariableInitializers(JetElement element) {
|
||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
|
isDeclared = element instanceof JetProperty;
|
||||||
possibleLocalInitializers.add(element);
|
possibleLocalInitializers.add(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableInitializers(Set<VariableInitializers> edgesData) {
|
public VariableInitializers(Set<VariableInitializers> edgesData) {
|
||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
|
isDeclared = true;
|
||||||
for (VariableInitializers edgeData : edgesData) {
|
for (VariableInitializers edgeData : edgesData) {
|
||||||
if (!edgeData.isInitialized) {
|
if (!edgeData.isInitialized) {
|
||||||
isInitialized = false;
|
isInitialized = false;
|
||||||
}
|
}
|
||||||
|
if (!edgeData.isDeclared) {
|
||||||
|
isDeclared = false;
|
||||||
|
}
|
||||||
possibleLocalInitializers.addAll(edgeData.possibleLocalInitializers);
|
possibleLocalInitializers.addAll(edgeData.possibleLocalInitializers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -695,6 +731,10 @@ public class JetFlowInformationProvider {
|
|||||||
return isInitialized;
|
return isInitialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isDeclared() {
|
||||||
|
return isDeclared;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ public interface Errors {
|
|||||||
};
|
};
|
||||||
|
|
||||||
PsiElementOnlyDiagnosticFactory1<JetExpression, DeclarationDescriptor> VAL_REASSIGNMENT = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Val can not be reassigned", NAME);
|
PsiElementOnlyDiagnosticFactory1<JetExpression, DeclarationDescriptor> VAL_REASSIGNMENT = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Val can not be reassigned", NAME);
|
||||||
|
PsiElementOnlyDiagnosticFactory1<JetExpression, DeclarationDescriptor> INITIALIZATION_BEFORE_DECLARATION = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Variable cannot be initialized before declaration", NAME);
|
||||||
SimplePsiElementOnlyDiagnosticFactory<JetExpression> VARIABLE_EXPECTED = new SimplePsiElementOnlyDiagnosticFactory<JetExpression>(ERROR, "Variable expected");
|
SimplePsiElementOnlyDiagnosticFactory<JetExpression> VARIABLE_EXPECTED = new SimplePsiElementOnlyDiagnosticFactory<JetExpression>(ERROR, "Variable expected");
|
||||||
|
|
||||||
PsiElementOnlyDiagnosticFactory1<JetSimpleNameExpression, DeclarationDescriptor> INITIALIZATION_USING_BACKING_FIELD_CUSTOM_SETTER = PsiElementOnlyDiagnosticFactory1.create(ERROR, "This property has a custom setter, so initialization using backing field required", NAME);
|
PsiElementOnlyDiagnosticFactory1<JetSimpleNameExpression, DeclarationDescriptor> INITIALIZATION_USING_BACKING_FIELD_CUSTOM_SETTER = PsiElementOnlyDiagnosticFactory1.create(ERROR, "This property has a custom setter, so initialization using backing field required", NAME);
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
package demo
|
package demo
|
||||||
|
|
||||||
public open class Identifier<T>(myName : T?, myHasDollar : Boolean) {
|
public open class Identifier<T>(myName : T?, myHasDollar : Boolean) {
|
||||||
|
private val myName : T?
|
||||||
|
private var myHasDollar : Boolean
|
||||||
|
private var myNullable : Boolean = true
|
||||||
|
|
||||||
{
|
{
|
||||||
$myName = myName
|
$myName = myName
|
||||||
$myHasDollar = myHasDollar
|
$myHasDollar = myHasDollar
|
||||||
}
|
}
|
||||||
|
|
||||||
private val myName : T?
|
|
||||||
private var myHasDollar : Boolean
|
|
||||||
private var myNullable : Boolean = true
|
|
||||||
|
|
||||||
open public fun getName() : T? {
|
open public fun getName() : T? {
|
||||||
return myName
|
return myName
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
//KT-897 Don't allow assignment to a property before it is defined
|
||||||
|
|
||||||
|
package kt897
|
||||||
|
|
||||||
|
class A() {
|
||||||
|
{
|
||||||
|
<!INITIALIZATION_BEFORE_DECLARATION!>i<!> = 11
|
||||||
|
}
|
||||||
|
val i : Int? = null // must be an error
|
||||||
|
|
||||||
|
{
|
||||||
|
<!INITIALIZATION_BEFORE_DECLARATION!>j<!> = 1
|
||||||
|
}
|
||||||
|
var j : Int = 2
|
||||||
|
|
||||||
|
{
|
||||||
|
<!INITIALIZATION_BEFORE_DECLARATION!>k<!> = 3
|
||||||
|
}
|
||||||
|
val k : Int
|
||||||
|
}
|
||||||
@@ -5,9 +5,9 @@ import junit.framework.TestSuite;
|
|||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
public class TestAll {
|
public class TestAll {
|
||||||
public static TestSuite suite() {
|
// public static TestSuite suite() {
|
||||||
TestSuite suite = new TestSuite(StandardCollectionTest.class, CollectionTest.class, IoTest.class, ListTest.class, MapTest.class, SetTest.class, OldStdlibTest.class);
|
// TestSuite suite = new TestSuite(StandardCollectionTest.class, CollectionTest.class, IoTest.class, ListTest.class, MapTest.class, SetTest.class, OldStdlibTest.class);
|
||||||
suite.addTest(testDslExample.namespace.getSuite());
|
// suite.addTest(testDslExample.namespace.getSuite());
|
||||||
return suite;
|
// return suite;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user