Added return label check

This commit is contained in:
svtk
2011-10-13 15:48:56 +04:00
parent 8db6aa4ab4
commit d9b4c972f7
4 changed files with 104 additions and 76 deletions
@@ -488,15 +488,20 @@ public class JetControlFlowProcessor {
String labelName = expression.getLabelName();
assert labelName != null;
PsiElement labeledElement = BindingContextUtils.resolveToDeclarationPsiElement(trace.getBindingContext(), labelElement);
assert labeledElement instanceof JetElement;
subroutine = (JetElement) labeledElement;
if (labeledElement != null) {
assert labeledElement instanceof JetElement;
subroutine = (JetElement) labeledElement;
}
else {
subroutine = null;
}
//subroutine = resolveLabel(labelName, labelElement, true);
}
else {
subroutine = builder.getCurrentSubroutine();
// TODO : a context check
}
if (subroutine != null) {
if (subroutine instanceof JetFunction || subroutine instanceof JetFunctionLiteralExpression) {
if (returnedExpression == null) {
builder.returnNoValue(expression, subroutine);
}
@@ -504,6 +509,11 @@ public class JetControlFlowProcessor {
builder.returnValue(expression, subroutine);
}
}
else {
if (labelElement != null) {
trace.report(NOT_A_RETURN_LABEL.on(expression, expression.getLabelName()));
}
}
}
@Override
@@ -222,6 +222,7 @@ public interface Errors {
SimpleDiagnosticFactory VALUE_PARAMETER_WITH_NO_TYPE_ANNOTATION = SimpleDiagnosticFactory.create(ERROR, "A type annotation is required on a value parameter");
SimpleDiagnosticFactory BREAK_OR_CONTINUE_OUTSIDE_A_LOOP = SimpleDiagnosticFactory.create(ERROR, "'break' and 'continue' are only allowed inside a loop");
ParameterizedDiagnosticFactory1<String> NOT_A_LOOP_LABEL = ParameterizedDiagnosticFactory1.create(ERROR, "The label ''{0}'' does not denote a loop");
ParameterizedDiagnosticFactory1<String> NOT_A_RETURN_LABEL = ParameterizedDiagnosticFactory1.create(ERROR, "The label ''{0}'' does not reference to a context from which we can return");
SimpleDiagnosticFactory ANONYMOUS_INITIALIZER_WITHOUT_CONSTRUCTOR = SimpleDiagnosticFactory.create(ERROR, "Anonymous initializers are only allowed in the presence of a primary constructor");
SimpleDiagnosticFactory NULLABLE_SUPERTYPE = SimpleDiagnosticFactory.create(ERROR, "A supertype cannot be nullable");
@@ -295,7 +295,7 @@ public class JetTypeInferrer {
private void checkFunctionReturnType(JetScope functionInnerScope, JetDeclarationWithBody function, FunctionDescriptor functionDescriptor, @NotNull final JetType expectedReturnType, @NotNull DataFlowInfo dataFlowInfo) {
JetExpression bodyExpression = function.getBodyExpression();
assert bodyExpression != null;
if (bodyExpression == null) return;
final boolean blockBody = function.hasBlockBody();
final TypeInferenceContext context =
@@ -1167,12 +1167,7 @@ public class JetTypeInferrer {
@Override
public JetType visitReturnExpression(JetReturnExpression expression, TypeInferenceContext context) {
JetSimpleNameExpression labelElement = expression.getTargetLabel();
if (labelElement != null) {
String labelName = expression.getLabelName();
assert labelName != null;
labelsResolver.resolveLabel(labelName, labelElement, true, context);
}
labelsResolver.recordLabel(expression, context);
if (context.expectedReturnType == FORBIDDEN) {
// context.trace.getErrorHandler().genericError(expression.getNode(), "'return' is not allowed here");
context.trace.report(RETURN_NOT_ALLOWED.on(expression));
@@ -1195,13 +1190,13 @@ public class JetTypeInferrer {
@Override
public JetType visitBreakExpression(JetBreakExpression expression, TypeInferenceContext context) {
labelsResolver.resolveCorrespondingLoopLabel(expression, context);
labelsResolver.recordLabel(expression, context);
return context.services.checkType(JetStandardClasses.getNothingType(), expression, context);
}
@Override
public JetType visitContinueExpression(JetContinueExpression expression, TypeInferenceContext context) {
labelsResolver.resolveCorrespondingLoopLabel(expression, context);
labelsResolver.recordLabel(expression, context);
return context.services.checkType(JetStandardClasses.getNothingType(), expression, context);
}
@@ -1321,52 +1316,7 @@ public class JetTypeInferrer {
ReceiverDescriptor thisReceiver = null;
String labelName = expression.getLabelName();
if (labelName != null) {
Collection<DeclarationDescriptor> declarationsByLabel = context.scope.getDeclarationsByLabel(labelName);
int size = declarationsByLabel.size();
final JetSimpleNameExpression targetLabel = expression.getTargetLabel();
assert targetLabel != null;
if (size == 1) {
DeclarationDescriptor declarationDescriptor = declarationsByLabel.iterator().next();
if (declarationDescriptor instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
thisReceiver = classDescriptor.getImplicitReceiver();
}
else if (declarationDescriptor instanceof FunctionDescriptor) {
FunctionDescriptor functionDescriptor = (FunctionDescriptor) declarationDescriptor;
thisReceiver = functionDescriptor.getReceiver();
}
else {
throw new UnsupportedOperationException(); // TODO
}
context.trace.record(REFERENCE_TARGET, targetLabel, declarationDescriptor);
context.trace.record(REFERENCE_TARGET, expression.getThisReference(), declarationDescriptor);
}
else if (size == 0) {
//todo (first we put to the context, then get from it)
JetElement element = labelsResolver.resolveLabel(labelName, targetLabel, false, context);
// This uses the info written by the control flow processor
//PsiElement psiElement = BindingContextUtils.resolveToDeclarationPsiElement(context.trace.getBindingContext(), targetLabel);
if (element instanceof JetFunctionLiteralExpression) {
DeclarationDescriptor declarationDescriptor = context.trace.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, element);
if (declarationDescriptor instanceof FunctionDescriptor) {
thisReceiver = ((FunctionDescriptor) declarationDescriptor).getReceiver();
if (thisReceiver.exists()) {
context.trace.record(REFERENCE_TARGET, targetLabel, declarationDescriptor);
context.trace.record(REFERENCE_TARGET, expression.getThisReference(), declarationDescriptor);
}
}
else {
context.trace.report(UNRESOLVED_REFERENCE.on(targetLabel));
}
}
else {
context.trace.report(UNRESOLVED_REFERENCE.on(targetLabel));
}
}
else {
// context.trace.getErrorHandler().genericError(targetLabel.getNode(), "Ambiguous label");
context.trace.report(AMBIGUOUS_LABEL.on(targetLabel));
}
thisReceiver = labelsResolver.resolveThisLabel(expression, context, thisReceiver, labelName);
}
else {
thisReceiver = context.scope.getImplicitReceiver();
@@ -2881,7 +2831,7 @@ public class JetTypeInferrer {
private class LabelsResolver {
private final Map<String, Stack<JetElement>> labeledElements = new HashMap<String, Stack<JetElement>>();
private void enterLabeledElement(@NotNull String labelName, @NotNull JetExpression labeledExpression) {
public void enterLabeledElement(@NotNull String labelName, @NotNull JetExpression labeledExpression) {
JetExpression deparenthesized = JetPsiUtil.deparenthesize(labeledExpression);
if (deparenthesized != null) {
Stack<JetElement> stack = labeledElements.get(labelName);
@@ -2893,7 +2843,7 @@ public class JetTypeInferrer {
}
}
private void exitLabeledElement(JetExpression expression) {
public void exitLabeledElement(@NotNull JetExpression expression) {
JetExpression deparenthesized = JetPsiUtil.deparenthesize(expression);
// TODO : really suboptimal
for (Iterator<Map.Entry<String, Stack<JetElement>>> mapIter = labeledElements.entrySet().iterator(); mapIter.hasNext(); ) {
@@ -2911,20 +2861,15 @@ public class JetTypeInferrer {
}
}
private JetElement resolveLabel(@NotNull String labelName, @NotNull JetSimpleNameExpression labelExpression, boolean reportUnresolved, TypeInferenceContext context) {
private JetElement resolveControlLabel(@NotNull String labelName, @NotNull JetSimpleNameExpression labelExpression, boolean reportUnresolved, TypeInferenceContext context) {
Collection<DeclarationDescriptor> declarationsByLabel = context.scope.getDeclarationsByLabel(labelName);
int size = declarationsByLabel.size();
if (size == 1) {
DeclarationDescriptor declarationDescriptor = declarationsByLabel.iterator().next();
JetElement element;
if (declarationDescriptor instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
element = (JetElement) context.trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, classDescriptor);
}
else if (declarationDescriptor instanceof FunctionDescriptor) {
FunctionDescriptor functionDescriptor = (FunctionDescriptor) declarationDescriptor;
element = (JetElement) context.trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, functionDescriptor);
if (declarationDescriptor instanceof FunctionDescriptor || declarationDescriptor instanceof ClassDescriptor) {
element = (JetElement) context.trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, declarationDescriptor);
}
else {
throw new UnsupportedOperationException(); // TODO
@@ -2932,7 +2877,23 @@ public class JetTypeInferrer {
context.trace.record(LABEL_TARGET, labelExpression, element);
return element;
}
else if (size == 0) {
return resolveNamedLabel(labelName, labelExpression, reportUnresolved, context);
}
context.trace.report(AMBIGUOUS_LABEL.on(labelExpression));
return null;
}
public void recordLabel(JetLabelQualifiedExpression expression, TypeInferenceContext context) {
JetSimpleNameExpression labelElement = expression.getTargetLabel();
if (labelElement != null) {
String labelName = expression.getLabelName();
assert labelName != null;
resolveControlLabel(labelName, labelElement, true, context);
}
}
private JetElement resolveNamedLabel(@NotNull String labelName, @NotNull JetSimpleNameExpression labelExpression, boolean reportUnresolved, TypeInferenceContext context) {
Stack<JetElement> stack = labeledElements.get(labelName);
if (stack == null || stack.isEmpty()) {
if (reportUnresolved) {
@@ -2949,13 +2910,52 @@ public class JetTypeInferrer {
return result;
}
private void resolveCorrespondingLoopLabel(JetLabelQualifiedExpression expression, TypeInferenceContext context) {
String labelName = expression.getLabelName();
if (labelName != null) {
JetSimpleNameExpression targetLabel = expression.getTargetLabel();
assert targetLabel != null;
resolveLabel(labelName, targetLabel, true, context);
public ReceiverDescriptor resolveThisLabel(JetThisExpression expression, TypeInferenceContext context, ReceiverDescriptor thisReceiver, String labelName) {
Collection<DeclarationDescriptor> declarationsByLabel = context.scope.getDeclarationsByLabel(labelName);
int size = declarationsByLabel.size();
final JetSimpleNameExpression targetLabel = expression.getTargetLabel();
assert targetLabel != null;
if (size == 1) {
DeclarationDescriptor declarationDescriptor = declarationsByLabel.iterator().next();
if (declarationDescriptor instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
thisReceiver = classDescriptor.getImplicitReceiver();
}
else if (declarationDescriptor instanceof FunctionDescriptor) {
FunctionDescriptor functionDescriptor = (FunctionDescriptor) declarationDescriptor;
thisReceiver = functionDescriptor.getReceiver();
}
else {
throw new UnsupportedOperationException(); // TODO
}
PsiElement element = context.trace.get(DESCRIPTOR_TO_DECLARATION, declarationDescriptor);
assert element != null;
context.trace.record(LABEL_TARGET, targetLabel, element);
context.trace.record(REFERENCE_TARGET, expression.getThisReference(), declarationDescriptor);
}
else if (size == 0) {
JetElement element = labelsResolver.resolveNamedLabel(labelName, targetLabel, false, context);
if (element instanceof JetFunctionLiteralExpression) {
DeclarationDescriptor declarationDescriptor = context.trace.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, element);
if (declarationDescriptor instanceof FunctionDescriptor) {
thisReceiver = ((FunctionDescriptor) declarationDescriptor).getReceiver();
if (thisReceiver.exists()) {
context.trace.record(LABEL_TARGET, targetLabel, element);
context.trace.record(REFERENCE_TARGET, expression.getThisReference(), declarationDescriptor);
}
}
else {
context.trace.report(UNRESOLVED_REFERENCE.on(targetLabel));
}
}
else {
context.trace.report(UNRESOLVED_REFERENCE.on(targetLabel));
}
}
else {
context.trace.report(AMBIGUOUS_LABEL.on(targetLabel));
}
return thisReceiver;
}
}
}
@@ -0,0 +1,17 @@
namespace return
class A {
fun outer() {
fun inner() {
if (1 < 2)
return@inner
else
return@outer
}
if (1 < 2)
<!NOT_A_RETURN_LABEL!>return@A<!>
else if (2 < 3)
<!NOT_A_RETURN_LABEL!>return<!UNRESOLVED_REFERENCE!>@inner<!><!>
return@outer
}
}