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() {
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() {
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.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.JetSemanticServices;
@@ -273,6 +274,7 @@ public class TopDownAnalyzer {
private void processProperty(WritableScope declaringScope, JetProperty property) {
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);
declaringScope.addVariableDescriptor(descriptor);
declaringScopesToProperties.put(declaringScope.getContainingDeclaration(), descriptor);
@@ -289,12 +291,9 @@ public class TopDownAnalyzer {
resolveDelegationSpecifierLists();
resolveAnonymousInitializers();
resolvePropertyDeclarationBodies();
resolveSecondaryConstructorBodies();
//TODO : anonymous initializers
resolvePropertyDeclarationBodies();
resolveFunctionBodies();
}
@@ -481,69 +480,115 @@ public class TopDownAnalyzer {
}
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();
if (initializer != null) {
JetFlowInformationProvider flowInformationProvider = computeFlowData(declaration, initializer);
JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(trace, flowInformationProvider);
JetType type = typeInferrer.getType(declaringScope, initializer, false);
// Member properties
Set<JetProperty> processed = Sets.newHashSet();
for (Map.Entry<JetClass, MutableClassDescriptor> entry : classes.entrySet()) {
JetClass jetClass = entry.getKey();
MutableClassDescriptor classDescriptor = entry.getValue();
JetType expectedType;
PropertySetterDescriptor setter = propertyDescriptor.getSetter();
if (setter != null) {
expectedType = setter.getUnsubstitutedReturnType();
}
else {
expectedType = propertyDescriptor.getInType();
if (expectedType == null) {
expectedType = propertyDescriptor.getOutType();
for (JetProperty property : jetClass.getProperties()) {
final PropertyDescriptor propertyDescriptor = properties.get(property);
assert propertyDescriptor != null;
WritableScope declaringScope = declaringScopes.get(property);
JetExpression initializer = property.getInitializer();
if (initializer != null) {
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)) {
semanticServices.getErrorHandler().typeMismatch(initializer, expectedType, type);
}
resolvePropertyAccessors(property, propertyDescriptor, declaringScope);
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) {
@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);
}
resolvePropertyAccessors(property, propertyDescriptor, declaringScope);
}
}
private void resolvePropertyAccessors(JetProperty property, PropertyDescriptor propertyDescriptor, WritableScope declaringScope) {
BindingTraceAdapter fieldAccessTrackingTrace = createFieldTrackingTrace(propertyDescriptor);
WritableScope accessorScope = semanticServices.createWritableScope(declaringScope, declaringScope.getContainingDeclaration());
accessorScope.addPropertyDescriptorByFieldName("$" + propertyDescriptor.getName(), 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();
PropertySetterDescriptor setterDescriptor = propertyDescriptor.getSetter();
if (setter != null && setterDescriptor != null) {
resolveFunctionBody(fieldAccessTrackingTrace, setter, setterDescriptor, accessorScope);
}
private void resolvePropertyInitializer(JetProperty property, PropertyDescriptor propertyDescriptor, JetExpression initializer, JetScope scope) {
JetFlowInformationProvider flowInformationProvider = computeFlowData(property, initializer); // TODO : flow JET-15
JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(trace, flowInformationProvider);
JetType type = typeInferrer.getType(scope, initializer, false);
if (!declaration.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");
JetType expectedType;
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() {
+18 -1
View File
@@ -31,4 +31,21 @@ class WithPC1(a : Int) {
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 {
this@A
this<error>@a</error>
@@ -1,4 +1,4 @@
class Test {
class Test() {
<info>abstract</info> val x : Int
<info>abstract</info> val x1 : Int <info>get</info>
<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;
}
+1 -1
View File
@@ -1,4 +1,4 @@
class PrivateVar {
class PrivateVar() {
private var x = 0;
fun setValueOfX(aValue: Int) { x = aValue }
@@ -30,7 +30,7 @@ public class PropertyGenTest extends CodegenTestCase {
}
public void testPublicVar() throws Exception {
loadText("class PublicVar { public var foo = 0; }");
loadText("class PublicVar() { public var foo = 0; }");
System.out.println(generateToText());
final Class aClass = loadImplementationClass(generateClassesInFile(), "PublicVar");
final Object instance = aClass.newInstance();
@@ -41,7 +41,7 @@ public class PropertyGenTest extends CodegenTestCase {
}
public void testAccessorsInInterface() {
loadText("class AccessorsInInterface { public var foo = 0; }");
loadText("class AccessorsInInterface() { public var foo = 0; }");
final Class aClass = loadClass("AccessorsInInterface", generateClassesInFile());
assertNotNull(findMethodByName(aClass, "getFoo"));
assertNotNull(findMethodByName(aClass, "setFoo"));
@@ -82,7 +82,7 @@ public class PropertyGenTest extends CodegenTestCase {
}
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 Object instance = aClass.newInstance();
final Method getFoo = findMethodByName(aClass, "getFoo");