Merge branch 'master' of git+ssh://git.labs.intellij.net/jet into KT-337
This commit is contained in:
@@ -35,18 +35,10 @@ import java.util.*;
|
||||
* @author alex.tkachman
|
||||
*/
|
||||
public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> {
|
||||
private static final String CLASS_OBJECT = "java/lang/Object";
|
||||
private static final String CLASS_STRING = "java/lang/String";
|
||||
public static final String CLASS_STRING_BUILDER = "java/lang/StringBuilder";
|
||||
private static final String CLASS_COMPARABLE = "java/lang/Comparable";
|
||||
|
||||
private static final String CLASS_NO_PATTERN_MATCHED_EXCEPTION = "jet/NoPatternMatchedException";
|
||||
private static final String CLASS_TYPE_CAST_EXCEPTION = "jet/TypeCastException";
|
||||
|
||||
private static final Type OBJECT_TYPE = Type.getType(Object.class);
|
||||
private static final Type THROWABLE_TYPE = Type.getType(Throwable.class);
|
||||
private static final Type STRING_TYPE = Type.getObjectType(CLASS_STRING);
|
||||
|
||||
private final Stack<Label> myContinueTargets = new Stack<Label>();
|
||||
private final Stack<Label> myBreakTargets = new Stack<Label>();
|
||||
|
||||
@@ -574,10 +566,10 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> {
|
||||
? ((JetEscapeStringTemplateEntry) entry).getUnescapedValue()
|
||||
: entry.getText();
|
||||
v.aconst(text);
|
||||
invokeAppendMethod(STRING_TYPE);
|
||||
invokeAppendMethod(JetTypeMapper.JL_STRING_TYPE);
|
||||
}
|
||||
}
|
||||
v.invokevirtual(CLASS_STRING_BUILDER, "toString", "()Ljava/lang/String;");
|
||||
v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
|
||||
return StackValue.onStack(expressionType(expression));
|
||||
}
|
||||
}
|
||||
@@ -972,7 +964,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> {
|
||||
v.invokevirtual("jet/typeinfo/TypeInfo", "getClassObject", "()Ljava/lang/Object;");
|
||||
v.checkcast(typeMapper.mapType(typeParameterDescriptor.getClassObjectType()));
|
||||
|
||||
return StackValue.onStack(OBJECT_TYPE);
|
||||
return StackValue.onStack(JetTypeMapper.TYPE_OBJECT);
|
||||
}
|
||||
else {
|
||||
// receiver
|
||||
@@ -1693,7 +1685,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> {
|
||||
|
||||
private StackValue generateNullSafeEquals(IElementType opToken, boolean leftNullable, boolean rightNullable) {
|
||||
if(!leftNullable) {
|
||||
v.invokevirtual(CLASS_OBJECT, "equals", "(Ljava/lang/Object;)Z");
|
||||
v.invokevirtual("java/lang/Object", "equals", "(Ljava/lang/Object;)Z");
|
||||
if (opToken == JetTokens.EXCLEQ) {
|
||||
v.iconst(1);
|
||||
v.xor(Type.INT_TYPE);
|
||||
@@ -1706,7 +1698,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> {
|
||||
v.ifnull(rightNull);
|
||||
Label leftNull = new Label();
|
||||
v.ifnull(leftNull);
|
||||
v.invokevirtual(CLASS_OBJECT, "equals", "(Ljava/lang/Object;)Z");
|
||||
v.invokevirtual("java/lang/Object", "equals", "(Ljava/lang/Object;)Z");
|
||||
if (opToken == JetTokens.EXCLEQ || opToken == JetTokens.EXCLEQEQEQ) {
|
||||
v.iconst(1);
|
||||
v.xor(Type.INT_TYPE);
|
||||
@@ -1731,7 +1723,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> {
|
||||
v.pop();
|
||||
Label leftNull = new Label();
|
||||
v.ifnull(leftNull);
|
||||
v.invokevirtual(CLASS_OBJECT, "equals", "(Ljava/lang/Object;)Z");
|
||||
v.invokevirtual("java/lang/Object", "equals", "(Ljava/lang/Object;)Z");
|
||||
if (opToken == JetTokens.EXCLEQ || opToken == JetTokens.EXCLEQEQEQ) {
|
||||
v.iconst(1);
|
||||
v.xor(Type.INT_TYPE);
|
||||
@@ -1796,7 +1788,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> {
|
||||
|
||||
private StackValue compareExpressionsOnStack(IElementType opToken, Type operandType) {
|
||||
if (operandType.getSort() == Type.OBJECT) {
|
||||
v.invokeinterface(CLASS_COMPARABLE, "compareTo", "(Ljava/lang/Object;)I");
|
||||
v.invokeinterface("java/lang/Comparable", "compareTo", "(Ljava/lang/Object;)I");
|
||||
v.iconst(0);
|
||||
operandType = Type.INT_TYPE;
|
||||
}
|
||||
@@ -1865,7 +1857,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> {
|
||||
v.anew(type);
|
||||
v.dup();
|
||||
Method method = new Method("<init>", Type.VOID_TYPE, new Type[0]);
|
||||
v.invokespecial(CLASS_STRING_BUILDER, method.getName(), method.getDescriptor());
|
||||
v.invokespecial("java/lang/StringBuilder", method.getName(), method.getDescriptor());
|
||||
}
|
||||
|
||||
public void invokeAppend(final JetExpression expr) {
|
||||
@@ -1885,7 +1877,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> {
|
||||
public void invokeAppendMethod(Type exprType) {
|
||||
Method appendDescriptor = new Method("append", JetTypeMapper.JL_STRING_BUILDER,
|
||||
new Type[] { exprType.getSort() == Type.OBJECT ? JetTypeMapper.TYPE_OBJECT : exprType});
|
||||
v.invokevirtual(CLASS_STRING_BUILDER, "append", appendDescriptor.getDescriptor());
|
||||
v.invokevirtual("java/lang/StringBuilder", "append", appendDescriptor.getDescriptor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2331,7 +2323,7 @@ If finally block is present, its last expression is the value of try expression.
|
||||
DeclarationDescriptor descriptor = jetType.getConstructor().getDeclarationDescriptor();
|
||||
if (descriptor instanceof ClassDescriptor || descriptor instanceof TypeParameterDescriptor) {
|
||||
Type type = JetTypeMapper.boxType(typeMapper.mapType(jetType));
|
||||
generateInstanceOf(StackValue.expression(OBJECT_TYPE, expression.getLeft(), this), jetType, true);
|
||||
generateInstanceOf(StackValue.expression(JetTypeMapper.TYPE_OBJECT, expression.getLeft(), this), jetType, true);
|
||||
Label isInstance = new Label();
|
||||
v.ifne(isInstance);
|
||||
v.pop();
|
||||
@@ -2353,7 +2345,7 @@ If finally block is present, its last expression is the value of try expression.
|
||||
|
||||
@Override
|
||||
public StackValue visitIsExpression(final JetIsExpression expression, StackValue receiver) {
|
||||
final StackValue match = StackValue.expression(OBJECT_TYPE, expression.getLeftHandSide(), this);
|
||||
final StackValue match = StackValue.expression(JetTypeMapper.TYPE_OBJECT, expression.getLeftHandSide(), this);
|
||||
return generatePatternMatch(expression.getPattern(), expression.isNegated(), match, null);
|
||||
}
|
||||
|
||||
@@ -2376,7 +2368,7 @@ If finally block is present, its last expression is the value of try expression.
|
||||
expressionToMatch.dupReceiver(v);
|
||||
expressionToMatch.put(subjectType, v);
|
||||
JetExpression condExpression = ((JetExpressionPattern) pattern).getExpression();
|
||||
Type condType = isNumberPrimitive(subjectType) ? expressionType(condExpression) : OBJECT_TYPE;
|
||||
Type condType = isNumberPrimitive(subjectType) ? expressionType(condExpression) : JetTypeMapper.TYPE_OBJECT;
|
||||
gen(condExpression, condType);
|
||||
return generateEqualsForExpressionsOnStack(JetTokens.EQEQ, subjectType, condType, false, false);
|
||||
}
|
||||
@@ -2407,7 +2399,7 @@ If finally block is present, its last expression is the value of try expression.
|
||||
Label lblFail = new Label();
|
||||
Label lblDone = new Label();
|
||||
expressionToMatch.dupReceiver(v);
|
||||
expressionToMatch.put(OBJECT_TYPE, v);
|
||||
expressionToMatch.put(JetTypeMapper.TYPE_OBJECT, v);
|
||||
v.dup();
|
||||
final String tupleClassName = "jet/Tuple" + entries.size();
|
||||
Type tupleType = Type.getObjectType(tupleClassName);
|
||||
@@ -2421,7 +2413,7 @@ If finally block is present, its last expression is the value of try expression.
|
||||
|
||||
v.mark(lblCheck);
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
final StackValue tupleField = StackValue.field(OBJECT_TYPE, tupleClassName, "_" + (i + 1), false);
|
||||
final StackValue tupleField = StackValue.field(JetTypeMapper.TYPE_OBJECT, tupleClassName, "_" + (i + 1), false);
|
||||
final StackValue stackValue = generatePatternMatch(entries.get(i).getPattern(), false, tupleField, nextEntry);
|
||||
stackValue.condJump(lblPopAndFail, true, v);
|
||||
}
|
||||
@@ -2467,7 +2459,7 @@ If finally block is present, its last expression is the value of try expression.
|
||||
private void generateInstanceOf(StackValue expressionToGen, JetType jetType, boolean leaveExpressionOnStack) {
|
||||
DeclarationDescriptor descriptor = jetType.getConstructor().getDeclarationDescriptor();
|
||||
if (!hasTypeInfoForInstanceOf(jetType)) {
|
||||
expressionToGen.put(OBJECT_TYPE, v);
|
||||
expressionToGen.put(JetTypeMapper.TYPE_OBJECT, v);
|
||||
if (leaveExpressionOnStack) {
|
||||
v.dup();
|
||||
}
|
||||
@@ -2491,7 +2483,7 @@ If finally block is present, its last expression is the value of try expression.
|
||||
}
|
||||
else {
|
||||
generateTypeInfo(jetType);
|
||||
expressionToGen.put(OBJECT_TYPE, v);
|
||||
expressionToGen.put(JetTypeMapper.TYPE_OBJECT, v);
|
||||
if (leaveExpressionOnStack) {
|
||||
v.dupX1();
|
||||
}
|
||||
@@ -2718,7 +2710,7 @@ If finally block is present, its last expression is the value of try expression.
|
||||
v.dup();
|
||||
generateTypeInfo(new ProjectionErasingJetType(bindingContext.get(BindingContext.EXPRESSION_TYPE, expression)));
|
||||
for (JetExpression entry : entries) {
|
||||
gen(entry, OBJECT_TYPE);
|
||||
gen(entry, JetTypeMapper.TYPE_OBJECT);
|
||||
}
|
||||
v.invokespecial(className, "<init>", signature.toString());
|
||||
return StackValue.onStack(tupleType);
|
||||
|
||||
@@ -26,7 +26,7 @@ public class Concat implements IntrinsicMethod {
|
||||
codegen.invokeAppend(arguments.get(1));
|
||||
}
|
||||
|
||||
v.invokevirtual(ExpressionCodegen.CLASS_STRING_BUILDER, "toString", "()Ljava/lang/String;");
|
||||
v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
|
||||
return StackValue.onStack(Type.getObjectType("java/lang/String"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import org.jetbrains.jet.lang.types.JetStandardLibrary;
|
||||
import org.jetbrains.jet.lang.types.TypeProjection;
|
||||
import org.jetbrains.jet.plugin.JetFileType;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import sun.tools.tree.NewArrayExpression;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -87,6 +86,7 @@ public class IntrinsicMethods {
|
||||
declareOverload(myStdLib.getLibraryScope().getFunctions("identityEquals"), 1, EQUALS);
|
||||
declareOverload(myStdLib.getLibraryScope().getFunctions("plus"), 1, new StringPlus());
|
||||
declareOverload(myStdLib.getLibraryScope().getFunctions("Array"), 1, new NewArray());
|
||||
declareOverload(myStdLib.getLibraryScope().getFunctions("sure"), 0, new Sure());
|
||||
|
||||
declareIntrinsicFunction("ByteIterator", "next", 0, ITERATOR_NEXT);
|
||||
declareIntrinsicFunction("ShortIterator", "next", 0, ITERATOR_NEXT);
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.jetbrains.jet.codegen.intrinsics;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.jet.codegen.ExpressionCodegen;
|
||||
import org.jetbrains.jet.codegen.JetTypeMapper;
|
||||
import org.jetbrains.jet.codegen.StackValue;
|
||||
import org.jetbrains.jet.lang.psi.JetExpression;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author alex.tkachman
|
||||
*/
|
||||
public class Sure implements IntrinsicMethod {
|
||||
@Override
|
||||
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, StackValue receiver) {
|
||||
receiver.put(JetTypeMapper.TYPE_OBJECT, v);
|
||||
v.invokestatic("jet/runtime/Intrinsics", "sure", "(Ljava/lang/Object;)Ljava/lang/Object;");
|
||||
StackValue.onStack(JetTypeMapper.TYPE_OBJECT).put(expectedType, v);
|
||||
return StackValue.onStack(expectedType);
|
||||
}
|
||||
}
|
||||
@@ -42,9 +42,7 @@ fun Any?.equals(other : Any?) : Boolean// = this === other
|
||||
// Returns "null" for null
|
||||
fun Any?.toString() : String// = this === other
|
||||
|
||||
// fun <T> array(vararg elements : T) : Array<T>
|
||||
|
||||
// fun array(vararg elements : Int) : IntArray
|
||||
fun <T : Any> T?.sure() : T
|
||||
|
||||
fun String?.plus(other: Any?) : String
|
||||
|
||||
|
||||
+40
-17
@@ -1,5 +1,6 @@
|
||||
package org.jetbrains.jet.lang.cfg;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
@@ -19,31 +20,36 @@ import java.util.Map;
|
||||
public class JetControlFlowGraphTraverser<D> {
|
||||
private final Pseudocode pseudocode;
|
||||
private final boolean lookInside;
|
||||
private final boolean straightDirection;
|
||||
private final Map<Instruction, Pair<D, D>> dataMap = Maps.newLinkedHashMap();
|
||||
|
||||
public static <D> JetControlFlowGraphTraverser<D> create(Pseudocode pseudocode, boolean lookInside) {
|
||||
return new JetControlFlowGraphTraverser<D>(pseudocode, lookInside);
|
||||
public static <D> JetControlFlowGraphTraverser<D> create(Pseudocode pseudocode, boolean lookInside, boolean straightDirection) {
|
||||
return new JetControlFlowGraphTraverser<D>(pseudocode, lookInside, straightDirection);
|
||||
}
|
||||
|
||||
private JetControlFlowGraphTraverser(Pseudocode pseudocode, boolean lookInside) {
|
||||
private JetControlFlowGraphTraverser(Pseudocode pseudocode, boolean lookInside, boolean straightDirection) {
|
||||
this.pseudocode = pseudocode;
|
||||
this.lookInside = lookInside;
|
||||
this.straightDirection = straightDirection;
|
||||
}
|
||||
|
||||
private Instruction getStartInstruction(Pseudocode pseudocode) {
|
||||
return straightDirection ? pseudocode.getEnterInstruction() : pseudocode.getSinkInstruction();
|
||||
}
|
||||
|
||||
public void collectInformationFromInstructionGraph(
|
||||
InstructionsMergeStrategy<D> instructionsMergeStrategy,
|
||||
InstructionDataMergeStrategy<D> instructionDataMergeStrategy,
|
||||
D initialDataValue,
|
||||
D initialDataValueForEnterInstruction,
|
||||
boolean straightDirection) {
|
||||
D initialDataValueForEnterInstruction) {
|
||||
initializeDataMap(pseudocode, initialDataValue);
|
||||
dataMap.put(straightDirection ? pseudocode.getEnterInstruction() : pseudocode.getSinkInstruction(),
|
||||
dataMap.put(getStartInstruction(pseudocode),
|
||||
Pair.create(initialDataValueForEnterInstruction, initialDataValueForEnterInstruction));
|
||||
|
||||
boolean[] changed = new boolean[1];
|
||||
changed[0] = true;
|
||||
while (changed[0]) {
|
||||
changed[0] = false;
|
||||
traverseSubGraph(pseudocode, instructionsMergeStrategy, Collections.<Instruction>emptyList(), straightDirection, changed, false);
|
||||
traverseSubGraph(pseudocode, instructionDataMergeStrategy, Collections.<Instruction>emptyList(), changed, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,22 +68,27 @@ public class JetControlFlowGraphTraverser<D> {
|
||||
|
||||
private void traverseSubGraph(
|
||||
Pseudocode pseudocode,
|
||||
InstructionsMergeStrategy<D> instructionsMergeStrategy,
|
||||
InstructionDataMergeStrategy<D> instructionDataMergeStrategy,
|
||||
Collection<Instruction> previousSubGraphInstructions,
|
||||
boolean straightDirection,
|
||||
boolean[] changed,
|
||||
boolean isLocal) {
|
||||
List<Instruction> instructions = pseudocode.getInstructions();
|
||||
SubroutineEnterInstruction enterInstruction = pseudocode.getEnterInstruction();
|
||||
Instruction startInstruction = getStartInstruction(pseudocode);
|
||||
|
||||
if (!straightDirection) {
|
||||
instructions = Lists.newArrayList(instructions);
|
||||
Collections.reverse(instructions);
|
||||
}
|
||||
for (Instruction instruction : instructions) {
|
||||
if (!isLocal && instruction instanceof SubroutineEnterInstruction) continue;
|
||||
boolean isStart = straightDirection ? instruction instanceof SubroutineEnterInstruction : instruction instanceof SubroutineSinkInstruction;
|
||||
if (!isLocal && isStart) continue;
|
||||
|
||||
Collection<Instruction> allPreviousInstructions;
|
||||
Collection<Instruction> previousInstructions = straightDirection
|
||||
? instruction.getPreviousInstructions()
|
||||
: instruction.getNextInstructions();
|
||||
|
||||
if (instruction == enterInstruction && !previousSubGraphInstructions.isEmpty()) {
|
||||
if (instruction == startInstruction && !previousSubGraphInstructions.isEmpty()) {
|
||||
allPreviousInstructions = Lists.newArrayList(previousInstructions);
|
||||
allPreviousInstructions.addAll(previousSubGraphInstructions);
|
||||
}
|
||||
@@ -87,7 +98,15 @@ 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, changed, true);
|
||||
Instruction lastInstruction = straightDirection ? subroutinePseudocode.getSinkInstruction() : subroutinePseudocode.getEnterInstruction();
|
||||
Pair<D, D> previousValue = dataMap.get(instruction);
|
||||
Pair<D, D> newValue = dataMap.get(lastInstruction);
|
||||
if (!previousValue.equals(newValue)) {
|
||||
changed[0] = true;
|
||||
dataMap.put(instruction, newValue);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Pair<D, D> previousDataValue = dataMap.get(instruction);
|
||||
|
||||
@@ -99,7 +118,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);
|
||||
@@ -116,12 +135,16 @@ public class JetControlFlowGraphTraverser<D> {
|
||||
Pseudocode pseudocode,
|
||||
InstructionDataAnalyzeStrategy<D> instructionDataAnalyzeStrategy) {
|
||||
List<Instruction> instructions = pseudocode.getInstructions();
|
||||
if (!straightDirection) {
|
||||
instructions = Lists.newArrayList(instructions);
|
||||
Collections.reverse(instructions);
|
||||
}
|
||||
for (Instruction instruction : instructions) {
|
||||
if (instruction.isDead()) continue;
|
||||
Pair<D, D> pair = dataMap.get(instruction);
|
||||
if (lookInside && instruction instanceof LocalDeclarationInstruction) {
|
||||
traverseAndAnalyzeInstructionGraph(((LocalDeclarationInstruction) instruction).getBody(), instructionDataAnalyzeStrategy);
|
||||
}
|
||||
Pair<D, D> pair = dataMap.get(instruction);
|
||||
instructionDataAnalyzeStrategy.execute(instruction,
|
||||
pair != null ? pair.getFirst() : null,
|
||||
pair != null ? pair.getSecond() : null);
|
||||
@@ -132,7 +155,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);
|
||||
}
|
||||
|
||||
|
||||
+258
-209
@@ -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, true);
|
||||
|
||||
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);
|
||||
|
||||
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) {
|
||||
@@ -368,7 +396,7 @@ public class JetFlowInformationProvider {
|
||||
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
||||
assert pseudocode != null;
|
||||
|
||||
JetControlFlowGraphTraverser.<Void>create(pseudocode, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
||||
JetControlFlowGraphTraverser.<Void>create(pseudocode, true, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
||||
@Override
|
||||
public void execute(Instruction instruction, Void enterData, Void exitData) {
|
||||
if (instruction instanceof ReadValueInstruction) {
|
||||
@@ -384,74 +412,57 @@ public class JetFlowInformationProvider {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// "Unused variable" & "unused value" analyses
|
||||
|
||||
public void markUnusedVariables(@NotNull JetElement subroutine) {
|
||||
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
||||
assert pseudocode != null;
|
||||
|
||||
final Set<VariableDescriptor> usedVariables = Sets.newHashSet();
|
||||
JetControlFlowGraphTraverser.<Void>create(pseudocode, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
||||
@Override
|
||||
public void execute(Instruction instruction, @Nullable Void enterData, @Nullable Void exitData) {
|
||||
if (instruction instanceof ReadValueInstruction) {
|
||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
||||
usedVariables.add(variableDescriptor);
|
||||
}
|
||||
}
|
||||
});
|
||||
Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
|
||||
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();
|
||||
PsiElement elementToMark = nameIdentifier != null ? nameIdentifier : element;
|
||||
trace.report(Errors.UNUSED_VARIABLE.on((JetProperty)element, elementToMark, declaredVariable));
|
||||
}
|
||||
}
|
||||
JetControlFlowGraphTraverser<Map<VariableDescriptor, VariableStatus>> traverser = JetControlFlowGraphTraverser.create(pseudocode, true, false);
|
||||
final Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
|
||||
Collection<VariableDescriptor> usedVariables = collectUsedVariables(pseudocode);
|
||||
Map<VariableDescriptor, VariableStatus> sinkInstructionData = Maps.newHashMap();
|
||||
for (VariableDescriptor usedVariable : usedVariables) {
|
||||
sinkInstructionData.put(usedVariable, VariableStatus.UNUSED);
|
||||
}
|
||||
|
||||
markUnusedValues(subroutine, pseudocode, declaredVariables);
|
||||
}
|
||||
|
||||
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<Map<VariableDescriptor, VariableStatus>>() {
|
||||
@Override
|
||||
public Pair<Set<VariableDescriptor>, Set<VariableDescriptor>> execute(Instruction instruction, @NotNull Collection<Set<VariableDescriptor>> incomingEdgesData) {
|
||||
Set<VariableDescriptor> enterResult = Sets.newHashSet();
|
||||
for (Set<VariableDescriptor> edgeData : incomingEdgesData) {
|
||||
enterResult.addAll(edgeData);
|
||||
}
|
||||
Set<VariableDescriptor> exitResult = Sets.newHashSet(enterResult);
|
||||
if (instruction instanceof ReadValueInstruction) {
|
||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true);
|
||||
if (variableDescriptor != null) {
|
||||
exitResult.add(variableDescriptor);
|
||||
public Pair<Map<VariableDescriptor, VariableStatus>, Map<VariableDescriptor, VariableStatus>> execute(Instruction instruction, @NotNull Collection<Map<VariableDescriptor, VariableStatus>> incomingEdgesData) {
|
||||
Map<VariableDescriptor, VariableStatus> enterResult = Maps.newHashMap();
|
||||
for (Map<VariableDescriptor, VariableStatus> edgeData : incomingEdgesData) {
|
||||
for (Map.Entry<VariableDescriptor, VariableStatus> entry : edgeData.entrySet()) {
|
||||
VariableDescriptor variableDescriptor = entry.getKey();
|
||||
VariableStatus variableStatus = entry.getValue();
|
||||
enterResult.put(variableDescriptor, variableStatus.merge(enterResult.get(variableDescriptor)));
|
||||
}
|
||||
}
|
||||
else if (instruction instanceof WriteValueInstruction) {
|
||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true);
|
||||
if (variableDescriptor != null) {
|
||||
exitResult.remove(variableDescriptor);
|
||||
Map<VariableDescriptor, VariableStatus> exitResult = Maps.newHashMap(enterResult);
|
||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true);
|
||||
if (variableDescriptor != null) {
|
||||
if (instruction instanceof ReadValueInstruction) {
|
||||
exitResult.put(variableDescriptor, VariableStatus.READ);
|
||||
}
|
||||
else if (instruction instanceof WriteValueInstruction) {
|
||||
exitResult.put(variableDescriptor, VariableStatus.WRITTEN);
|
||||
}
|
||||
}
|
||||
return new Pair<Set<VariableDescriptor>, Set<VariableDescriptor>>(enterResult, exitResult);
|
||||
return new Pair<Map<VariableDescriptor, VariableStatus>, Map<VariableDescriptor, VariableStatus>>(enterResult, exitResult);
|
||||
}
|
||||
}, Collections.<VariableDescriptor>emptySet(), Collections.<VariableDescriptor>emptySet(), false);
|
||||
traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Set<VariableDescriptor>>() {
|
||||
}, Collections.<VariableDescriptor, VariableStatus>emptyMap(), sinkInstructionData);
|
||||
traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableStatus>>() {
|
||||
@Override
|
||||
public void execute(Instruction instruction, @Nullable Set<VariableDescriptor> enterData, @Nullable Set<VariableDescriptor> exitData) {
|
||||
public void execute(Instruction instruction, @Nullable Map<VariableDescriptor, VariableStatus> enterData, @Nullable Map<VariableDescriptor, VariableStatus> exitData) {
|
||||
assert enterData != null && exitData != null;
|
||||
if (instruction instanceof WriteValueInstruction) {
|
||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
||||
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);
|
||||
if (isLocal) {
|
||||
JetElement element = ((WriteValueInstruction) instruction).getElement();
|
||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
||||
if (variableDescriptor != null && declaredVariables.contains(variableDescriptor)) {
|
||||
PsiElement variableDeclarationElement = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor);
|
||||
assert variableDeclarationElement instanceof JetDeclaration;
|
||||
boolean isLocal = JetPsiUtil.isLocal((JetDeclaration) variableDeclarationElement);
|
||||
if (isLocal) {
|
||||
if (instruction instanceof WriteValueInstruction) {
|
||||
JetElement element = ((WriteValueInstruction) instruction).getElement();
|
||||
if (enterData.get(variableDescriptor) == VariableStatus.WRITTEN || enterData.get(variableDescriptor) == VariableStatus.UNUSED) {
|
||||
if (element instanceof JetBinaryExpression && ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) {
|
||||
JetExpression right = ((JetBinaryExpression) element).getRight();
|
||||
if (right != null) {
|
||||
@@ -465,6 +476,20 @@ public class JetFlowInformationProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (instruction instanceof VariableDeclarationInstruction) {
|
||||
JetDeclaration element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
|
||||
if (element instanceof JetProperty) {
|
||||
PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier();
|
||||
PsiElement elementToMark = nameIdentifier != null ? nameIdentifier : element;
|
||||
if (enterData.get(variableDescriptor) == VariableStatus.UNUSED) {
|
||||
trace.report(Errors.UNUSED_VARIABLE.on((JetNamedDeclaration) element, elementToMark, variableDescriptor));
|
||||
}
|
||||
else if (enterData.get(variableDescriptor) == VariableStatus.WRITTEN) {
|
||||
trace.report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE.on((JetNamedDeclaration) element, elementToMark, variableDescriptor));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -472,23 +497,44 @@ public class JetFlowInformationProvider {
|
||||
});
|
||||
}
|
||||
|
||||
private static enum VariableStatus {
|
||||
READ(2),
|
||||
WRITTEN(1),
|
||||
UNUSED(0);
|
||||
|
||||
private int importance;
|
||||
|
||||
private VariableStatus(int importance) {
|
||||
this.importance = importance;
|
||||
}
|
||||
|
||||
public VariableStatus merge(@Nullable VariableStatus variableStatus) {
|
||||
if (variableStatus == null || importance > variableStatus.importance) return this;
|
||||
return variableStatus;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Util methods
|
||||
|
||||
@Nullable
|
||||
private VariableDescriptor extractVariableDescriptorIfAny(Instruction instruction, boolean onlyReference) {
|
||||
VariableDescriptor variableDescriptor = null;
|
||||
JetElement element = null;
|
||||
if (instruction instanceof ReadValueInstruction) {
|
||||
JetElement element = ((ReadValueInstruction) instruction).getElement();
|
||||
variableDescriptor = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), element, onlyReference);
|
||||
element = ((ReadValueInstruction) instruction).getElement();
|
||||
}
|
||||
else if (instruction instanceof WriteValueInstruction) {
|
||||
JetElement lValue = ((WriteValueInstruction) instruction).getlValue();
|
||||
variableDescriptor = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), lValue, onlyReference);
|
||||
element = ((WriteValueInstruction) instruction).getlValue();
|
||||
}
|
||||
return variableDescriptor;
|
||||
else if (instruction instanceof VariableDeclarationInstruction) {
|
||||
element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
|
||||
}
|
||||
return BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), element, onlyReference);
|
||||
}
|
||||
|
||||
private Collection<VariableDescriptor> collectUsedVariables(Pseudocode pseudocode) {
|
||||
final Set<VariableDescriptor> usedVariables = Sets.newHashSet();
|
||||
JetControlFlowGraphTraverser.<Void>create(pseudocode, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
||||
JetControlFlowGraphTraverser.<Void>create(pseudocode, true, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
||||
@Override
|
||||
public void execute(Instruction instruction, Void enterData, Void exitData) {
|
||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
||||
@@ -505,7 +551,7 @@ public class JetFlowInformationProvider {
|
||||
assert pseudocode != null;
|
||||
|
||||
final Set<VariableDescriptor> declaredVariables = Sets.newHashSet();
|
||||
JetControlFlowGraphTraverser.<Void>create(pseudocode, false).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
||||
JetControlFlowGraphTraverser.<Void>create(pseudocode, false, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
||||
@Override
|
||||
public void execute(Instruction instruction, @Nullable Void enterData, @Nullable Void exitData) {
|
||||
if (instruction instanceof VariableDeclarationInstruction) {
|
||||
@@ -521,31 +567,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 +604,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 +618,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;
|
||||
}
|
||||
|
||||
@@ -144,7 +144,8 @@ public interface Errors {
|
||||
PsiElementOnlyDiagnosticFactory3<JetModifierListOwner, CallableMemberDescriptor, CallableMemberDescriptor, DeclarationDescriptor> VIRTUAL_MEMBER_HIDDEN = PsiElementOnlyDiagnosticFactory3.create(ERROR, "''{0}'' hides ''{1}'' in class {2} and needs 'override' modifier", DescriptorRenderer.TEXT);
|
||||
|
||||
PsiElementOnlyDiagnosticFactory1<JetSimpleNameExpression, DeclarationDescriptor> UNINITIALIZED_VARIABLE = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Variable ''{0}'' must be initialized", NAME);
|
||||
PsiElementOnlyDiagnosticFactory1<JetProperty, DeclarationDescriptor> UNUSED_VARIABLE = PsiElementOnlyDiagnosticFactory1.create(WARNING, "Variable ''{0}'' is never used", NAME);
|
||||
PsiElementOnlyDiagnosticFactory1<JetNamedDeclaration, DeclarationDescriptor> UNUSED_VARIABLE = PsiElementOnlyDiagnosticFactory1.create(WARNING, "Variable ''{0}'' is never used", NAME);
|
||||
PsiElementOnlyDiagnosticFactory1<JetNamedDeclaration, DeclarationDescriptor> ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE = PsiElementOnlyDiagnosticFactory1.create(WARNING, "Variable ''{0}'' is assigned but never accessed", NAME);
|
||||
PsiElementOnlyDiagnosticFactory2<JetElement, JetElement, DeclarationDescriptor> UNUSED_VALUE = new PsiElementOnlyDiagnosticFactory2<JetElement, JetElement, DeclarationDescriptor>(WARNING, "The value ''{0}'' assigned to ''{1}'' is never used", NAME) {
|
||||
@Override
|
||||
protected String makeMessageForA(@NotNull JetElement element) {
|
||||
@@ -158,17 +159,7 @@ public interface Errors {
|
||||
}
|
||||
};
|
||||
|
||||
PsiElementOnlyDiagnosticFactory2<JetExpression, DeclarationDescriptor, JetProperty[]> VAL_REASSIGNMENT = new PsiElementOnlyDiagnosticFactory2<JetExpression, DeclarationDescriptor, JetProperty[]>(ERROR, "Val can not be reassigned", NAME) {
|
||||
@NotNull
|
||||
@Override
|
||||
public DiagnosticWithPsiElement<JetExpression> on(@NotNull JetExpression elementToBlame, @NotNull ASTNode nodeToMark, @NotNull DeclarationDescriptor declarationDescriptor, @NotNull JetProperty[] property) {
|
||||
DiagnosticWithPsiElement<JetExpression> diagnostic = super.on(elementToBlame, nodeToMark, declarationDescriptor, property);
|
||||
if (property.length == 1) {
|
||||
return diagnostic.add(DiagnosticParameters.PROPERTY, property[0]);
|
||||
}
|
||||
return diagnostic;
|
||||
}
|
||||
};
|
||||
PsiElementOnlyDiagnosticFactory1<JetExpression, DeclarationDescriptor> VAL_REASSIGNMENT = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Val can not be reassigned", NAME);
|
||||
SimplePsiElementOnlyDiagnosticFactory<JetExpression> VARIABLE_EXPECTED = new SimplePsiElementOnlyDiagnosticFactory<JetExpression>(ERROR, "Variable expected");
|
||||
|
||||
PsiElementOnlyDiagnosticFactory1<JetSimpleNameExpression, DeclarationDescriptor> INITIALIZATION_USING_BACKING_FIELD = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Initialization using backing field required", NAME);
|
||||
|
||||
@@ -87,6 +87,11 @@ public class JetPsiFactory {
|
||||
return function.getBodyExpression();
|
||||
}
|
||||
|
||||
public static JetParameter createParameter(Project project, String name, String type) {
|
||||
JetNamedFunction function = createFunction(project, "fun foo(" + name + " : " + type + ") {}");
|
||||
return function.getValueParameters().get(0);
|
||||
}
|
||||
|
||||
public static JetNamespace createNamespace(Project project, String text) {
|
||||
JetFile file = createFile(project, text);
|
||||
return file.getRootNamespace();
|
||||
@@ -96,7 +101,7 @@ public class JetPsiFactory {
|
||||
JetNamespace namespace = createNamespace(project, "import " + classPath);
|
||||
return namespace.getImportDirectives().iterator().next();
|
||||
}
|
||||
|
||||
|
||||
public static PsiElement createPrimaryConstructor(Project project) {
|
||||
JetClass aClass = createClass(project, "class A()");
|
||||
return aClass.findElementAt(7).getParent();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
+22
-3
@@ -20,11 +20,12 @@ import org.jetbrains.jet.lexer.JetTokens;
|
||||
|
||||
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
|
||||
import static org.jetbrains.jet.lang.resolve.BindingContext.INDEXED_LVALUE_SET;
|
||||
import static org.jetbrains.jet.lang.resolve.BindingContext.VARIABLE_REASSIGNMENT;
|
||||
import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.getExpressionReceiver;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
* @author abreslav
|
||||
*/
|
||||
public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingVisitor {
|
||||
private final WritableScope scope;
|
||||
|
||||
@@ -138,17 +139,35 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
|
||||
}
|
||||
assignmentOperationType = getTypeForBinaryCall(scope, counterpartName, context, expression);
|
||||
if (assignmentOperationType != null) {
|
||||
context.trace.record(BindingContext.VARIABLE_REASSIGNMENT, expression);
|
||||
context.trace.record(VARIABLE_REASSIGNMENT, expression);
|
||||
ExpressionTypingUtils.checkWrappingInRef(expression.getLeft(), context);
|
||||
}
|
||||
}
|
||||
else {
|
||||
temporaryBindingTrace.commit();
|
||||
checkReassignment(expression, context, assignmentOperationType, left);
|
||||
}
|
||||
checkLValue(context.trace, expression.getLeft());
|
||||
return checkAssignmentType(assignmentOperationType, expression, contextWithExpectedType);
|
||||
}
|
||||
|
||||
private void checkReassignment(@NotNull JetBinaryExpression expression, @NotNull ExpressionTypingContext context,
|
||||
@NotNull JetType assignmentOperationType, @Nullable JetExpression left) {
|
||||
if (left == null) return;
|
||||
JetType leftType = facade.getType(left, context.replaceScope(scope));
|
||||
if (leftType == null) return;
|
||||
boolean isUnit = context.semanticServices.getTypeChecker().
|
||||
isSubtypeOf(assignmentOperationType, JetStandardClasses.getUnitType());
|
||||
if (isUnit) return;
|
||||
|
||||
if (context.semanticServices.getTypeChecker().isSubtypeOf(assignmentOperationType, leftType)) {
|
||||
context.trace.record(VARIABLE_REASSIGNMENT, expression);
|
||||
}
|
||||
else {
|
||||
context.trace.report(TYPE_MISMATCH.on(expression, leftType, assignmentOperationType));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JetType visitAssignment(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) {
|
||||
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceExpectedReturnType(TypeUtils.NO_EXPECTED_TYPE);
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
//KT-600 Problem with 'sure' extension function type inference
|
||||
|
||||
fun <T : Any> T?.sure() : T { if (this != null) return this else throw NullPointerException() }
|
||||
|
||||
fun test() {
|
||||
val i : Int? = 10
|
||||
val <!UNUSED_VARIABLE!>i2<!> : Int = i.sure() // inferred type is Int? but Int was excepted
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
import java.util.Collections
|
||||
import java.util.List
|
||||
|
||||
val ab = Collections.emptyList<Int>() : List<Int>?
|
||||
+4
-2
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
namespace abstract
|
||||
|
||||
class MyClass() {
|
||||
@@ -235,7 +237,7 @@ abstract class B3(i: Int) {
|
||||
this(): this(1)
|
||||
}
|
||||
|
||||
fun foo(a: B3) {
|
||||
fun foo(c: B3) {
|
||||
val <!UNUSED_VARIABLE!>a<!> = <!CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS!>B3()<!>
|
||||
val <!UNUSED_VARIABLE!>b<!> = <!CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS!>B1(2, "s")<!>
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ class AClass() {
|
||||
val x : Any? = 1
|
||||
|
||||
fun Any?.vars(a: Any?) : Int {
|
||||
var <!UNUSED_VARIABLE!>b<!>: Int = 0
|
||||
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>b<!>: Int = 0
|
||||
if (ns.y is Int) {
|
||||
b = <!UNUSED_VALUE!>ns.y<!>
|
||||
}
|
||||
@@ -74,7 +74,7 @@ trait T {}
|
||||
|
||||
open class C {
|
||||
fun foo() {
|
||||
var <!UNUSED_VARIABLE!>t<!> : T? = null
|
||||
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>t<!> : T? = null
|
||||
if (this is T) {
|
||||
t = <!UNUSED_VALUE!>this<!>
|
||||
}
|
||||
@@ -88,4 +88,4 @@ open class C {
|
||||
t = <!UNUSED_VALUE!>this@C<!>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ class C() {
|
||||
}
|
||||
|
||||
fun f(): Unit {
|
||||
var x: Int? = 1
|
||||
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>x<!>: Int? = 1
|
||||
x = 1
|
||||
x <!UNSAFE_INFIX_CALL!>+<!> 1
|
||||
x <!UNSAFE_INFIX_CALL!>plus<!> 1
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
import java.util.*
|
||||
|
||||
namespace html {
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
namespace Jet86
|
||||
|
||||
class A {
|
||||
@@ -27,4 +29,4 @@ val s = <!NO_CLASS_OBJECT!>System<!> // error
|
||||
fun test() {
|
||||
System.out?.println()
|
||||
java.lang.System.out?.println()
|
||||
}
|
||||
}
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
trait A {
|
||||
fun foo() : Int = 1
|
||||
fun foo2() : Int = 1
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
fun Int?.optint() : Unit {}
|
||||
val Int?.optval : Unit = ()
|
||||
|
||||
@@ -68,4 +70,4 @@ namespace null_safety {
|
||||
if (command == null) 1
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
import java.util.*;
|
||||
|
||||
class NotRange1() {
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
fun none() {}
|
||||
|
||||
fun unitEmptyInfer() {}
|
||||
@@ -209,4 +211,4 @@ fun testFunctionLiterals() {
|
||||
object A {}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
namespace foo
|
||||
|
||||
class X {}
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
namespace foobar
|
||||
|
||||
namespace a {
|
||||
@@ -60,4 +62,4 @@ abstract class Collection<E> : Iterable<E> {
|
||||
}
|
||||
return iteratee.done()
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
fun test() {
|
||||
val a : Int? = 0
|
||||
if (a != null) {
|
||||
@@ -277,4 +279,4 @@ fun f9(a : Int?) : Int {
|
||||
if (a != null)
|
||||
return a
|
||||
return 1
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
// Fixpoint generic in Java: Enum<T extends Enum<T>>
|
||||
fun test(a : annotation.RetentionPolicy) {
|
||||
|
||||
@@ -17,4 +19,4 @@ fun test(a : java.util.ArrayList<Int>) {
|
||||
|
||||
fun test(a : java.lang.Class<Int>) {
|
||||
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
import java.*
|
||||
import util.*
|
||||
import <!UNRESOLVED_REFERENCE!>utils<!>.*
|
||||
@@ -49,4 +51,4 @@ fun test(l : java.util.List<Int>) {
|
||||
|
||||
namespace xxx {
|
||||
import java.lang.Class;
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -16,7 +16,7 @@ fun t1(b : Boolean) {
|
||||
}
|
||||
doSmth(<!UNINITIALIZED_VARIABLE!>u<!>)
|
||||
|
||||
var r: String
|
||||
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>r<!>: String
|
||||
if (b) {
|
||||
r = "s"
|
||||
}
|
||||
@@ -63,10 +63,10 @@ fun t4(a: A, val b: A, var c: A) {
|
||||
// reassigned vals
|
||||
|
||||
fun t1() {
|
||||
val <!UNUSED_VARIABLE!>a<!> : Int = 1
|
||||
val <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>a<!> : Int = 1
|
||||
<!VAL_REASSIGNMENT!>a<!> = <!UNUSED_VALUE!>2<!>
|
||||
|
||||
var <!UNUSED_VARIABLE!>b<!> : Int = 1
|
||||
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>b<!> : Int = 1
|
||||
b = <!UNUSED_VALUE!>3<!>
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
fun t1() : Int{
|
||||
return 0
|
||||
<!UNREACHABLE_CODE!>1<!>
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace unused_variables
|
||||
|
||||
fun testSimpleCases() {
|
||||
var i = 2
|
||||
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>i<!> = 2
|
||||
i = <!UNUSED_VALUE!>34<!>
|
||||
i = 34
|
||||
doSmth(i)
|
||||
@@ -9,7 +9,7 @@ fun testSimpleCases() {
|
||||
|
||||
var j = 2
|
||||
j = <!UNUSED_CHANGED_VALUE!>j++<!>
|
||||
j = <!UNUSED_CHANGED_VALUE, UNUSED_VALUE!>j--<!>
|
||||
j = <!UNUSED_VALUE, UNUSED_CHANGED_VALUE!>j--<!>
|
||||
}
|
||||
|
||||
class IncDec() {
|
||||
@@ -45,7 +45,7 @@ class MyTest() {
|
||||
fun testSimple() {
|
||||
a = "rro"
|
||||
|
||||
var <!UNUSED_VARIABLE!>i<!> = 1;
|
||||
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>i<!> = 1;
|
||||
i = <!UNUSED_VALUE!>34<!>;
|
||||
i = <!UNUSED_VALUE!>456<!>;
|
||||
}
|
||||
@@ -62,7 +62,7 @@ class MyTest() {
|
||||
}
|
||||
|
||||
fun testIf() {
|
||||
var a : Any
|
||||
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>a<!> : Any
|
||||
if (1 < 2) {
|
||||
a = 23
|
||||
}
|
||||
@@ -91,7 +91,7 @@ class MyTest() {
|
||||
}
|
||||
|
||||
fun testInnerFunctions() {
|
||||
var <!UNUSED_VARIABLE!>y<!> = 1
|
||||
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>y<!> = 1
|
||||
fun foo() {
|
||||
y = <!UNUSED_VALUE!>1<!>
|
||||
}
|
||||
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
class Foo1() : java.util.ArrayList<Int>()
|
||||
|
||||
open class Bar() {
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
// KT-389 Wrong type inference for varargs etc.
|
||||
// +JDK
|
||||
|
||||
import java.util.*
|
||||
|
||||
@@ -23,4 +24,4 @@ fun test() {
|
||||
fool(1, 2, 3)
|
||||
food(1.0, 2.0, 3.0)
|
||||
foof(1.0.flt, 2.0.flt, 3.0.flt)
|
||||
}
|
||||
}
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
import java.util.List;
|
||||
|
||||
fun ff(l: Any) = l as List<*>
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
import java.util.List;
|
||||
|
||||
fun ff(a: Any) = <!UNCHECKED_CAST!>a as List<String><!>
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
import java.util.List;
|
||||
|
||||
fun ff(l: Any) = l is <!CANNOT_CHECK_FOR_ERASED!>List<String><!>
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
import java.util.List;
|
||||
|
||||
fun ff(l: Any) = l is List<*>
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
import java.util.List;
|
||||
|
||||
fun ff(l: Any) = when(l) {
|
||||
+1
-1
@@ -179,7 +179,7 @@ fun declarations(a: Any?) {
|
||||
}
|
||||
}
|
||||
fun vars(a: Any?) {
|
||||
var <!UNUSED_VARIABLE!>b<!>: Int = 0
|
||||
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>b<!>: Int = 0
|
||||
if (a is Int) {
|
||||
b = <!UNUSED_VALUE!>a<!>
|
||||
}
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
// +JDK
|
||||
|
||||
import java.*
|
||||
import util.*
|
||||
|
||||
@@ -63,4 +65,4 @@ fun main(args: Array<String>) {
|
||||
catch(e: Throwable) {
|
||||
System.out?.println(e.getMessage())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// +JDK
|
||||
|
||||
import java.util.Collections
|
||||
import java.util.List
|
||||
|
||||
val ab = Collections.emptyList<Int>() : List<Int>?
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
// JET-72 Type inference doesn't work when iterating over ArrayList
|
||||
// +JDK
|
||||
|
||||
import java.util.ArrayList
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user