Merge branch 'master' of git+ssh://git.labs.intellij.net/jet into KT-337

This commit is contained in:
Nikolay Krasko
2011-11-28 15:13:31 +04:00
129 changed files with 702 additions and 493 deletions
@@ -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);
}
}
+1 -3
View File
@@ -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
@@ -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);
}
@@ -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();
}
@@ -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>?
@@ -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<!>
}
}
}
}
@@ -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
@@ -1,3 +1,5 @@
// +JDK
import java.util.*
namespace html {
@@ -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()
}
}
@@ -1,3 +1,5 @@
// +JDK
trait A {
fun foo() : Int = 1
fun foo2() : Int = 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
}
}
}
@@ -1,3 +1,5 @@
// +JDK
fun none() {}
fun unitEmptyInfer() {}
@@ -209,4 +211,4 @@ fun testFunctionLiterals() {
object A {}
}
}
}
@@ -1,3 +1,5 @@
// +JDK
namespace foobar
namespace a {
@@ -60,4 +62,4 @@ abstract class Collection<E> : Iterable<E> {
}
return iteratee.done()
}
}
}
@@ -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
}
}
@@ -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>) {
}
}
@@ -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;
}
}
@@ -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
@@ -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<!>
}
@@ -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)
}
}
@@ -1,3 +1,5 @@
// +JDK
import java.util.List;
import java.util.Collection;
@@ -1,3 +1,5 @@
// +JDK
import java.util.List;
import java.util.Collection;
@@ -1,3 +1,5 @@
// +JDK
import java.util.List;
fun ff(l: Any) = l as List<*>
@@ -1,3 +1,5 @@
// +JDK
import java.util.List;
fun ff(a: Any) = <!UNCHECKED_CAST!>a as List<String><!>
@@ -1,3 +1,5 @@
// +JDK
import java.util.Collection;
import java.util.List;
@@ -1,3 +1,5 @@
// +JDK
import java.util.List;
fun ff(l: Any) = l is <!CANNOT_CHECK_FOR_ERASED!>List<String><!>
@@ -1,3 +1,5 @@
// +JDK
import java.util.List;
fun ff(l: Any) = l is List<*>
@@ -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<!>
}
@@ -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,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