Basic support for blocks and local variables
This commit is contained in:
@@ -19,7 +19,8 @@ public class JetBlockExpression extends JetExpression {
|
||||
visitor.visitBlockExpression(this);
|
||||
}
|
||||
|
||||
public List<JetExpression> getStatements() {
|
||||
return Arrays.asList(findChildrenByClass(JetExpression.class));
|
||||
@NotNull
|
||||
public List<JetElement> getStatements() {
|
||||
return Arrays.asList(findChildrenByClass(JetElement.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,9 @@ public class JetChangeUtil {
|
||||
JetNamespace rootNamespace = file.getRootNamespace();
|
||||
List<JetDeclaration> dcls = rootNamespace.getDeclarations();
|
||||
assert dcls.size() == 1;
|
||||
//noinspection unchecked
|
||||
return (T) dcls.get(0);
|
||||
@SuppressWarnings("unchecked")
|
||||
T result = (T) dcls.get(0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static PsiElement createNameIdentifier(Project project, String name) {
|
||||
|
||||
@@ -45,8 +45,8 @@ public class JetFunctionLiteralExpression extends JetExpression {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<JetExpression> getBody() {
|
||||
return PsiTreeUtil.getChildrenOfTypeAsList(getBodyNode().getPsi(), JetExpression.class);
|
||||
public List<JetElement> getBody() {
|
||||
return PsiTreeUtil.getChildrenOfTypeAsList(getBodyNode().getPsi(), JetElement.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -81,4 +81,8 @@ public class JetFunctionLiteralExpression extends JetExpression {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean hasParameterSpecification() {
|
||||
return findChildByType(JetTokens.DOUBLE_ARROW) != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.jetbrains.jet.lang.resolve;
|
||||
|
||||
import org.jetbrains.jet.lang.types.PropertyDescriptor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public class AccumulatingScope extends JetScopeAdapter {
|
||||
private final Map<String, PropertyDescriptor> properties = new HashMap<String, PropertyDescriptor>();
|
||||
|
||||
public AccumulatingScope(JetScope outerScope) {
|
||||
super(outerScope);
|
||||
}
|
||||
|
||||
public void addPropertyDescriptor(PropertyDescriptor propertyDescriptor) {
|
||||
if (properties.put(propertyDescriptor.getName(), propertyDescriptor) != null) {
|
||||
throw new IllegalArgumentException("Duplicate property: " + propertyDescriptor.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyDescriptor getProperty(String name) {
|
||||
PropertyDescriptor propertyDescriptor = properties.get(name);
|
||||
if (propertyDescriptor == null) {
|
||||
return super.getProperty(name);
|
||||
}
|
||||
return propertyDescriptor;
|
||||
}
|
||||
}
|
||||
@@ -77,8 +77,8 @@ public class ClassDescriptorResolver {
|
||||
}
|
||||
|
||||
private static final class TypeParameterExtensibleScope extends JetScopeAdapter {
|
||||
private final Map<String, TypeParameterDescriptor> typeParameterDescriptors = new HashMap<String, TypeParameterDescriptor>();
|
||||
|
||||
private final Map<String, TypeParameterDescriptor> typeParameterDescriptors = new HashMap<String, TypeParameterDescriptor>();
|
||||
private TypeParameterExtensibleScope(JetScope scope) {
|
||||
super(scope);
|
||||
}
|
||||
@@ -99,8 +99,8 @@ public class ClassDescriptorResolver {
|
||||
}
|
||||
return super.getTypeParameterDescriptor(name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@NotNull
|
||||
public PropertyDescriptor resolvePropertyDescriptor(@NotNull JetScope scope, @NotNull JetParameter parameter) {
|
||||
return new PropertyDescriptor(
|
||||
@@ -108,4 +108,24 @@ public class ClassDescriptorResolver {
|
||||
parameter.getName(),
|
||||
TypeResolver.INSTANCE.resolveType(scope, parameter.getTypeReference()));
|
||||
}
|
||||
|
||||
public static PropertyDescriptor resolvePropertyDescriptor(@NotNull JetScope scope, JetProperty property) {
|
||||
// TODO : receiver?
|
||||
JetTypeReference propertyTypeRef = property.getPropertyTypeRef();
|
||||
|
||||
Type type;
|
||||
if (propertyTypeRef == null) {
|
||||
JetExpression initializer = property.getInitializer();
|
||||
assert initializer != null;
|
||||
// TODO : ??? Fix-point here: what if we have something like "val a = foo {a.bar()}"
|
||||
type = JetTypeChecker.INSTANCE.getType(scope, initializer, false);
|
||||
} else {
|
||||
type = TypeResolver.INSTANCE.resolveType(scope, propertyTypeRef);
|
||||
}
|
||||
|
||||
return new PropertyDescriptor(
|
||||
AttributeResolver.INSTANCE.resolveAttributes(property.getModifierList()),
|
||||
property.getName(),
|
||||
type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.jetbrains.jet.lang.resolve;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.types.*;
|
||||
|
||||
/**
|
||||
@@ -12,6 +13,7 @@ public class JetScopeAdapter implements JetScope {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Type getThisType() {
|
||||
return scope.getThisType();
|
||||
|
||||
@@ -2,7 +2,11 @@ package org.jetbrains.jet.lang.types;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.resolve.JetScope;
|
||||
import org.jetbrains.jet.lang.resolve.JetScopeImpl;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -135,6 +139,43 @@ public class JetStandardClasses {
|
||||
Collections.<TypeProjection>emptyList(),
|
||||
TypeMemberDomain.EMPTY);
|
||||
|
||||
private static final Map<String, ClassDescriptor> CLASS_MAP = new HashMap<String, ClassDescriptor>();
|
||||
static {
|
||||
Field[] declaredFields = JetStandardClasses.class.getDeclaredFields();
|
||||
for (Field field : declaredFields) {
|
||||
if ((field.getModifiers() & Modifier.STATIC) == 0) {
|
||||
continue;
|
||||
}
|
||||
Class<?> type = field.getType();
|
||||
if (type == ClassDescriptor.class) {
|
||||
try {
|
||||
ClassDescriptor descriptor = (ClassDescriptor) field.get(null);
|
||||
CLASS_MAP.put(descriptor.getName(), descriptor);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
} else if (type.isArray() && type.getComponentType() == ClassDescriptor.class) {
|
||||
try {
|
||||
ClassDescriptor[] array = (ClassDescriptor[]) field.get(null);
|
||||
for (ClassDescriptor descriptor : array) {
|
||||
CLASS_MAP.put(descriptor.getName(), descriptor);
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
CLASS_MAP.put("Unit", getTuple(0));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static final JetScope STANDARD_CLASSES = new JetScopeImpl() {
|
||||
@Override
|
||||
public ClassDescriptor getClass(String name) {
|
||||
return CLASS_MAP.get(name);
|
||||
}
|
||||
};
|
||||
|
||||
@NotNull
|
||||
public static ClassDescriptor getAny() {
|
||||
return ANY;
|
||||
@@ -204,6 +245,16 @@ public class JetStandardClasses {
|
||||
return TUPLE[size];
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ClassDescriptor getFunction(int parameterCount) {
|
||||
return FUNCTION[parameterCount];
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ClassDescriptor getReceiverFunction(int parameterCount) {
|
||||
return RECEIVER_FUNCTION[parameterCount];
|
||||
}
|
||||
|
||||
public static Type getIntType() {
|
||||
return INT_TYPE;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.JetNodeTypes;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.ClassDescriptorResolver;
|
||||
import org.jetbrains.jet.lang.resolve.JetScope;
|
||||
import org.jetbrains.jet.lang.resolve.JetScopeAdapter;
|
||||
import org.jetbrains.jet.lang.resolve.TypeResolver;
|
||||
import org.jetbrains.jet.lang.resolve.*;
|
||||
import org.jetbrains.jet.lexer.JetTokens;
|
||||
|
||||
import java.util.*;
|
||||
@@ -33,7 +30,7 @@ public class JetTypeChecker {
|
||||
: declaration
|
||||
: "namespace" // for the root namespace
|
||||
*/
|
||||
public Type getType(@NotNull final JetScope scope, @NotNull JetExpression expression) {
|
||||
public Type getType(@NotNull final JetScope scope, @NotNull JetExpression expression, final boolean preferBlock) {
|
||||
final Type[] result = new Type[1];
|
||||
expression.accept(new JetVisitor() {
|
||||
@Override
|
||||
@@ -48,6 +45,11 @@ public class JetTypeChecker {
|
||||
|
||||
@Override
|
||||
public void visitFunctionLiteralExpression(JetFunctionLiteralExpression expression) {
|
||||
if (preferBlock && !expression.hasParameterSpecification()) {
|
||||
result[0] = getBlockReturnedType(scope, expression.getBody());
|
||||
return;
|
||||
}
|
||||
|
||||
JetTypeReference returnTypeRef = expression.getReturnTypeRef();
|
||||
|
||||
JetTypeReference receiverTypeRef = expression.getReceiverTypeRef();
|
||||
@@ -58,7 +60,7 @@ public class JetTypeChecker {
|
||||
thisType = scope.getThisType();
|
||||
}
|
||||
|
||||
List<JetExpression> body = expression.getBody();
|
||||
List<JetElement> body = expression.getBody();
|
||||
final Map<String, PropertyDescriptor> parameterDescriptors = new HashMap<String, PropertyDescriptor>();
|
||||
List<Type> parameterTypes = new ArrayList<Type>();
|
||||
for (JetParameter parameter : expression.getParameters()) {
|
||||
@@ -73,10 +75,8 @@ public class JetTypeChecker {
|
||||
Type returnType;
|
||||
if (returnTypeRef != null) {
|
||||
returnType = TypeResolver.INSTANCE.resolveType(scope, returnTypeRef);
|
||||
} else if (body.isEmpty()) {
|
||||
returnType = JetStandardClasses.getUnitType();
|
||||
} else {
|
||||
returnType = getType(new JetScopeAdapter(scope) {
|
||||
returnType = getBlockReturnedType(new JetScopeAdapter(scope) {
|
||||
@Override
|
||||
public Type getThisType() {
|
||||
return thisType;
|
||||
@@ -90,14 +90,14 @@ public class JetTypeChecker {
|
||||
}
|
||||
return propertyDescriptor;
|
||||
}
|
||||
}, body.get(body.size() - 1));
|
||||
}, body);
|
||||
}
|
||||
result[0] = JetStandardClasses.getFunctionType(null, receiverTypeRef == null ? null : thisType, parameterTypes, returnType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitParenthesizedExpression(JetParenthesizedExpression expression) {
|
||||
result[0] = getType(scope, expression.getExpression());
|
||||
result[0] = getType(scope, expression.getExpression(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -157,7 +157,7 @@ public class JetTypeChecker {
|
||||
@Override
|
||||
public void visitBinaryWithTypeRHSExpression(JetBinaryExpressionWithTypeRHS expression) {
|
||||
if (expression.getOperationSign() == JetTokens.COLON) {
|
||||
Type actualType = getType(scope, expression.getLeft());
|
||||
Type actualType = getType(scope, expression.getLeft(), false);
|
||||
Type expectedType = TypeResolver.INSTANCE.resolveType(scope, expression.getRight());
|
||||
if (isSubtypeOf(actualType, expectedType)) {
|
||||
result[0] = expectedType;
|
||||
@@ -179,8 +179,8 @@ public class JetTypeChecker {
|
||||
// TODO : type-check the branch
|
||||
result[0] = JetStandardClasses.getUnitType();
|
||||
} else {
|
||||
Type thenType = getType(scope, expression.getThen());
|
||||
Type elseType = getType(scope, elseBranch);
|
||||
Type thenType = getType(scope, expression.getThen(), true);
|
||||
Type elseType = getType(scope, elseBranch, true);
|
||||
result[0] = commonSupertype(Arrays.asList(thenType, elseType));
|
||||
}
|
||||
}
|
||||
@@ -202,12 +202,12 @@ public class JetTypeChecker {
|
||||
if (finallyBlock == null) {
|
||||
for (JetCatchClause catchClause : catchClauses) {
|
||||
// TODO: change scope here
|
||||
types.add(getType(scope, catchClause.getCatchBody()));
|
||||
types.add(getType(scope, catchClause.getCatchBody(), true));
|
||||
}
|
||||
} else {
|
||||
types.add(getType(scope, finallyBlock.getFinalExpression()));
|
||||
types.add(getType(scope, finallyBlock.getFinalExpression(), true));
|
||||
}
|
||||
types.add(getType(scope, tryBlock));
|
||||
types.add(getType(scope, tryBlock, true));
|
||||
result[0] = commonSupertype(types);
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ public class JetTypeChecker {
|
||||
List<JetExpression> entries = expression.getEntries();
|
||||
List<Type> types = new ArrayList<Type>();
|
||||
for (JetExpression entry : entries) {
|
||||
types.add(getType(scope, entry));
|
||||
types.add(getType(scope, entry, false));
|
||||
}
|
||||
// TODO : labels
|
||||
result[0] = JetStandardClasses.getTupleType(types);
|
||||
@@ -247,12 +247,7 @@ public class JetTypeChecker {
|
||||
@Override
|
||||
public void visitBlockExpression(JetBlockExpression expression) {
|
||||
// TODO : this is a stub, consider function literals
|
||||
List<JetExpression> statements = expression.getStatements();
|
||||
if (statements.isEmpty()) {
|
||||
result[0] = JetStandardClasses.getUnitType();
|
||||
} else {
|
||||
result[0] = getType(scope, statements.get(statements.size() - 1));
|
||||
}
|
||||
result[0] = getBlockReturnedType(scope, expression.getStatements());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -268,6 +263,29 @@ public class JetTypeChecker {
|
||||
return result[0];
|
||||
}
|
||||
|
||||
private Type getBlockReturnedType(@NotNull JetScope outerScope, List<JetElement> block) {
|
||||
// TODO : this is a stub, consider function literals
|
||||
if (block.isEmpty()) {
|
||||
return JetStandardClasses.getUnitType();
|
||||
} else {
|
||||
AccumulatingScope scope = new AccumulatingScope(outerScope);
|
||||
for (JetElement statement : block) {
|
||||
// TODO: consider other declarations
|
||||
if (statement instanceof JetProperty) {
|
||||
JetProperty property = (JetProperty) statement;
|
||||
scope.addPropertyDescriptor(ClassDescriptorResolver.resolvePropertyDescriptor(scope, property));
|
||||
}
|
||||
}
|
||||
JetElement lastElement = block.get(block.size() - 1);
|
||||
if (lastElement instanceof JetExpression) {
|
||||
JetExpression expression = (JetExpression) lastElement;
|
||||
return getType(scope, expression, true);
|
||||
}
|
||||
// TODO: functions, classes, etc.
|
||||
throw new IllegalArgumentException("Last item in the block must be an expression");
|
||||
}
|
||||
}
|
||||
|
||||
private void collectAllReturnTypes(JetWhenExpression whenExpression, JetScope scope, List<Type> result) {
|
||||
for (JetWhenEntry entry : whenExpression.getEntries()) {
|
||||
JetWhenExpression subWhen = entry.getSubWhen();
|
||||
@@ -276,7 +294,7 @@ public class JetTypeChecker {
|
||||
} else {
|
||||
JetExpression resultExpression = entry.getExpression();
|
||||
if (resultExpression != null) {
|
||||
result.add(getType(scope, resultExpression));
|
||||
result.add(getType(scope, resultExpression, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.jetbrains.jet.types;
|
||||
import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.*;
|
||||
import org.jetbrains.jet.lang.types.*;
|
||||
@@ -296,6 +297,13 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
|
||||
assertType("{Any.(a : Int, b : String) => b}", "{Any.(Int, String) : String}");
|
||||
}
|
||||
|
||||
public void testBlocks() throws Exception {
|
||||
assertType("if (1) {val a = 1; a} else {null}", "Int?");
|
||||
assertType("if (1) {() => val a = 1; a} else {() => null}", "Function0<Int?>");
|
||||
assertType("if (1) {() => val a = 1; a; var b : Boolean; b = true; b} else null", "Function0<Boolean>?");
|
||||
assertType("if (1) {() => val a = 1; a; var b = a; b} else null", "Function0<Int>?");
|
||||
}
|
||||
|
||||
public void testImplicitConversions() throws Exception {
|
||||
assertConvertibleTo("1", JetStandardClasses.getByteType());
|
||||
}
|
||||
@@ -344,13 +352,14 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
|
||||
private static void assertType(String expression, Type expectedType) {
|
||||
Project project = getProject();
|
||||
JetExpression jetExpression = JetChangeUtil.createExpression(project, expression);
|
||||
Type type = JetTypeChecker.INSTANCE.getType(ClassDefinitions.BASIC_SCOPE, jetExpression);
|
||||
Type type = JetTypeChecker.INSTANCE.getType(ClassDefinitions.BASIC_SCOPE, jetExpression, false);
|
||||
assertTrue(type + " != " + expectedType, JetTypeChecker.INSTANCE.equalTypes(type, expectedType));
|
||||
}
|
||||
|
||||
private void assertType(String contextType, String expression, String expectedType) {
|
||||
private static void assertType(String contextType, String expression, String expectedType) {
|
||||
final Type thisType = makeType(contextType);
|
||||
JetScope scope = new JetScopeAdapter(ClassDefinitions.BASIC_SCOPE) {
|
||||
@NotNull
|
||||
@Override
|
||||
public Type getThisType() {
|
||||
return thisType;
|
||||
@@ -366,7 +375,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
|
||||
private static void assertType(JetScope scope, String expression, String expectedTypeStr) {
|
||||
Project project = getProject();
|
||||
JetExpression jetExpression = JetChangeUtil.createExpression(project, expression);
|
||||
Type type = JetTypeChecker.INSTANCE.getType(scope, jetExpression);
|
||||
Type type = JetTypeChecker.INSTANCE.getType(scope, jetExpression, false);
|
||||
Type expectedType = makeType(expectedTypeStr);
|
||||
assertTrue(type + " != " + expectedType, JetTypeChecker.INSTANCE.equalTypes(type, expectedType));
|
||||
}
|
||||
@@ -392,32 +401,9 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
|
||||
"open class Derived_outT<out T> : Base_outT<T>",
|
||||
};
|
||||
|
||||
public static JetScope BASIC_SCOPE = new JetScopeImpl() {
|
||||
public static JetScope BASIC_SCOPE = new JetScopeAdapter(JetStandardClasses.STANDARD_CLASSES) {
|
||||
@Override
|
||||
public ClassDescriptor getClass(String name) {
|
||||
if ("Int".equals(name)) {
|
||||
return JetStandardClasses.getInt();
|
||||
} else if ("Boolean".equals(name)) {
|
||||
return JetStandardClasses.getBoolean();
|
||||
} else if ("Byte".equals(name)) {
|
||||
return JetStandardClasses.getByte();
|
||||
} else if ("Char".equals(name)) {
|
||||
return JetStandardClasses.getChar();
|
||||
} else if ("Short".equals(name)) {
|
||||
return JetStandardClasses.getShort();
|
||||
} else if ("Long".equals(name)) {
|
||||
return JetStandardClasses.getLong();
|
||||
} else if ("Float".equals(name)) {
|
||||
return JetStandardClasses.getFloat();
|
||||
} else if ("Double".equals(name)) {
|
||||
return JetStandardClasses.getDouble();
|
||||
} else if ("Unit".equals(name)) {
|
||||
return JetStandardClasses.getTuple(0);
|
||||
} else if ("Any".equals(name)) {
|
||||
return JetStandardClasses.getAny();
|
||||
} else if ("Nothing".equals(name)) {
|
||||
return JetStandardClasses.getNothing();
|
||||
}
|
||||
if (CLASSES.isEmpty()) {
|
||||
for (String classDeclaration : CLASS_DECLARATIONS) {
|
||||
JetClass classElement = JetChangeUtil.createClass(getProject(), classDeclaration);
|
||||
@@ -429,7 +415,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
|
||||
if (classDescriptor != null) {
|
||||
return classDescriptor;
|
||||
}
|
||||
return null;
|
||||
return super.getClass(name);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user