From d9b4c972f7ae7835665380d5af2816ead39a65ae Mon Sep 17 00:00:00 2001 From: svtk Date: Thu, 13 Oct 2011 15:48:56 +0400 Subject: [PATCH] Added return label check --- .../jet/lang/cfg/JetControlFlowProcessor.java | 16 +- .../jet/lang/diagnostics/Errors.java | 1 + .../jet/lang/types/JetTypeInferrer.java | 146 +++++++++--------- .../checkerWithErrorTypes/full/Return.jet | 17 ++ 4 files changed, 104 insertions(+), 76 deletions(-) create mode 100644 idea/testData/checkerWithErrorTypes/full/Return.jet diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java index c841ff2d212..07337a81a63 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java @@ -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 diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java index f92959e2ef7..5c1990f36e7 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -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 NOT_A_LOOP_LABEL = ParameterizedDiagnosticFactory1.create(ERROR, "The label ''{0}'' does not denote a loop"); + ParameterizedDiagnosticFactory1 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"); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index 2f2c58b1672..25e7ce8c69f 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -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 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> labeledElements = new HashMap>(); - 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 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>> 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 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 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 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; } } } diff --git a/idea/testData/checkerWithErrorTypes/full/Return.jet b/idea/testData/checkerWithErrorTypes/full/Return.jet new file mode 100644 index 00000000000..5b087076efe --- /dev/null +++ b/idea/testData/checkerWithErrorTypes/full/Return.jet @@ -0,0 +1,17 @@ +namespace return + +class A { + fun outer() { + fun inner() { + if (1 < 2) + return@inner + else + return@outer + } + if (1 < 2) + return@A + else if (2 < 3) + return@inner + return@outer + } +}