JET-16 Check property initializers as parts of the primary constructor's body

This commit is contained in:
Andrey Breslav
2011-04-26 18:47:16 +04:00
parent 64a8da3296
commit 244fc31da8
9 changed files with 135 additions and 61 deletions
@@ -78,4 +78,11 @@ public class JetClass extends JetTypeParameterListOwner implements JetClassOrObj
public boolean hasPrimaryConstructor() { public boolean hasPrimaryConstructor() {
return getPrimaryConstructorParameterList() != null; return getPrimaryConstructorParameterList() != null;
} }
public List<JetProperty> getProperties() {
JetClassBody body = (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY);
if (body == null) return Collections.emptyList();
return body.getProperties();
}
} }
@@ -32,4 +32,9 @@ public class JetClassBody extends JetElement {
public List<JetClassInitializer> getAnonymousInitializers() { public List<JetClassInitializer> getAnonymousInitializers() {
return findChildrenByType(JetNodeTypes.ANONYMOUS_INITIALIZER); return findChildrenByType(JetNodeTypes.ANONYMOUS_INITIALIZER);
} }
@NotNull
public List<JetProperty> getProperties() {
return findChildrenByType(JetNodeTypes.PROPERTY);
}
} }
@@ -3,6 +3,7 @@ package org.jetbrains.jet.lang.resolve;
import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.JetSemanticServices; import org.jetbrains.jet.lang.JetSemanticServices;
@@ -273,6 +274,7 @@ public class TopDownAnalyzer {
private void processProperty(WritableScope declaringScope, JetProperty property) { private void processProperty(WritableScope declaringScope, JetProperty property) {
declaringScopes.put(property, declaringScope); declaringScopes.put(property, declaringScope);
// TODO : Do not infer the type from the initializer here: the scope is wrong, and not ready anyway
PropertyDescriptor descriptor = classDescriptorResolver.resolvePropertyDescriptor(declaringScope.getContainingDeclaration(), declaringScope, property); PropertyDescriptor descriptor = classDescriptorResolver.resolvePropertyDescriptor(declaringScope.getContainingDeclaration(), declaringScope, property);
declaringScope.addVariableDescriptor(descriptor); declaringScope.addVariableDescriptor(descriptor);
declaringScopesToProperties.put(declaringScope.getContainingDeclaration(), descriptor); declaringScopesToProperties.put(declaringScope.getContainingDeclaration(), descriptor);
@@ -289,12 +291,9 @@ public class TopDownAnalyzer {
resolveDelegationSpecifierLists(); resolveDelegationSpecifierLists();
resolveAnonymousInitializers(); resolveAnonymousInitializers();
resolvePropertyDeclarationBodies();
resolveSecondaryConstructorBodies(); resolveSecondaryConstructorBodies();
//TODO : anonymous initializers
resolvePropertyDeclarationBodies();
resolveFunctionBodies(); resolveFunctionBodies();
} }
@@ -481,69 +480,115 @@ public class TopDownAnalyzer {
} }
private void resolvePropertyDeclarationBodies() { private void resolvePropertyDeclarationBodies() {
for (Map.Entry<JetProperty, PropertyDescriptor> entry : properties.entrySet()) {
JetProperty declaration = entry.getKey();
final PropertyDescriptor propertyDescriptor = entry.getValue();
WritableScope declaringScope = declaringScopes.get(declaration);
JetExpression initializer = declaration.getInitializer(); // Member properties
if (initializer != null) { Set<JetProperty> processed = Sets.newHashSet();
JetFlowInformationProvider flowInformationProvider = computeFlowData(declaration, initializer); for (Map.Entry<JetClass, MutableClassDescriptor> entry : classes.entrySet()) {
JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(trace, flowInformationProvider); JetClass jetClass = entry.getKey();
JetType type = typeInferrer.getType(declaringScope, initializer, false); MutableClassDescriptor classDescriptor = entry.getValue();
JetType expectedType; for (JetProperty property : jetClass.getProperties()) {
PropertySetterDescriptor setter = propertyDescriptor.getSetter(); final PropertyDescriptor propertyDescriptor = properties.get(property);
if (setter != null) { assert propertyDescriptor != null;
expectedType = setter.getUnsubstitutedReturnType();
} WritableScope declaringScope = declaringScopes.get(property);
else {
expectedType = propertyDescriptor.getInType(); JetExpression initializer = property.getInitializer();
if (expectedType == null) { if (initializer != null) {
expectedType = propertyDescriptor.getOutType(); ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
if (primaryConstructor == null) {
semanticServices.getErrorHandler().genericError(initializer.getNode(), "Property initializers are not allowed when no primary constructor is present");
}
else {
JetScope scope = getInnerScopeForConstructor(primaryConstructor, classDescriptor.getWritableUnsubstitutedMemberScope());
resolvePropertyInitializer(property, propertyDescriptor, initializer, scope);
} }
} }
if (type != null && expectedType != null
&& !semanticServices.getTypeChecker().isConvertibleTo(type, expectedType)) { resolvePropertyAccessors(property, propertyDescriptor, declaringScope);
semanticServices.getErrorHandler().typeMismatch(initializer, expectedType, type); processed.add(property);
} }
}
// Top-level properties
for (Map.Entry<JetProperty, PropertyDescriptor> entry : properties.entrySet()) {
JetProperty property = entry.getKey();
if (processed.contains(property)) continue;
final PropertyDescriptor propertyDescriptor = entry.getValue();
WritableScope declaringScope = declaringScopes.get(property);
JetExpression initializer = property.getInitializer();
if (initializer != null) {
resolvePropertyInitializer(property, propertyDescriptor, initializer, declaringScope);
} }
BindingTraceAdapter fieldAccessTrackingTrace = new BindingTraceAdapter(trace) { resolvePropertyAccessors(property, propertyDescriptor, declaringScope);
@Override }
public void recordReferenceResolution(@NotNull JetReferenceExpression expression, @NotNull DeclarationDescriptor descriptor) { }
super.recordReferenceResolution(expression, descriptor);
if (expression instanceof JetSimpleNameExpression) { private void resolvePropertyAccessors(JetProperty property, PropertyDescriptor propertyDescriptor, WritableScope declaringScope) {
JetSimpleNameExpression simpleNameExpression = (JetSimpleNameExpression) expression; BindingTraceAdapter fieldAccessTrackingTrace = createFieldTrackingTrace(propertyDescriptor);
if (simpleNameExpression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER) {
// This check may be considered redundant as long as $x is only accessible from accessors to $x WritableScope accessorScope = semanticServices.createWritableScope(declaringScope, declaringScope.getContainingDeclaration());
if (descriptor == propertyDescriptor) { // TODO : original? accessorScope.addPropertyDescriptorByFieldName("$" + propertyDescriptor.getName(), propertyDescriptor);
recordFieldAccessFromAccessor(propertyDescriptor);
} JetPropertyAccessor getter = property.getGetter();
PropertyGetterDescriptor getterDescriptor = propertyDescriptor.getGetter();
if (getter != null && getterDescriptor != null) {
resolveFunctionBody(fieldAccessTrackingTrace, getter, getterDescriptor, accessorScope);
}
JetPropertyAccessor setter = property.getSetter();
PropertySetterDescriptor setterDescriptor = propertyDescriptor.getSetter();
if (setter != null && setterDescriptor != null) {
resolveFunctionBody(fieldAccessTrackingTrace, setter, setterDescriptor, accessorScope);
}
JetExpression initializer = property.getInitializer();
if (!property.isVar() && initializer != null && !trace.hasBackingField(propertyDescriptor)) {
semanticServices.getErrorHandler().genericError(initializer.getNode(), "Initializer is not allowed here because this property has no setter and no backing field either");
}
}
private BindingTraceAdapter createFieldTrackingTrace(final PropertyDescriptor propertyDescriptor) {
return new BindingTraceAdapter(trace) {
@Override
public void recordReferenceResolution(@NotNull JetReferenceExpression expression, @NotNull DeclarationDescriptor descriptor) {
super.recordReferenceResolution(expression, descriptor);
if (expression instanceof JetSimpleNameExpression) {
JetSimpleNameExpression simpleNameExpression = (JetSimpleNameExpression) expression;
if (simpleNameExpression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER) {
// This check may be considered redundant as long as $x is only accessible from accessors to $x
if (descriptor == propertyDescriptor) { // TODO : original?
recordFieldAccessFromAccessor(propertyDescriptor);
} }
} }
} }
};
WritableScope accessorScope = semanticServices.createWritableScope(declaringScope, declaringScope.getContainingDeclaration());
accessorScope.addPropertyDescriptorByFieldName("$" + propertyDescriptor.getName(), propertyDescriptor);
JetPropertyAccessor getter = declaration.getGetter();
PropertyGetterDescriptor getterDescriptor = propertyDescriptor.getGetter();
if (getter != null && getterDescriptor != null) {
resolveFunctionBody(fieldAccessTrackingTrace, getter, getterDescriptor, accessorScope);
} }
};
}
JetPropertyAccessor setter = declaration.getSetter(); private void resolvePropertyInitializer(JetProperty property, PropertyDescriptor propertyDescriptor, JetExpression initializer, JetScope scope) {
PropertySetterDescriptor setterDescriptor = propertyDescriptor.getSetter(); JetFlowInformationProvider flowInformationProvider = computeFlowData(property, initializer); // TODO : flow JET-15
if (setter != null && setterDescriptor != null) { JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(trace, flowInformationProvider);
resolveFunctionBody(fieldAccessTrackingTrace, setter, setterDescriptor, accessorScope); JetType type = typeInferrer.getType(scope, initializer, false);
}
if (!declaration.isVar() && initializer != null && !trace.hasBackingField(propertyDescriptor)) { JetType expectedType;
semanticServices.getErrorHandler().genericError(initializer.getNode(), "Initializer is not allowed here because this property has no setter and no backing field either"); PropertySetterDescriptor setter = propertyDescriptor.getSetter();
if (setter != null) {
expectedType = setter.getUnsubstitutedReturnType();
}
else {
expectedType = propertyDescriptor.getInType();
if (expectedType == null) {
expectedType = propertyDescriptor.getOutType();
} }
} }
if (type != null && expectedType != null
&& !semanticServices.getTypeChecker().isConvertibleTo(type, expectedType)) {
semanticServices.getErrorHandler().typeMismatch(initializer, expectedType, type);
}
} }
private void resolveFunctionBodies() { private void resolveFunctionBodies() {
+17
View File
@@ -32,3 +32,20 @@ class WithPC1(a : Int) {
class Foo() : <error>WithPC0</error>, <error>this</error>() { class Foo() : <error>WithPC0</error>, <error>this</error>() {
} }
class WithCPI_Dup(<error>x : Int</error>) {
<error>var x : Int</error>
}
class WithCPI(x : Int) {
val a = 1
val b : Int = $a
val xy : Int = x
}
class NoCPI {
val a = <error>1</error>
var ab = <error>1</error>
get() = 1
set(v) {}
}
+1 -1
View File
@@ -4,7 +4,7 @@ class Dup {
} }
} }
class A { class A() {
fun foo() : Unit { fun foo() : Unit {
this@A this@A
this<error>@a</error> this<error>@a</error>
@@ -1,4 +1,4 @@
class Test { class Test() {
<info>abstract</info> val x : Int <info>abstract</info> val x : Int
<info>abstract</info> val x1 : Int <info>get</info> <info>abstract</info> val x1 : Int <info>get</info>
<info>abstract</info> val x2 : Int <info>get</info>() = 1 <info>abstract</info> val x2 : Int <info>get</info>() = 1
+1 -1
View File
@@ -1,3 +1,3 @@
class PrivateVal { class PrivateVal() {
private val prop = 0; private val prop = 0;
} }
+1 -1
View File
@@ -1,4 +1,4 @@
class PrivateVar { class PrivateVar() {
private var x = 0; private var x = 0;
fun setValueOfX(aValue: Int) { x = aValue } fun setValueOfX(aValue: Int) { x = aValue }
@@ -30,7 +30,7 @@ public class PropertyGenTest extends CodegenTestCase {
} }
public void testPublicVar() throws Exception { public void testPublicVar() throws Exception {
loadText("class PublicVar { public var foo = 0; }"); loadText("class PublicVar() { public var foo = 0; }");
System.out.println(generateToText()); System.out.println(generateToText());
final Class aClass = loadImplementationClass(generateClassesInFile(), "PublicVar"); final Class aClass = loadImplementationClass(generateClassesInFile(), "PublicVar");
final Object instance = aClass.newInstance(); final Object instance = aClass.newInstance();
@@ -41,7 +41,7 @@ public class PropertyGenTest extends CodegenTestCase {
} }
public void testAccessorsInInterface() { public void testAccessorsInInterface() {
loadText("class AccessorsInInterface { public var foo = 0; }"); loadText("class AccessorsInInterface() { public var foo = 0; }");
final Class aClass = loadClass("AccessorsInInterface", generateClassesInFile()); final Class aClass = loadClass("AccessorsInInterface", generateClassesInFile());
assertNotNull(findMethodByName(aClass, "getFoo")); assertNotNull(findMethodByName(aClass, "getFoo"));
assertNotNull(findMethodByName(aClass, "setFoo")); assertNotNull(findMethodByName(aClass, "setFoo"));
@@ -82,7 +82,7 @@ public class PropertyGenTest extends CodegenTestCase {
} }
public void testAccessorsWithoutBody() throws Exception { public void testAccessorsWithoutBody() throws Exception {
loadText("class AccessorsWithoutBody { public var foo: Int = 349\n get\n private set\n fun setter() { foo = 610; } } "); loadText("class AccessorsWithoutBody() { public var foo: Int = 349\n get\n private set\n fun setter() { foo = 610; } } ");
final Class aClass = loadImplementationClass(generateClassesInFile(), "AccessorsWithoutBody"); final Class aClass = loadImplementationClass(generateClassesInFile(), "AccessorsWithoutBody");
final Object instance = aClass.newInstance(); final Object instance = aClass.newInstance();
final Method getFoo = findMethodByName(aClass, "getFoo"); final Method getFoo = findMethodByName(aClass, "getFoo");