From 00dd1dbb0bfecb449d61b82b58914bba958b57bc Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Fri, 11 Jul 2014 19:30:25 +0400 Subject: [PATCH] Control-Flow Analysis: Compute STATEMENT slice using control-flow information --- .../jet/lang/cfg/JetControlFlowProcessor.java | 11 ++--- .../lang/cfg/JetFlowInformationProvider.java | 47 +++++++++++++++++++ .../jetbrains/jet/lang/cfg/WhenChecker.java | 3 +- .../jet/lang/cfg/pseudocode/pseudocodeUtil.kt | 3 -- .../jet/lang/resolve/BindingContext.java | 2 +- .../jet/lang/resolve/BindingContextUtils.kt | 3 ++ .../expressions/ExpressionTypingServices.java | 3 -- .../PatternMatchingTypingVisitor.java | 4 -- .../ConvertToForEachLoopIntention.kt | 1 + .../branchedTransformations/IfThenUtils.kt | 14 +----- .../intentions/DoubleBangToIfThenIntention.kt | 2 +- .../intentions/IfThenToDoubleBangIntention.kt | 3 +- .../intentions/SafeAccessToIfThenIntention.kt | 2 +- .../plugin/project/ResolveElementCache.java | 7 +++ .../extractFunction/extractFunctionUtils.kt | 8 ++-- .../expression/ExpressionVisitor.java | 4 +- .../translate/expression/WhenTranslator.java | 34 +++++++------- .../k2js/translate/utils/BindingUtils.java | 4 -- .../utils/dangerous/FindDangerousVisitor.java | 14 ++---- 19 files changed, 99 insertions(+), 70 deletions(-) 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 9ef8379c799..4fa0b558482 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java @@ -1252,8 +1252,6 @@ public class JetControlFlowProcessor { generateInstructions(subjectExpression); } - boolean hasElse = false; - List branches = new ArrayList(); Label doneLabel = builder.createUnboundLabel(); @@ -1265,7 +1263,6 @@ public class JetControlFlowProcessor { boolean isElse = whenEntry.isElse(); if (isElse) { - hasElse = true; if (iterator.hasNext()) { trace.report(ELSE_MISPLACED_IN_WHEN.on(whenEntry)); } @@ -1300,9 +1297,6 @@ public class JetControlFlowProcessor { } } builder.bindLabel(doneLabel); - if (!hasElse && WhenChecker.mustHaveElse(expression, trace)) { - trace.report(NO_ELSE_IN_WHEN.on(expression)); - } mergeValues(branches, expression); } @@ -1407,6 +1401,11 @@ public class JetControlFlowProcessor { builder.magic(specifier, specifier, arguments, PseudocodePackage.expectedTypeFor(expectedTypePredicate, arguments), MagicKind.VALUE_CONSUMER); } + @Override + public void visitDelegationSpecifierList(@NotNull JetDelegationSpecifierList list) { + list.acceptChildren(this); + } + @Override public void visitJetFile(@NotNull JetFile file) { for (JetDeclaration declaration : file.getDeclarations()) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java index 6a54b885532..9be8dba2d50 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java @@ -29,6 +29,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableInitState; import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState; +import org.jetbrains.jet.lang.cfg.pseudocode.PseudoValue; import org.jetbrains.jet.lang.cfg.pseudocode.Pseudocode; import org.jetbrains.jet.lang.cfg.pseudocode.PseudocodeUtil; import org.jetbrains.jet.lang.cfg.pseudocode.instructions.Instruction; @@ -121,6 +122,10 @@ public class JetFlowInformationProvider { markUnusedVariables(); markUnusedLiteralsInBlock(); + + markStatements(); + + markWhenWithoutElse(); } public void checkFunction(@Nullable JetType expectedReturnType) { @@ -697,6 +702,48 @@ public class JetFlowInformationProvider { ); } +//////////////////////////////////////////////////////////////////////////////// +// Statements + + public void markStatements() { + PseudocodeTraverserPackage.traverse( + pseudocode, FORWARD, new JetFlowInformationProvider.FunctionVoid1() { + @Override + public void execute(@NotNull Instruction instruction) { + PseudoValue value = instruction instanceof InstructionWithValue + ? ((InstructionWithValue) instruction).getOutputValue() + : null; + Pseudocode pseudocode = instruction.getOwner(); + boolean isUsedAsExpression = !pseudocode.getUsages(value).isEmpty(); + for (JetElement element : pseudocode.getValueElements(value)) { + trace.record(BindingContext.USED_AS_EXPRESSION, element, isUsedAsExpression); + } + } + } + ); + } + + public void markWhenWithoutElse() { + PseudocodeTraverserPackage.traverse( + pseudocode, FORWARD, new JetFlowInformationProvider.FunctionVoid1() { + @Override + public void execute(@NotNull Instruction instruction) { + PseudoValue value = instruction instanceof InstructionWithValue + ? ((InstructionWithValue) instruction).getOutputValue() + : null; + for (JetElement element : instruction.getOwner().getValueElements(value)) { + if (element instanceof JetWhenExpression) { + JetWhenExpression whenExpression = (JetWhenExpression) element; + if (whenExpression.getElseExpression() == null && WhenChecker.mustHaveElse(whenExpression, trace)) { + trace.report(NO_ELSE_IN_WHEN.on(whenExpression)); + } + } + } + } + } + ); + } + //////////////////////////////////////////////////////////////////////////////// // Tail calls diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/WhenChecker.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/WhenChecker.java index ed8e349793f..9654ad36b4b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/WhenChecker.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/WhenChecker.java @@ -24,6 +24,7 @@ import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingTrace; +import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; @@ -37,7 +38,7 @@ public final class WhenChecker { JetType expectedType = trace.get(BindingContext.EXPECTED_EXPRESSION_TYPE, expression); boolean isUnit = expectedType != null && KotlinBuiltIns.getInstance().isUnit(expectedType); // Some "statements" are actually expressions returned from lambdas, their expected types are non-null - boolean isStatement = Boolean.TRUE.equals(trace.get(BindingContext.STATEMENT, expression)) && expectedType == null; + boolean isStatement = BindingContextUtilPackage.isUsedAsStatement(expression, trace.getBindingContext()) && expectedType == null; return !isUnit && !isStatement && !isWhenExhaustive(expression, trace); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/pseudocodeUtil.kt b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/pseudocodeUtil.kt index 3c66a32a983..ccd40fad9ed 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/pseudocodeUtil.kt +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/pseudocodeUtil.kt @@ -33,9 +33,6 @@ import org.jetbrains.jet.lang.types.TypeUtils import org.jetbrains.jet.lang.types.JetType import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction -fun JetExpression.isStatement(pseudocode: Pseudocode): Boolean = - pseudocode.getUsages(pseudocode.getElementValue(this)).isEmpty() - fun getReceiverTypePredicate(resolvedCall: ResolvedCall<*>, receiverValue: ReceiverValue): TypePredicate? { val callableDescriptor = resolvedCall.getResultingDescriptor() if (callableDescriptor == null) return null diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java index a7ebe6358f8..54708064425 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java @@ -136,7 +136,7 @@ public interface BindingContext { * Has type of current expression has been already resolved */ WritableSlice PROCESSED = Slices.createSimpleSetSlice(); - WritableSlice STATEMENT = Slices.createRemovableSetSlice(); + WritableSlice USED_AS_EXPRESSION = Slices.createRemovableSetSlice(); WritableSlice CAPTURED_IN_CLOSURE = new BasicWritableSlice(DO_NOTHING); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.kt b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.kt index f3a245f54c9..199c9417920 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.kt +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.kt @@ -139,3 +139,6 @@ public fun JetReturnExpression.getTargetFunctionDescriptor(context: BindingConte .dropWhile { it is AnonymousFunctionDescriptor } .firstOrNull() } + +public fun JetExpression.isUsedAsExpression(context: BindingContext): Boolean = context[BindingContext.USED_AS_EXPRESSION, this]!! +public fun JetExpression.isUsedAsStatement(context: BindingContext): Boolean = !isUsedAsExpression(context) diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java index 82530b21fca..6851de1d521 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java @@ -49,7 +49,6 @@ import javax.inject.Inject; import java.util.Iterator; import java.util.List; -import static org.jetbrains.jet.lang.resolve.BindingContext.STATEMENT; import static org.jetbrains.jet.lang.types.TypeUtils.*; import static org.jetbrains.jet.lang.types.expressions.CoercionStrategy.COERCION_TO_UNIT; @@ -253,7 +252,6 @@ public class ExpressionTypingServices { ); JetTypeInfo typeInfo = expressionTypingFacade.getTypeInfo(bodyExpression, context, function.hasBlockBody()); - trace.record(STATEMENT, bodyExpression, false); JetType type = typeInfo.getType(); if (type != null) { return type; @@ -283,7 +281,6 @@ public class ExpressionTypingServices { if (!(statement instanceof JetExpression)) { continue; } - trace.record(STATEMENT, statement); JetExpression statementExpression = (JetExpression) statement; if (!iterator.hasNext()) { result = getTypeOfLastExpressionInBlock( diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java index a863c59876f..f571b3f30f4 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java @@ -71,10 +71,6 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { } public JetTypeInfo visitWhenExpression(JetWhenExpression expression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) { - if (isStatement) { - contextWithExpectedType.trace.record(BindingContext.STATEMENT, expression); - } - DataFlowUtils.recordExpectedType(contextWithExpectedType.trace, expression, contextWithExpectedType.expectedType); ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT); diff --git a/idea/src/org/jetbrains/jet/plugin/intentions/ConvertToForEachLoopIntention.kt b/idea/src/org/jetbrains/jet/plugin/intentions/ConvertToForEachLoopIntention.kt index ec580dd4588..6521e2c8d35 100644 --- a/idea/src/org/jetbrains/jet/plugin/intentions/ConvertToForEachLoopIntention.kt +++ b/idea/src/org/jetbrains/jet/plugin/intentions/ConvertToForEachLoopIntention.kt @@ -42,6 +42,7 @@ public class ConvertToForEachLoopIntention : JetSelfTargetingIntention null } } + else -> null } } is JetBinaryExpression -> element.getRight() diff --git a/idea/src/org/jetbrains/jet/plugin/intentions/branchedTransformations/IfThenUtils.kt b/idea/src/org/jetbrains/jet/plugin/intentions/branchedTransformations/IfThenUtils.kt index 2a7cac01ec3..9696fcffafe 100644 --- a/idea/src/org/jetbrains/jet/plugin/intentions/branchedTransformations/IfThenUtils.kt +++ b/idea/src/org/jetbrains/jet/plugin/intentions/branchedTransformations/IfThenUtils.kt @@ -28,7 +28,6 @@ import org.jetbrains.jet.plugin.refactoring.introduce.introduceVariable.KotlinIn import org.jetbrains.jet.lang.psi.JetSafeQualifiedExpression import org.jetbrains.jet.lang.resolve.BindingContext import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache -import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns import com.intellij.psi.PsiElement import org.jetbrains.jet.plugin.refactoring.inline.KotlinInlineValHandler import org.jetbrains.jet.lang.psi.JetSimpleNameExpression @@ -43,6 +42,7 @@ import org.jetbrains.jet.lang.psi.JetPostfixExpression import org.jetbrains.jet.lang.psi.JetCallExpression import org.jetbrains.jet.lang.resolve.DescriptorUtils import org.jetbrains.jet.lang.psi.JetElement +import org.jetbrains.jet.lang.resolve.bindingContextUtil.isUsedAsStatement val NULL_PTR_EXCEPTION = "NullPointerException" val NULL_PTR_EXCEPTION_FQ = "java.lang.NullPointerException" @@ -72,17 +72,7 @@ fun JetExpression.extractExpressionIfSingle(): JetExpression? { return innerExpression } -fun JetExpression.isStatement(): Boolean { - val context = AnalyzerFacadeWithCache.getContextForElement(this) - - val expectedType = context.get(BindingContext.EXPECTED_EXPRESSION_TYPE, this); - val isUnit = expectedType != null && KotlinBuiltIns.getInstance().isUnit(expectedType); - - // Some "statements" are actually expressions returned from lambdas, their expected types are non-null - val isStatement = context.get(BindingContext.STATEMENT, this) == true && expectedType == null; - - return isStatement || isUnit -} +fun JetExpression.isStatement(): Boolean = isUsedAsStatement(AnalyzerFacadeWithCache.getContextForElement(this)) fun JetBinaryExpression.getNonNullExpression(): JetExpression? = when { this.getLeft()?.isNullExpression() == false -> diff --git a/idea/src/org/jetbrains/jet/plugin/intentions/branchedTransformations/intentions/DoubleBangToIfThenIntention.kt b/idea/src/org/jetbrains/jet/plugin/intentions/branchedTransformations/intentions/DoubleBangToIfThenIntention.kt index 7c4528df69c..4afbe1261c2 100644 --- a/idea/src/org/jetbrains/jet/plugin/intentions/branchedTransformations/intentions/DoubleBangToIfThenIntention.kt +++ b/idea/src/org/jetbrains/jet/plugin/intentions/branchedTransformations/intentions/DoubleBangToIfThenIntention.kt @@ -35,9 +35,9 @@ import com.intellij.codeInsight.template.TemplateBuilderImpl import com.intellij.psi.PsiDocumentManager import org.apache.commons.lang.StringEscapeUtils.escapeJava import org.jetbrains.jet.plugin.intentions.branchedTransformations.convertToIfNullExpression -import org.jetbrains.jet.plugin.intentions.branchedTransformations.isStatement import org.jetbrains.jet.plugin.intentions.branchedTransformations.NULL_PTR_EXCEPTION import org.jetbrains.jet.plugin.intentions.branchedTransformations.KOTLIN_NULL_PTR_EXCEPTION +import org.jetbrains.jet.plugin.intentions.branchedTransformations.isStatement public class DoubleBangToIfThenIntention : JetSelfTargetingIntention("double.bang.to.if.then", javaClass()) { diff --git a/idea/src/org/jetbrains/jet/plugin/intentions/branchedTransformations/intentions/IfThenToDoubleBangIntention.kt b/idea/src/org/jetbrains/jet/plugin/intentions/branchedTransformations/intentions/IfThenToDoubleBangIntention.kt index 726d4d4892a..2678c00112e 100644 --- a/idea/src/org/jetbrains/jet/plugin/intentions/branchedTransformations/intentions/IfThenToDoubleBangIntention.kt +++ b/idea/src/org/jetbrains/jet/plugin/intentions/branchedTransformations/intentions/IfThenToDoubleBangIntention.kt @@ -32,9 +32,8 @@ import org.jetbrains.jet.plugin.intentions.branchedTransformations.extractExpres import org.jetbrains.jet.lang.psi.JetThrowExpression import org.jetbrains.jet.plugin.JetBundle import org.jetbrains.jet.plugin.intentions.branchedTransformations.isNullExpressionOrEmptyBlock -import org.jetbrains.jet.plugin.intentions.branchedTransformations.isStatement import org.jetbrains.jet.plugin.intentions.branchedTransformations.throwsNullPointerExceptionWithNoArguments - +import org.jetbrains.jet.plugin.intentions.branchedTransformations.isStatement public class IfThenToDoubleBangIntention : JetSelfTargetingIntention("if.then.to.double.bang", javaClass()) { diff --git a/idea/src/org/jetbrains/jet/plugin/intentions/branchedTransformations/intentions/SafeAccessToIfThenIntention.kt b/idea/src/org/jetbrains/jet/plugin/intentions/branchedTransformations/intentions/SafeAccessToIfThenIntention.kt index 1ad819e7539..af065ba0981 100644 --- a/idea/src/org/jetbrains/jet/plugin/intentions/branchedTransformations/intentions/SafeAccessToIfThenIntention.kt +++ b/idea/src/org/jetbrains/jet/plugin/intentions/branchedTransformations/intentions/SafeAccessToIfThenIntention.kt @@ -25,8 +25,8 @@ import org.jetbrains.jet.lang.psi.JetBinaryExpression import org.jetbrains.jet.plugin.intentions.branchedTransformations.convertToIfNotNullExpression import org.jetbrains.jet.plugin.intentions.branchedTransformations.introduceValueForCondition import org.jetbrains.jet.lang.psi.JetDotQualifiedExpression -import org.jetbrains.jet.plugin.intentions.branchedTransformations.isStatement import org.jetbrains.jet.plugin.intentions.branchedTransformations.isStableVariable +import org.jetbrains.jet.plugin.intentions.branchedTransformations.isStatement public class SafeAccessToIfThenIntention : JetSelfTargetingIntention("safe.access.to.if.then", javaClass()) { override fun isApplicableTo(element: JetSafeQualifiedExpression): Boolean = true diff --git a/idea/src/org/jetbrains/jet/plugin/project/ResolveElementCache.java b/idea/src/org/jetbrains/jet/plugin/project/ResolveElementCache.java index 96e412feb46..dd971daecae 100644 --- a/idea/src/org/jetbrains/jet/plugin/project/ResolveElementCache.java +++ b/idea/src/org/jetbrains/jet/plugin/project/ResolveElementCache.java @@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.analyzer.AnalyzerPackage; import org.jetbrains.jet.di.InjectorForBodyResolve; +import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.descriptors.annotations.Annotated; import org.jetbrains.jet.lang.psi.*; @@ -173,6 +174,8 @@ public class ResolveElementCache { assert false : "Invalid type of the topmost parent"; } + new JetFlowInformationProvider(resolveElement, trace).checkDeclaration(); + return trace.getBindingContext(); } @@ -315,6 +318,10 @@ public class ResolveElementCache { } bodyResolver.resolvePropertyAccessors(bodyResolveContext, jetProperty, descriptor); + + for (JetPropertyAccessor accessor : jetProperty.getAccessors()) { + new JetFlowInformationProvider(accessor, trace).checkDeclaration(); + } } private static void functionAdditionalResolve( diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/extractFunctionUtils.kt b/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/extractFunctionUtils.kt index 481608a9502..e65f5057084 100644 --- a/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/extractFunctionUtils.kt +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/extractFunctionUtils.kt @@ -64,6 +64,7 @@ import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.TraversalOrder import org.jetbrains.jet.lang.resolve.bindingContextUtil.getTargetFunctionDescriptor import com.intellij.psi.PsiWhiteSpace import org.jetbrains.jet.lang.resolve.OverridingUtil +import org.jetbrains.jet.lang.resolve.bindingContextUtil.isUsedAsStatement import org.jetbrains.jet.lang.psi.psiUtil.isAncestor import org.jetbrains.jet.plugin.intentions.declarations.DeclarationUtils import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils @@ -93,7 +94,6 @@ private fun List.getExitPoints(): List = filter { localInstruction -> localInstruction.nextInstructions.any { it !in this } } private fun List.getResultType( - pseudocode: Pseudocode, bindingContext: BindingContext, options: ExtractionOptions): JetType { fun instructionToType(instruction: Instruction): JetType? { @@ -106,7 +106,7 @@ private fun List.getResultType( } if (expression == null) return null - if (options.inferUnitTypeForUnusedValues && expression.isStatement(pseudocode)) return null + if (options.inferUnitTypeForUnusedValues && expression.isUsedAsStatement(bindingContext)) return null return bindingContext[BindingContext.EXPRESSION_TYPE, expression] } @@ -206,8 +206,8 @@ private fun ExtractionData.analyzeControlFlow( val nonLocallyUsedDeclarations = getLocalDeclarationsWithNonLocalUsages(pseudocode, localInstructions, bindingContext) val (declarationsToCopy, declarationsToReport) = nonLocallyUsedDeclarations.partition { it is JetProperty && it.isLocal() } - val typeOfDefaultFlow = defaultExits.getResultType(pseudocode, bindingContext, options) - val returnValueType = valuedReturnExits.getResultType(pseudocode, bindingContext, options) + val typeOfDefaultFlow = defaultExits.getResultType(bindingContext, options) + val returnValueType = valuedReturnExits.getResultType(bindingContext, options) val defaultReturnType = if (returnValueType.isMeaningful()) returnValueType else typeOfDefaultFlow if (defaultReturnType.isError()) return Pair(DefaultControlFlow(DEFAULT_RETURN_TYPE, declarationsToCopy), ErrorMessage.ERROR_TYPES) diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java index bff86d88814..bd37fa3bce9 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java @@ -25,6 +25,7 @@ import org.jetbrains.jet.lang.descriptors.VariableDescriptor; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContextUtils; +import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage; import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; import org.jetbrains.jet.lang.resolve.constants.NullValue; import org.jetbrains.jet.lang.types.JetType; @@ -42,7 +43,6 @@ import org.jetbrains.k2js.translate.reference.AccessTranslationUtils; import org.jetbrains.k2js.translate.reference.CallExpressionTranslator; import org.jetbrains.k2js.translate.reference.QualifiedExpressionTranslator; import org.jetbrains.k2js.translate.reference.ReferenceTranslator; -import org.jetbrains.k2js.translate.utils.BindingUtils; import org.jetbrains.k2js.translate.utils.JsAstUtils; import org.jetbrains.k2js.translate.utils.TranslationUtils; import org.jetbrains.k2js.translate.utils.mutator.AssignToExpressionMutator; @@ -185,7 +185,7 @@ public final class ExpressionVisitor extends TranslatorVisitor { JsNode thenNode = thenExpression.accept(this, context); JsNode elseNode = elseExpression == null ? null : elseExpression.accept(this, context); - boolean isKotlinStatement = BindingUtils.isStatement(context.bindingContext(), expression); + boolean isKotlinStatement = BindingContextUtilPackage.isUsedAsStatement(expression, context.bindingContext()); boolean canBeJsExpression = thenNode instanceof JsExpression && elseNode instanceof JsExpression; if (!isKotlinStatement && canBeJsExpression) { return new JsConditional(testExpression, convertToExpression(thenNode), convertToExpression(elseNode)).source(expression); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java index bd8ce45cdbc..798a1f9ec0e 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java @@ -21,10 +21,10 @@ import com.intellij.openapi.util.Pair; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage; import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.general.AbstractTranslator; import org.jetbrains.k2js.translate.general.Translation; -import org.jetbrains.k2js.translate.utils.BindingUtils; import org.jetbrains.k2js.translate.utils.TranslationUtils; import org.jetbrains.k2js.translate.utils.mutator.AssignToExpressionMutator; import org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator; @@ -39,7 +39,7 @@ public final class WhenTranslator extends AbstractTranslator { public static JsNode translate(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) { WhenTranslator translator = new WhenTranslator(expression, context); - if (BindingUtils.isStatement(context.bindingContext(), expression)) { + if (BindingContextUtilPackage.isUsedAsStatement(expression, context.bindingContext())) { JsBlock jsBlock = new JsBlock(); translator.translateAsStatement(jsBlock.getStatements()); return jsBlock; @@ -83,25 +83,25 @@ public final class WhenTranslator extends AbstractTranslator { JsIf prevIf = null; for (JetWhenEntry entry : whenExpression.getEntries()) { - JsStatement statement = withReturnValueCaptured(translateEntryExpression(entry)); - if (entry.isElse()) { - if (prevIf == null) { - statements.add(statement); - } - else { - prevIf.setElseStatement(statement); - } - break; + JsBlock entryBody = new JsBlock(); + JsStatement statement = withReturnValueCaptured(translateEntryExpression(entry, context().innerBlock(entryBody))); + if (!entryBody.isEmpty()) { + entryBody.getStatements().add(statement); + statement = entryBody; } - JsIf ifStatement = new JsIf(translateConditions(entry), statement); + JsStatement statementToAdd = entry.isElse() ? statement : new JsIf(translateConditions(entry), statement); if (prevIf == null) { - statements.add(ifStatement); + statements.add(statementToAdd); } else { - prevIf.setElseStatement(ifStatement); + prevIf.setElseStatement(statementToAdd); } - prevIf = ifStatement; + + if (entry.isElse()) break; + + assert statementToAdd instanceof JsIf : "Non-else entry expected: " + entry.getText(); + prevIf = (JsIf) statementToAdd; } } @@ -127,10 +127,10 @@ public final class WhenTranslator extends AbstractTranslator { } @NotNull - private JsNode translateEntryExpression(@NotNull JetWhenEntry entry) { + private JsNode translateEntryExpression(@NotNull JetWhenEntry entry, @NotNull TranslationContext context) { JetExpression expressionToExecute = entry.getExpression(); assert expressionToExecute != null : "WhenEntry should have whenExpression to execute."; - return Translation.translateExpression(expressionToExecute, context()); + return Translation.translateExpression(expressionToExecute, context); } @NotNull diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/BindingUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/BindingUtils.java index 0406f6c63ad..7f58adb73e5 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/BindingUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/BindingUtils.java @@ -95,10 +95,6 @@ public final class BindingUtils { return (JsDescriptorUtils.findAncestorClass(superclassDescriptors) != null); } - public static boolean isStatement(@NotNull BindingContext context, @NotNull JetExpression expression) { - return BindingContextUtils.getNotNull(context, BindingContext.STATEMENT, expression); - } - @NotNull public static JetType getTypeByReference(@NotNull BindingContext context, @NotNull JetTypeReference typeReference) { diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/dangerous/FindDangerousVisitor.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/dangerous/FindDangerousVisitor.java index 7d44c81f674..f3cc62e0698 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/dangerous/FindDangerousVisitor.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/dangerous/FindDangerousVisitor.java @@ -18,11 +18,10 @@ package org.jetbrains.k2js.translate.utils.dangerous; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage; import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.reference.InlinedCallExpressionTranslator; -import static org.jetbrains.k2js.translate.utils.BindingUtils.isStatement; - public final class FindDangerousVisitor extends JetTreeVisitor { @NotNull @@ -63,12 +62,9 @@ public final class FindDangerousVisitor extends JetTreeVisitor { @Override public Void visitBlockExpression(@NotNull JetBlockExpression expression, DangerousData data) { - if (isStatement(context.bindingContext(), expression)) { - return null; - } - else { - return super.visitBlockExpression(expression, data); - } + return BindingContextUtilPackage.isUsedAsStatement(expression, context.bindingContext()) + ? null + : super.visitBlockExpression(expression, data); } @Override @@ -85,7 +81,7 @@ public final class FindDangerousVisitor extends JetTreeVisitor { if (data.exists()) { return true; } - if (!isStatement(context.bindingContext(), expression)) { + if (!BindingContextUtilPackage.isUsedAsStatement(expression, context.bindingContext())) { data.setDangerousNode(expression); return true; }