Basic user type test passes
This commit is contained in:
@@ -123,7 +123,7 @@ public interface JetNodeTypes {
|
||||
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);
|
||||
JetNodeType TYPE_PROJECTION = new JetNodeType("TYPE_PROJECTION");
|
||||
JetNodeType TYPE_PROJECTION = new JetNodeType("TYPE_PROJECTION", JetTypeProjection.class);
|
||||
|
||||
JetNodeType NAMESPACE_NAME = new JetNodeType("NAMESPACE_NAME", JetContainerNode.class);
|
||||
}
|
||||
|
||||
@@ -45,8 +45,8 @@ public class JetCallExpression extends JetExpression {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<JetTypeReference> getTypeArguments() {
|
||||
public List<JetTypeProjection> getTypeArguments() {
|
||||
JetTypeArgumentList list = getTypeArgumentList();
|
||||
return list != null ? list.getArguments() : Collections.<JetTypeReference>emptyList();
|
||||
return list != null ? list.getArguments() : Collections.<JetTypeProjection>emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@ public class JetChangeUtil {
|
||||
return property.getPropertyTypeRef();
|
||||
}
|
||||
|
||||
public static JetClass createClass(Project project, String text) {
|
||||
return createDeclaration(project, text, JetClass.class);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JetFile createFile(Project project, String text) {
|
||||
return (JetFile) PsiFileFactory.getInstance(project).createFileFromText("dummy.jet", JetFileType.INSTANCE, text, LocalTimeCounter.currentTime(), true);
|
||||
@@ -34,11 +38,16 @@ public class JetChangeUtil {
|
||||
}
|
||||
|
||||
private static JetProperty createProperty(Project project, String text) {
|
||||
return createDeclaration(project, text, JetProperty.class);
|
||||
}
|
||||
|
||||
private static <T> T createDeclaration(Project project, String text, Class<T> clazz) {
|
||||
JetFile file = createFile(project, text);
|
||||
JetNamespace rootNamespace = file.getRootNamespace();
|
||||
List<JetDeclaration> dcls = rootNamespace.getDeclarations();
|
||||
assert dcls.size() == 1;
|
||||
return (JetProperty) dcls.get(0);
|
||||
//noinspection unchecked
|
||||
return (T) dcls.get(0);
|
||||
}
|
||||
|
||||
public static PsiElement createNameIdentifier(Project project, String name) {
|
||||
|
||||
@@ -19,7 +19,7 @@ public class JetTypeArgumentList extends JetElement {
|
||||
visitor.visitTypeArgumentList(this);
|
||||
}
|
||||
|
||||
public List<JetTypeReference> getArguments() {
|
||||
return findChildrenByType(JetNodeTypes.TYPE_REFERENCE);
|
||||
public List<JetTypeProjection> getArguments() {
|
||||
return findChildrenByType(JetNodeTypes.TYPE_PROJECTION);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
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.lang.types.ProjectionKind;
|
||||
import org.jetbrains.jet.lexer.JetTokens;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public class JetTypeProjection extends JetDeclaration {
|
||||
public JetTypeProjection(@NotNull ASTNode node) {
|
||||
super(node);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ProjectionKind getProjectionKind() {
|
||||
JetModifierList modifierList = getModifierList();
|
||||
if (modifierList != null) {
|
||||
if (modifierList.hasModifier(JetTokens.IN_KEYWORD)) {
|
||||
return ProjectionKind.IN_ONLY;
|
||||
}
|
||||
if (modifierList.hasModifier(JetTokens.OUT_KEYWORD)) {
|
||||
return ProjectionKind.OUT_ONLY;
|
||||
}
|
||||
}
|
||||
if (findChildByType(JetTokens.MUL) != null) {
|
||||
return ProjectionKind.NEITHER_OUT_NOR_IN;
|
||||
}
|
||||
|
||||
return ProjectionKind.NO_PROJECTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(@NotNull JetVisitor visitor) {
|
||||
visitor.visitTypeProjection(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JetTypeReference getTypeReference() {
|
||||
return (JetTypeReference) findChildByType(JetNodeTypes.TYPE_REFERENCE);
|
||||
}
|
||||
}
|
||||
@@ -29,16 +29,6 @@ public class JetTypeReference extends JetElement {
|
||||
return findChildByClass(JetTypeElement.class);
|
||||
}
|
||||
|
||||
public JetTypeArgumentList getTypeArgumentList() {
|
||||
return findChildByClass(JetTypeArgumentList.class);
|
||||
}
|
||||
|
||||
public List<JetTypeReference> getTypeArguments() {
|
||||
// TODO: empty elements in PSI
|
||||
JetTypeArgumentList typeArgumentList = getTypeArgumentList();
|
||||
return typeArgumentList == null ? Collections.<JetTypeReference>emptyList() : typeArgumentList.getArguments();
|
||||
}
|
||||
|
||||
public List<JetAttribute> getAttributes() {
|
||||
List<JetAttribute> answer = null;
|
||||
for (JetAttributeAnnotation annotation : getAttributeAnnotations()) {
|
||||
|
||||
@@ -4,6 +4,9 @@ import com.intellij.lang.ASTNode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.JetNodeTypes;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
*/
|
||||
@@ -17,6 +20,16 @@ public class JetUserType extends JetTypeElement {
|
||||
visitor.visitUserType(this);
|
||||
}
|
||||
|
||||
public JetTypeArgumentList getTypeArgumentList() {
|
||||
return (JetTypeArgumentList) findChildByType(JetNodeTypes.TYPE_ARGUMENT_LIST);
|
||||
}
|
||||
|
||||
public List<JetTypeProjection> getTypeArguments() {
|
||||
// TODO: empty elements in PSI
|
||||
JetTypeArgumentList typeArgumentList = getTypeArgumentList();
|
||||
return typeArgumentList == null ? Collections.<JetTypeProjection>emptyList() : typeArgumentList.getArguments();
|
||||
}
|
||||
|
||||
public JetReferenceExpression getReferenceExpression() {
|
||||
return (JetReferenceExpression) findChildByType(JetNodeTypes.REFERENCE_EXPRESSION);
|
||||
}
|
||||
|
||||
@@ -325,4 +325,8 @@ public class JetVisitor extends PsiElementVisitor {
|
||||
public void visitNullableType(JetNullableType nullableType) {
|
||||
visitTypeElement(nullableType);
|
||||
}
|
||||
|
||||
public void visitTypeProjection(JetTypeProjection typeProjection) {
|
||||
visitJetElement(typeProjection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ public class ClassType extends TypeImpl {
|
||||
|
||||
public ClassType(List<Annotation> annotations, ClassDescriptor classDescriptor, List<TypeProjection> arguments) {
|
||||
super(annotations, classDescriptor.getTypeConstructor(), arguments);
|
||||
assert classDescriptor.getTypeConstructor().getParameters().size() == arguments.size();
|
||||
assert classDescriptor.getTypeConstructor().getParameters().size() == arguments.size() : classDescriptor.getTypeConstructor().getParameters().size() + " != " + arguments.size();
|
||||
|
||||
this.classDescriptor = classDescriptor;
|
||||
}
|
||||
|
||||
@@ -37,8 +37,7 @@ public class JetStandardClasses {
|
||||
for (int j = 0; j < i; j++) {
|
||||
parameters.add(new TypeParameterDescriptor(
|
||||
Collections.<Annotation>emptyList(),
|
||||
"T" + j,
|
||||
Variance.OUT_VARIANCE,
|
||||
Variance.OUT_VARIANCE, "T" + j,
|
||||
Collections.<Type>emptySet()));
|
||||
}
|
||||
TUPLE[i] = new ClassDescriptor(
|
||||
|
||||
@@ -138,7 +138,7 @@ public class JetTypeChecker {
|
||||
case INVARIANT:
|
||||
switch (superArgument.getProjectionKind()) {
|
||||
case NO_PROJECTION:
|
||||
if (!subArgumentType.equals(superArgumentType)) {
|
||||
if (!equalTypes(subArgumentType, superArgumentType)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
@@ -199,4 +199,26 @@ public class JetTypeChecker {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean equalTypes(Type type1, Type type2) {
|
||||
if (!type1.getConstructor().equals(type2.getConstructor())) {
|
||||
return false;
|
||||
}
|
||||
List<TypeProjection> type1Arguments = type1.getArguments();
|
||||
List<TypeProjection> type2Arguments = type2.getArguments();
|
||||
if (type1Arguments.size() != type2Arguments.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < type1Arguments.size(); i++) {
|
||||
TypeProjection typeProjection1 = type1Arguments.get(i);
|
||||
TypeProjection typeProjection2 = type2Arguments.get(i);
|
||||
if (typeProjection1.getProjectionKind() != typeProjection2.getProjectionKind()) {
|
||||
return false;
|
||||
}
|
||||
if (!equalTypes(typeProjection1.getType(), typeProjection2.getType())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,7 +12,7 @@ public class TypeParameterDescriptor extends NamedAnnotatedImpl {
|
||||
private final Collection<Type> upperBounds;
|
||||
private final TypeConstructor typeConstructor;
|
||||
|
||||
public TypeParameterDescriptor(List<Annotation> annotations, String name, Variance variance, Collection<Type> upperBounds) {
|
||||
public TypeParameterDescriptor(List<Annotation> annotations, Variance variance, String name, Collection<Type> upperBounds) {
|
||||
super(annotations, name);
|
||||
this.variance = variance;
|
||||
this.upperBounds = upperBounds;
|
||||
|
||||
@@ -10,14 +10,32 @@ import org.jetbrains.jet.lang.types.*;
|
||||
import org.jetbrains.jet.parsing.JetParsingTest;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
|
||||
|
||||
public static final ClassDescriptor BASE_T = new ClassDescriptor(
|
||||
Collections.<Annotation>emptyList(),
|
||||
"Base_T",
|
||||
Arrays.asList(
|
||||
new TypeParameterDescriptor(
|
||||
Collections.<Annotation>emptyList(),
|
||||
Variance.INVARIANT, "T",
|
||||
Collections.<Type>emptyList())
|
||||
),
|
||||
Collections.singleton(JetStandardClasses.getAnyType())
|
||||
);
|
||||
|
||||
private static Map<String, ClassDescriptor> CLASSES = new HashMap<String, ClassDescriptor>();
|
||||
private static String[] CLASS_DECLARATIONS = {
|
||||
"class Base_T<T>",
|
||||
"class Base_inT<in T>",
|
||||
"class Base_outT<out T>",
|
||||
};
|
||||
|
||||
private static final JetScope BASIC_SCOPE = new JetScope.JetScopeImpl() {
|
||||
@Override
|
||||
public ClassDescriptor getClass(String name) {
|
||||
@@ -42,11 +60,23 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
|
||||
} else if ("Any".equals(name)) {
|
||||
return JetStandardClasses.getAny();
|
||||
}
|
||||
if (CLASSES.isEmpty()) {
|
||||
for (String classDeclaration : CLASS_DECLARATIONS) {
|
||||
JetClass classElement = JetChangeUtil.createClass(getProject(), classDeclaration);
|
||||
ClassDescriptor classDescriptor = toClassDescriptor(classElement);
|
||||
CLASSES.put(classDescriptor.getName(), classDescriptor);
|
||||
}
|
||||
}
|
||||
ClassDescriptor classDescriptor = CLASSES.get(name);
|
||||
if (classDescriptor != null) {
|
||||
return classDescriptor;
|
||||
}
|
||||
fail("Type not found: " + name);
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return getHomeDirectory() + "/idea/testData";
|
||||
@@ -86,7 +116,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
|
||||
assertType("()", JetStandardTypes.getUnit());
|
||||
}
|
||||
|
||||
public void testSubtyping() throws Exception {
|
||||
public void testBasicSubtyping() throws Exception {
|
||||
assertSubtype("Boolean", "Boolean");
|
||||
assertSubtype("Byte", "Byte");
|
||||
assertSubtype("Char", "Char");
|
||||
@@ -149,18 +179,59 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
|
||||
assertSubtype("(Unit, Unit)", "(Any, Any)");
|
||||
assertSubtype("(Unit, Unit)", "(Any, Unit)");
|
||||
assertSubtype("(Unit, Unit)", "(Unit, Any)");
|
||||
|
||||
}
|
||||
|
||||
private void assertSubtype(String type1, String type2) {
|
||||
public void testProjections() throws Exception {
|
||||
assertSubtype("Base_T<Int>", "Base_T<Int>");
|
||||
}
|
||||
|
||||
public void testImplicitConversions() throws Exception {
|
||||
assertConvertibleTo("1", JetStandardTypes.getByte());
|
||||
}
|
||||
|
||||
private static ClassDescriptor toClassDescriptor(JetClass classElement) {
|
||||
return new ClassDescriptor(
|
||||
toAttributes(classElement.getModifierList()),
|
||||
classElement.getName(),
|
||||
toTypeParameters(classElement.getTypeParameters()),
|
||||
toTypes(classElement.getDelegationSpecifiers())
|
||||
);
|
||||
}
|
||||
|
||||
private static List<TypeParameterDescriptor> toTypeParameters(List<JetTypeParameter> typeParameters) {
|
||||
List<TypeParameterDescriptor> result = new ArrayList<TypeParameterDescriptor>();
|
||||
for (JetTypeParameter typeParameter : typeParameters) {
|
||||
result.add(toTypeParameter(typeParameter));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static TypeParameterDescriptor toTypeParameter(JetTypeParameter typeParameter) {
|
||||
JetTypeReference extendsBound = typeParameter.getExtendsBound();
|
||||
return new TypeParameterDescriptor(
|
||||
toAttributes(typeParameter.getModifierList()),
|
||||
typeParameter.getVariance(),
|
||||
typeParameter.getName(),
|
||||
extendsBound == null ? Collections.<Type>singleton(JetStandardClasses.getAnyType()) : Collections.singleton(toType(extendsBound))
|
||||
);
|
||||
}
|
||||
|
||||
private static Collection<? extends Type> toTypes(List<JetDelegationSpecifier> delegationSpecifiers) {
|
||||
if (delegationSpecifiers.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
throw new UnsupportedOperationException(); // TODO
|
||||
}
|
||||
|
||||
private static void assertSubtype(String type1, String type2) {
|
||||
assertSubtypingRelation(type1, type2, true);
|
||||
}
|
||||
|
||||
private void assertNotSubtype(String type1, String type2) {
|
||||
private static void assertNotSubtype(String type1, String type2) {
|
||||
assertSubtypingRelation(type1, type2, false);
|
||||
}
|
||||
|
||||
private void assertSubtypingRelation(String type1, String type2, boolean expected) {
|
||||
private static void assertSubtypingRelation(String type1, String type2, boolean expected) {
|
||||
Type typeNode1 = toType(JetChangeUtil.createType(getProject(), type1));
|
||||
Type typeNode2 = toType(JetChangeUtil.createType(getProject(), type2));
|
||||
boolean result = new JetTypeChecker().isSubtypeOf(
|
||||
@@ -170,19 +241,20 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
|
||||
assertTrue(typeNode1 + " is " + modifier + "a subtype of " + typeNode2, result == expected);
|
||||
}
|
||||
|
||||
private Type toType(JetTypeReference typeNode) {
|
||||
List<JetAttribute> attributes = typeNode.getAttributes();
|
||||
private static Type toType(JetTypeReference typeNode) {
|
||||
List<JetAttribute> attributeElements = typeNode.getAttributes();
|
||||
final List<Annotation> attributes = toAttributes(attributeElements);
|
||||
JetTypeElement typeElement = typeNode.getTypeElement();
|
||||
List<JetTypeReference> argumentElements = typeNode.getTypeArguments();
|
||||
|
||||
final List<Type> arguments = toTypes(argumentElements);
|
||||
|
||||
// TODO annotations
|
||||
final Type[] result = new Type[1];
|
||||
typeElement.accept(new JetVisitor() {
|
||||
@Override
|
||||
public void visitUserType(JetUserType type) {
|
||||
result[0] = new ClassType(TypeResolver.INSTANCE.resolveClass(BASIC_SCOPE, type));
|
||||
List<JetTypeProjection> argumentElements = type.getTypeArguments();
|
||||
|
||||
final List<TypeProjection> arguments = toTypeProjections(argumentElements);
|
||||
result[0] = new ClassType(attributes, TypeResolver.INSTANCE.resolveClass(BASIC_SCOPE, type), arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -200,7 +272,19 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
|
||||
return result[0];
|
||||
}
|
||||
|
||||
private List<Type> toTypes(List<JetTypeReference> argumentElements) {
|
||||
private static List<Annotation> toAttributes(List<JetAttribute> attributeElements) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static List<Annotation> toAttributes(JetModifierList modifierList) {
|
||||
if (modifierList == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// TODO:
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static List<Type> toTypes(List<JetTypeReference> argumentElements) {
|
||||
final List<Type> arguments = new ArrayList<Type>();
|
||||
for (JetTypeReference argumentElement : argumentElements) {
|
||||
arguments.add(toType(argumentElement));
|
||||
@@ -208,8 +292,14 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
public void testImplicitConversions() throws Exception {
|
||||
assertConvertibleTo("1", JetStandardTypes.getByte());
|
||||
private static List<TypeProjection> toTypeProjections(List<JetTypeProjection> argumentElements) {
|
||||
final List<TypeProjection> arguments = new ArrayList<TypeProjection>();
|
||||
for (JetTypeProjection argumentElement : argumentElements) {
|
||||
Type type = toType(argumentElement.getTypeReference());
|
||||
TypeProjection typeProjection = new TypeProjection(argumentElement.getProjectionKind(), type);
|
||||
arguments.add(typeProjection);
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
|
||||
private static void assertConvertibleTo(String expression, Type type) {
|
||||
|
||||
Reference in New Issue
Block a user