KT-799 Allow 'return' expressions in conditionals assigned to variables
This commit is contained in:
@@ -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
|
||||
|
||||
+2
-2
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+43
-17
@@ -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) {
|
||||
}
|
||||
Reference in New Issue
Block a user