From 2e2c31ea00b60ae75120bcc8721c50db098dcec6 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Thu, 3 Feb 2011 15:10:07 +0300 Subject: [PATCH] Typing for When-expressions --- idea/src/org/jetbrains/jet/JetNodeTypes.java | 4 +- .../jetbrains/jet/lang/psi/JetVisitor.java | 8 ++++ .../jetbrains/jet/lang/psi/JetWhenEntry.java | 40 +++++++++++++++++++ .../jet/lang/psi/JetWhenExpression.java | 26 ++++++++++++ .../jet/lang/types/JetTypeChecker.java | 24 ++++++++++- .../jet/types/JetTypeCheckerTest.java | 10 +++++ 6 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/lang/psi/JetWhenEntry.java create mode 100644 idea/src/org/jetbrains/jet/lang/psi/JetWhenExpression.java diff --git a/idea/src/org/jetbrains/jet/JetNodeTypes.java b/idea/src/org/jetbrains/jet/JetNodeTypes.java index 916b6b79ed9..a45b5bdb187 100644 --- a/idea/src/org/jetbrains/jet/JetNodeTypes.java +++ b/idea/src/org/jetbrains/jet/JetNodeTypes.java @@ -115,8 +115,8 @@ public interface JetNodeTypes { JetNodeType TYPE_PATTERN = new JetNodeType("TYPE_PATTERN"); JetNodeType EXPRESSION_PATTERN = new JetNodeType("EXPRESSION_PATTERN"); JetNodeType BINDING_PATTERN = new JetNodeType("BINDING_PATTERN"); - JetNodeType WHEN = new JetNodeType("WHEN"); - JetNodeType WHEN_ENTRY = new JetNodeType("WHEN_ENTRY"); + JetNodeType WHEN = new JetNodeType("WHEN", JetWhenExpression.class); + JetNodeType WHEN_ENTRY = new JetNodeType("WHEN_ENTRY", JetWhenEntry.class); JetNodeType WHEN_CONDITION = new JetNodeType("WHEN_CONDITION"); JetNodeType TUPLE_PATTERN_ENTRY = new JetNodeType("TUPLE_PATTERN_ENTRY"); JetNodeType NULLABLE_TYPE = new JetNodeType("NULLABLE_TYPE", JetNullableType.class); diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetVisitor.java b/idea/src/org/jetbrains/jet/lang/psi/JetVisitor.java index a5e9c5058a9..5dc3e611369 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetVisitor.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetVisitor.java @@ -186,6 +186,10 @@ public class JetVisitor extends PsiElementVisitor { visitExpression(expression); } + public void visitWhenExpression(JetWhenExpression expression) { + visitExpression(expression); + } + public void visitTryExpression(JetTryExpression expression) { visitExpression(expression); } @@ -321,4 +325,8 @@ public class JetVisitor extends PsiElementVisitor { public void visitTypeProjection(JetTypeProjection typeProjection) { visitJetElement(typeProjection); } + + public void visitWhenEntry(JetWhenEntry jetWhenEntry) { + visitJetElement(jetWhenEntry); + } } diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetWhenEntry.java b/idea/src/org/jetbrains/jet/lang/psi/JetWhenEntry.java new file mode 100644 index 00000000000..8fa37ab930a --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/psi/JetWhenEntry.java @@ -0,0 +1,40 @@ +package org.jetbrains.jet.lang.psi; + +import com.intellij.lang.ASTNode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.JetNodeTypes; +import org.jetbrains.jet.lexer.JetTokens; + +/** + * @author abreslav + */ +public class JetWhenEntry extends JetElement { + public JetWhenEntry(@NotNull ASTNode node) { + super(node); + } + + public boolean isElse() { + return findChildByType(JetTokens.ELSE_KEYWORD) != null; + } + + public boolean isElseContinue() { + return isElse() && (findChildByType(JetTokens.DOUBLE_ARROW) == null) && (findChildByType(JetTokens.CONTINUE_KEYWORD) != null); + } + + @Nullable + public JetExpression getExpression() { + return findChildByClass(JetExpression.class); + } + + @Nullable + public JetWhenExpression getSubWhen() { + // TODO: this may be a WHEN that goes after "=>" + return (JetWhenExpression) findChildByType(JetNodeTypes.WHEN); + } + + @Override + public void accept(@NotNull JetVisitor visitor) { + visitor.visitWhenEntry(this); + } +} diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetWhenExpression.java b/idea/src/org/jetbrains/jet/lang/psi/JetWhenExpression.java new file mode 100644 index 00000000000..982aac07e22 --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/psi/JetWhenExpression.java @@ -0,0 +1,26 @@ +package org.jetbrains.jet.lang.psi; + +import com.intellij.lang.ASTNode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.JetNodeTypes; + +import java.util.List; + +/** + * @author abreslav + */ +public class JetWhenExpression extends JetExpression { + public JetWhenExpression(@NotNull ASTNode node) { + super(node); + } + + @NotNull + public List getEntries() { + return findChildrenByType(JetNodeTypes.WHEN_ENTRY); + } + + @Override + public void accept(JetVisitor visitor) { + visitor.visitWhenExpression(this); + } +} diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java index a489dd7e1ea..4d2fac687a8 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java @@ -119,7 +119,7 @@ public class JetTypeChecker { @Override public void visitIfExpression(JetIfExpression expression) { // TODO : check condition type - // TODO : change types according to is and nullability according to null comparisons + // TODO : change types according to is and nullability checks JetExpression elseBranch = expression.getElse(); if (elseBranch == null) { // TODO : type-check the branch @@ -131,6 +131,15 @@ public class JetTypeChecker { } } + @Override + public void visitWhenExpression(JetWhenExpression expression) { + // TODO : what if there're sub-whens? + // TODO : what if there's "else continue"? + List expressions = new ArrayList(); + collectAllReturnTypes(expression, scope, expressions); + result[0] = commonSupertype(expressions); + } + @Override public void visitTupleExpression(JetTupleExpression expression) { List entries = expression.getEntries(); @@ -150,6 +159,19 @@ public class JetTypeChecker { return result[0]; } + private void collectAllReturnTypes(JetWhenExpression whenExpression, JetScope scope, List result) { + for (JetWhenEntry entry : whenExpression.getEntries()) { + if (entry.getSubWhen() != null) { + collectAllReturnTypes(entry.getSubWhen(), scope, result); + } else { + JetExpression resultExpression = entry.getExpression(); + if (resultExpression != null) { + result.add(getType(scope, resultExpression)); + } + } + } + } + public Type commonSupertype(Collection types) { Collection typeSet = new HashSet(types); assert !typeSet.isEmpty(); diff --git a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index 77f61d8f66d..e8fd6fb279f 100644 --- a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -89,6 +89,16 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { assertType("if (true) 1 else '1'", "Any"); } + public void testWhen() throws Exception { + assertType("when (1) { is 1 => 2; } ", "Int"); + assertType("when (1) { is 1 => 2; is 1 => '2'} ", "Any"); + assertType("when (1) { is 1 => 2; is 1 => '2'; is 1 => null} ", "Any?"); + assertType("when (1) { is 1 => 2; is 1 => '2'; else => null} ", "Any?"); + assertType("when (1) { is 1 => 2; is 1 => '2'; else continue} ", "Any"); + assertType("when (1) { is 1 => 2; is 1 => '2'; is 1 when(e) {is 1 => null}} ", "Any?"); + assertType("when (1) { is 1 => 2; is 1 => '2'; is 1 => when(e) {is 1 => null}} ", "Any?"); + } + public void testCommonSupertypes() throws Exception { assertCommonSupertype("Int", "Int", "Int");