From 1a6f7f5f7951b51279f6d7b1ad636c17a7fdcd6e Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 26 Jan 2011 17:48:02 +0300 Subject: [PATCH] Basic user type test passes --- idea/src/org/jetbrains/jet/JetNodeTypes.java | 2 +- .../jet/lang/psi/JetCallExpression.java | 4 +- .../jetbrains/jet/lang/psi/JetChangeUtil.java | 11 +- .../jet/lang/psi/JetTypeArgumentList.java | 4 +- .../jet/lang/psi/JetTypeProjection.java | 45 +++++++ .../jet/lang/psi/JetTypeReference.java | 10 -- .../jetbrains/jet/lang/psi/JetUserType.java | 13 ++ .../jetbrains/jet/lang/psi/JetVisitor.java | 4 + .../jetbrains/jet/lang/types/ClassType.java | 2 +- .../jet/lang/types/JetStandardClasses.java | 3 +- .../jet/lang/types/JetTypeChecker.java | 24 +++- .../lang/types/TypeParameterDescriptor.java | 2 +- .../jet/types/JetTypeCheckerTest.java | 122 +++++++++++++++--- 13 files changed, 209 insertions(+), 37 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/lang/psi/JetTypeProjection.java diff --git a/idea/src/org/jetbrains/jet/JetNodeTypes.java b/idea/src/org/jetbrains/jet/JetNodeTypes.java index 506aa7bc745..fe859340c02 100644 --- a/idea/src/org/jetbrains/jet/JetNodeTypes.java +++ b/idea/src/org/jetbrains/jet/JetNodeTypes.java @@ -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); } diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetCallExpression.java b/idea/src/org/jetbrains/jet/lang/psi/JetCallExpression.java index 7cc8e3f80c0..62f2a90c5aa 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetCallExpression.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetCallExpression.java @@ -45,8 +45,8 @@ public class JetCallExpression extends JetExpression { } @NotNull - public List getTypeArguments() { + public List getTypeArguments() { JetTypeArgumentList list = getTypeArgumentList(); - return list != null ? list.getArguments() : Collections.emptyList(); + return list != null ? list.getArguments() : Collections.emptyList(); } } diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetChangeUtil.java b/idea/src/org/jetbrains/jet/lang/psi/JetChangeUtil.java index 3b60e625fdc..ca854caafd5 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetChangeUtil.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetChangeUtil.java @@ -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 createDeclaration(Project project, String text, Class clazz) { JetFile file = createFile(project, text); JetNamespace rootNamespace = file.getRootNamespace(); List 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) { diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetTypeArgumentList.java b/idea/src/org/jetbrains/jet/lang/psi/JetTypeArgumentList.java index 677b0485c6a..29b76a938e5 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetTypeArgumentList.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetTypeArgumentList.java @@ -19,7 +19,7 @@ public class JetTypeArgumentList extends JetElement { visitor.visitTypeArgumentList(this); } - public List getArguments() { - return findChildrenByType(JetNodeTypes.TYPE_REFERENCE); + public List getArguments() { + return findChildrenByType(JetNodeTypes.TYPE_PROJECTION); } } diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetTypeProjection.java b/idea/src/org/jetbrains/jet/lang/psi/JetTypeProjection.java new file mode 100644 index 00000000000..92c63b9f4fd --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/psi/JetTypeProjection.java @@ -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); + } +} diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetTypeReference.java b/idea/src/org/jetbrains/jet/lang/psi/JetTypeReference.java index 8bada79ef17..241a7ed70e8 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetTypeReference.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetTypeReference.java @@ -29,16 +29,6 @@ public class JetTypeReference extends JetElement { return findChildByClass(JetTypeElement.class); } - public JetTypeArgumentList getTypeArgumentList() { - return findChildByClass(JetTypeArgumentList.class); - } - - public List getTypeArguments() { - // TODO: empty elements in PSI - JetTypeArgumentList typeArgumentList = getTypeArgumentList(); - return typeArgumentList == null ? Collections.emptyList() : typeArgumentList.getArguments(); - } - public List getAttributes() { List answer = null; for (JetAttributeAnnotation annotation : getAttributeAnnotations()) { diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetUserType.java b/idea/src/org/jetbrains/jet/lang/psi/JetUserType.java index dac53a8f6c8..f0c2da815eb 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetUserType.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetUserType.java @@ -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 getTypeArguments() { + // TODO: empty elements in PSI + JetTypeArgumentList typeArgumentList = getTypeArgumentList(); + return typeArgumentList == null ? Collections.emptyList() : typeArgumentList.getArguments(); + } + public JetReferenceExpression getReferenceExpression() { return (JetReferenceExpression) findChildByType(JetNodeTypes.REFERENCE_EXPRESSION); } diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetVisitor.java b/idea/src/org/jetbrains/jet/lang/psi/JetVisitor.java index 2f0c27e1dd6..0e7197b1319 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetVisitor.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetVisitor.java @@ -325,4 +325,8 @@ public class JetVisitor extends PsiElementVisitor { public void visitNullableType(JetNullableType nullableType) { visitTypeElement(nullableType); } + + public void visitTypeProjection(JetTypeProjection typeProjection) { + visitJetElement(typeProjection); + } } diff --git a/idea/src/org/jetbrains/jet/lang/types/ClassType.java b/idea/src/org/jetbrains/jet/lang/types/ClassType.java index 6b04d349aa0..0974dd88c93 100644 --- a/idea/src/org/jetbrains/jet/lang/types/ClassType.java +++ b/idea/src/org/jetbrains/jet/lang/types/ClassType.java @@ -12,7 +12,7 @@ public class ClassType extends TypeImpl { public ClassType(List annotations, ClassDescriptor classDescriptor, List 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; } diff --git a/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java b/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java index 98509e3a723..fbbe1782661 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java @@ -37,8 +37,7 @@ public class JetStandardClasses { for (int j = 0; j < i; j++) { parameters.add(new TypeParameterDescriptor( Collections.emptyList(), - "T" + j, - Variance.OUT_VARIANCE, + Variance.OUT_VARIANCE, "T" + j, Collections.emptySet())); } TUPLE[i] = new ClassDescriptor( diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java index c4c6927a41f..eb7f92b8326 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java @@ -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 type1Arguments = type1.getArguments(); + List 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; + } + } \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/lang/types/TypeParameterDescriptor.java b/idea/src/org/jetbrains/jet/lang/types/TypeParameterDescriptor.java index a4e4ee32d38..0cf4ee22e4e 100644 --- a/idea/src/org/jetbrains/jet/lang/types/TypeParameterDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/types/TypeParameterDescriptor.java @@ -12,7 +12,7 @@ public class TypeParameterDescriptor extends NamedAnnotatedImpl { private final Collection upperBounds; private final TypeConstructor typeConstructor; - public TypeParameterDescriptor(List annotations, String name, Variance variance, Collection upperBounds) { + public TypeParameterDescriptor(List annotations, Variance variance, String name, Collection upperBounds) { super(annotations, name); this.variance = variance; this.upperBounds = upperBounds; diff --git a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index f7f3486411c..beeb3550993 100644 --- a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -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.emptyList(), + "Base_T", + Arrays.asList( + new TypeParameterDescriptor( + Collections.emptyList(), + Variance.INVARIANT, "T", + Collections.emptyList()) + ), + Collections.singleton(JetStandardClasses.getAnyType()) + ); + + private static Map CLASSES = new HashMap(); + private static String[] CLASS_DECLARATIONS = { + "class Base_T", + "class Base_inT", + "class Base_outT", + }; + 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", "Base_T"); + } + + 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 toTypeParameters(List typeParameters) { + List result = new ArrayList(); + 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.singleton(JetStandardClasses.getAnyType()) : Collections.singleton(toType(extendsBound)) + ); + } + + private static Collection toTypes(List 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 attributes = typeNode.getAttributes(); + private static Type toType(JetTypeReference typeNode) { + List attributeElements = typeNode.getAttributes(); + final List attributes = toAttributes(attributeElements); JetTypeElement typeElement = typeNode.getTypeElement(); - List argumentElements = typeNode.getTypeArguments(); - - final List 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 argumentElements = type.getTypeArguments(); + + final List 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 toTypes(List argumentElements) { + private static List toAttributes(List attributeElements) { + return Collections.emptyList(); + } + + private static List toAttributes(JetModifierList modifierList) { + if (modifierList == null) { + return Collections.emptyList(); + } + // TODO: + return Collections.emptyList(); + } + + private static List toTypes(List argumentElements) { final List arguments = new ArrayList(); 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 toTypeProjections(List argumentElements) { + final List arguments = new ArrayList(); + 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) {