KT-799 Allow 'return' expressions in conditionals assigned to variables

This commit is contained in:
svtk
2012-02-09 12:38:23 +04:00
parent c13f653244
commit 255e37efb9
15 changed files with 128 additions and 73 deletions
@@ -578,11 +578,6 @@ public class JetControlFlowProcessor {
if (subroutine instanceof JetFunctionLiteralExpression) {
subroutine = ((JetFunctionLiteralExpression) subroutine).getFunctionLiteral();
}
boolean error = false;
if (builder.getCurrentSubroutine() != subroutine) {
trace.report(RETURN_NOT_ALLOWED.on(expression));
error = true;
}
if (subroutine instanceof JetFunction || subroutine instanceof JetPropertyAccessor || subroutine instanceof JetSecondaryConstructor) {
if (returnedExpression == null) {
builder.returnNoValue(expression, subroutine);
@@ -591,14 +586,6 @@ public class JetControlFlowProcessor {
builder.returnValue(expression, subroutine);
}
}
else if (!error) {
if (labelElement != null) {
trace.report(NOT_A_RETURN_LABEL.on(expression, labelName));
}
else {
trace.report(RETURN_NOT_ALLOWED.on(expression));
}
}
}
@Override
@@ -20,7 +20,7 @@ public abstract class VariableDescriptorImpl extends DeclarationDescriptorImpl i
@NotNull DeclarationDescriptor containingDeclaration,
@NotNull List<AnnotationDescriptor> annotations,
@NotNull String name,
@NotNull JetType outType) {
@Nullable JetType outType) {
super(containingDeclaration, annotations, name);
this.outType = outType;
@@ -40,7 +40,7 @@ public abstract class VariableDescriptorImpl extends DeclarationDescriptorImpl i
return outType;
}
protected void setOutType(JetType outType) {
public void setOutType(JetType outType) {
assert this.outType == null;
this.outType = outType;
}
@@ -3,10 +3,7 @@ package org.jetbrains.jet.lang.resolve;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableAsFunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.types.JetType;
@@ -49,12 +46,4 @@ public class BindingContextUtils {
}
return null;
}
@Nullable
public static JetType getFunctionReturnType(@NotNull BindingContext bindingContext, @NotNull JetFunction function) {
DeclarationDescriptor descriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, function);
assert descriptor instanceof FunctionDescriptor;
FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
return functionDescriptor.getReturnType();
}
}
@@ -57,8 +57,13 @@ public class DelegatingBindingTrace implements BindingTrace {
@Override
public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
if (map.containsKey(slice, key)) {
return map.get(slice, key);
V value = map.get(slice, key);
if (slice instanceof Slices.SetSlice) {
assert value != null;
if (value.equals(true)) return value;
}
else {
if (value != null) return value;
}
return parentContext.get(slice, key);
}
@@ -425,13 +425,15 @@ public class DescriptorResolver {
@NotNull
public VariableDescriptor resolveLocalVariableDescriptor(DeclarationDescriptor containingDeclaration, JetScope scope, JetProperty property, DataFlowInfo dataFlowInfo) {
JetType type = getVariableType(scope, property, dataFlowInfo, false); // For a local variable the type must not be deferred
VariableDescriptorImpl variableDescriptor = resolveLocalVariableDescriptorWithType(containingDeclaration, property, null);
return resolveLocalVariableDescriptorWithType(containingDeclaration, property, type);
JetType type = getVariableType(scope, property, dataFlowInfo, false); // For a local variable the type must not be deferred
variableDescriptor.setOutType(type);
return variableDescriptor;
}
@NotNull
public VariableDescriptor resolveLocalVariableDescriptorWithType(DeclarationDescriptor containingDeclaration, JetProperty property, JetType type) {
public VariableDescriptorImpl resolveLocalVariableDescriptorWithType(DeclarationDescriptor containingDeclaration, JetProperty property, JetType type) {
VariableDescriptorImpl variableDescriptor = new LocalVariableDescriptor(
containingDeclaration,
annotationResolver.createAnnotationStubs(property.getModifierList()),
@@ -5,6 +5,8 @@ import com.google.common.collect.Maps;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
import org.jetbrains.jet.lang.types.*;
@@ -220,4 +222,24 @@ public class DescriptorUtils {
}
return descriptor.getClassObjectDescriptor();
}
@NotNull
public static JetType getFunctionExpectedReturnType(@NotNull FunctionDescriptor descriptor, @NotNull JetElement function) {
JetType expectedType;
if (function instanceof JetFunction) {
if (((JetFunction) function).getReturnTypeRef() != null || ((JetFunction) function).hasBlockBody()) {
expectedType = descriptor.getReturnType();
}
else {
expectedType = TypeUtils.NO_EXPECTED_TYPE;
}
}
else if (function instanceof JetSecondaryConstructor) {
expectedType = JetStandardClasses.getUnitType();
}
else {
expectedType = descriptor.getReturnType();
}
return expectedType != null ? expectedType : TypeUtils.NO_EXPECTED_TYPE;
}
}
@@ -4,7 +4,9 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.NamedFunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*;
@@ -420,27 +422,51 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
@Override
public JetType visitReturnExpression(JetReturnExpression expression, ExpressionTypingContext context) {
context.labelResolver.recordLabel(expression, context);
if (context.expectedReturnType == TypeUtils.FORBIDDEN) {
context.trace.report(RETURN_NOT_ALLOWED.on(expression));
return null;
}
JetElement element = context.labelResolver.resolveLabel(expression, context);
JetExpression returnedExpression = expression.getReturnedExpression();
JetType expectedType = context.expectedReturnType;
JetType expectedType = TypeUtils.NO_EXPECTED_TYPE;
JetExpression parentDeclaration = PsiTreeUtil.getParentOfType(expression, JetDeclaration.class);
if (parentDeclaration instanceof JetFunctionLiteral) {
parentDeclaration = (JetFunctionLiteralExpression) parentDeclaration.getParent();
}
if (parentDeclaration instanceof JetParameter) {
context.trace.report(RETURN_NOT_ALLOWED.on(expression));
}
assert parentDeclaration != null;
DeclarationDescriptor declarationDescriptor = context.trace.get(DECLARATION_TO_DESCRIPTOR, parentDeclaration);
FunctionDescriptor containingFunctionDescriptor = DescriptorUtils.getParentOfType(declarationDescriptor, FunctionDescriptor.class, false);
if (expression.getTargetLabel() == null) {
if (PsiTreeUtil.getParentOfType(expression, JetFunctionLiteral.class) ==
PsiTreeUtil.getParentOfType(expression, JetDeclaration.class)) { // expression is located in function literal
JetNamedFunction function = JetPsiUtil.getSurroundingFunction(expression);
if (function != null && function.getReturnTypeRef() != null) {
expectedType = BindingContextUtils.getFunctionReturnType(context.trace.getBindingContext(), function);
if (containingFunctionDescriptor != null) {
PsiElement containingFunction = context.trace.get(DESCRIPTOR_TO_DECLARATION, containingFunctionDescriptor);
assert containingFunction != null;
if (containingFunction instanceof JetFunctionLiteralExpression) {
do {
containingFunctionDescriptor = DescriptorUtils.getParentOfType(containingFunctionDescriptor, FunctionDescriptor.class);
containingFunction = containingFunctionDescriptor != null ? context.trace.get(DESCRIPTOR_TO_DECLARATION, containingFunctionDescriptor) : null;
} while (containingFunction instanceof JetFunctionLiteralExpression);
context.trace.report(RETURN_NOT_ALLOWED.on(expression));
}
if (containingFunctionDescriptor != null) {
expectedType = DescriptorUtils.getFunctionExpectedReturnType(containingFunctionDescriptor, (JetElement) containingFunction);
}
}
else {
context.trace.report(RETURN_NOT_ALLOWED.on(expression));
}
}
else {
PsiElement element = context.trace.get(LABEL_TARGET, expression.getTargetLabel());
if (element instanceof JetFunction && ((JetFunction) element).getReturnTypeRef() != null) {
expectedType = BindingContextUtils.getFunctionReturnType(context.trace.getBindingContext(), (JetFunction) element);
else if (element != null) {
NamedFunctionDescriptor functionDescriptor = context.trace.get(FUNCTION, element);
if (functionDescriptor != null) {
expectedType = DescriptorUtils.getFunctionExpectedReturnType(functionDescriptor, element);
if (functionDescriptor != containingFunctionDescriptor) {
context.trace.report(RETURN_NOT_ALLOWED.on(expression));
}
}
else {
context.trace.report(NOT_A_RETURN_LABEL.on(expression, expression.getLabelName()));
}
}
if (returnedExpression != null) {
@@ -456,13 +482,13 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
@Override
public JetType visitBreakExpression(JetBreakExpression expression, ExpressionTypingContext context) {
context.labelResolver.recordLabel(expression, context);
context.labelResolver.resolveLabel(expression, context);
return DataFlowUtils.checkType(JetStandardClasses.getNothingType(), expression, context);
}
@Override
public JetType visitContinueExpression(JetContinueExpression expression, ExpressionTypingContext context) {
context.labelResolver.recordLabel(expression, context);
context.labelResolver.resolveLabel(expression, context);
return DataFlowUtils.checkType(JetStandardClasses.getNothingType(), expression, context);
}
}
@@ -2,6 +2,7 @@ package org.jetbrains.jet.lang.types.expressions;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
@@ -57,6 +58,7 @@ public class LabelResolver {
}
}
@Nullable
private JetElement resolveControlLabel(@NotNull String labelName, @NotNull JetSimpleNameExpression labelExpression, boolean reportUnresolved, ExpressionTypingContext context) {
Collection<DeclarationDescriptor> declarationsByLabel = context.scope.getDeclarationsByLabel(labelName);
int size = declarationsByLabel.size();
@@ -80,13 +82,15 @@ public class LabelResolver {
return null;
}
public void recordLabel(JetLabelQualifiedExpression expression, ExpressionTypingContext context) {
@Nullable
public JetElement resolveLabel(JetLabelQualifiedExpression expression, ExpressionTypingContext context) {
JetSimpleNameExpression labelElement = expression.getTargetLabel();
if (labelElement != null) {
String labelName = expression.getLabelName();
assert labelName != null;
resolveControlLabel(labelName, labelElement, true, context);
return resolveControlLabel(labelName, labelElement, true, context);
}
return null;
}
private JetElement resolveNamedLabel(@NotNull String labelName, @NotNull JetSimpleNameExpression labelExpression, boolean reportUnresolved, ExpressionTypingContext context) {
@@ -16,11 +16,6 @@ public interface SlicedMap extends Iterable<Map.Entry<SlicedMapKey<?, ?>, ?>> {
return slice.computeValue(this, key, null, true);
}
@Override
public <K, V> boolean containsKey(ReadOnlySlice<K, V> slice, K key) {
return false;
}
@Override
public <K, V> Collection<K> getKeys(WritableSlice<K, V> slice) {
return Collections.emptySet();
@@ -34,8 +29,6 @@ public interface SlicedMap extends Iterable<Map.Entry<SlicedMapKey<?, ?>, ?>> {
<K, V> V get(ReadOnlySlice<K, V> slice, K key);
<K, V> boolean containsKey(ReadOnlySlice<K, V> slice, K key);
// slice.isCollective() must return true
<K, V> Collection<K> getKeys(WritableSlice<K, V> slice);
}
@@ -79,11 +79,6 @@ public class SlicedMapImpl implements MutableSlicedMap {
return (Collection<K>) collectiveSliceKeys.get(slice);
}
@Override
public <K, V> boolean containsKey(ReadOnlySlice<K, V> slice, K key) {
return map.containsKey(slice.makeKey(key));
}
@Override
public <K, V> V remove(RemovableSlice<K, V> slice, K key) {
//noinspection unchecked
@@ -105,9 +105,8 @@ public class Slices {
public V computeValue(SlicedMap map, K key, V value, boolean valueNotFound) {
if (valueNotFound) {
for (ReadOnlySlice<K, V> slice : furtherLookupSlices) {
if (map.containsKey(slice, key)) {
return map.get(slice, key);
}
V v = map.get(slice, key);
if (v != null) return v;
}
return defaultValue;
}
@@ -178,8 +177,8 @@ public class Slices {
@Override
public Boolean computeValue(SlicedMap map, K key, Boolean value, boolean valueNotFound) {
if (valueNotFound) return false;
return super.computeValue(map, key, value, valueNotFound);
Boolean result = super.computeValue(map, key, value, valueNotFound);
return result != null ? result : false;
}
@Override
@@ -0,0 +1,8 @@
package kt799
fun foo(b: Boolean) : String {
val a = if (b) true else return "false"
return "$a"
}
fun box() = if (foo(true) == "true" && foo(false) == "false") "OK" else "fail"
@@ -7,10 +7,10 @@ fun unitEmpty() : Unit {}
fun unitEmptyReturn() : Unit {return}
fun unitIntReturn() : Unit {return <!TYPE_MISMATCH!>1<!>}
fun unitUnitReturn() : Unit {return #()}
fun test1() : Any = {<!RETURN_TYPE_MISMATCH, RETURN_NOT_ALLOWED!>return<!>}
fun test1() : Any = {<!RETURN_NOT_ALLOWED, RETURN_TYPE_MISMATCH!>return<!>}
fun test2() : Any = @a {return@a 1}
fun test3() : Any { <!RETURN_TYPE_MISMATCH!>return<!> }
fun test4(): ()-> Unit = { <!RETURN_TYPE_MISMATCH, RETURN_NOT_ALLOWED!>return@test4<!> }
fun test4(): ()-> Unit = { <!RETURN_NOT_ALLOWED, RETURN_TYPE_MISMATCH!>return@test4<!> }
fun test5(): Any = @{ return@ }
fun test6(): Any = {<!RETURN_NOT_ALLOWED!>return 1<!>}
@@ -9,9 +9,9 @@ class A {
<!RETURN_NOT_ALLOWED!>return@outer<!>
}
if (1 < 2)
<!RETURN_NOT_ALLOWED!>return@A<!>
<!NOT_A_RETURN_LABEL!>return@A<!>
else if (2 < 3)
<!RETURN_NOT_ALLOWED!>return<!UNRESOLVED_REFERENCE!>@inner<!><!>
return<!UNRESOLVED_REFERENCE!>@inner<!>
return@outer
}
}
}
@@ -0,0 +1,25 @@
//KT-799 Allow 'return' expressions in conditionals assigned to variables
package kt799
fun test() {
val <!UNUSED_VARIABLE!>a<!> : Int = if (true) 6 else return // should be allowed
val <!UNUSED_VARIABLE!>b<!> = if (true) 6 else return // should be allowed
doSmth(if (true) 3 else return)
doSmth(if (true) 3 else return, <!TOO_MANY_ARGUMENTS!>1<!>)
}
val a : Nothing = <!RETURN_NOT_ALLOWED!>return 1<!>
val b = <!RETURN_NOT_ALLOWED!>return 1<!>
val c = doSmth(if (true) 3 else <!RETURN_NOT_ALLOWED!>return<!>)
fun f(<!UNUSED_PARAMETER!>mi<!>: Int = if (true) 0 else <!RETURN_NOT_ALLOWED!>return<!>) {}
fun doSmth(<!UNUSED_PARAMETER!>i<!>: Int) {
}