JET-16 Check property initializers as parts of the primary constructor's body
This commit is contained in:
@@ -107,6 +107,11 @@ public class JetPsiChecker implements Annotator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public void visitParameter(JetParameter parameter) {
|
||||||
|
// bindingContext.get
|
||||||
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitJetElement(JetElement elem) {
|
public void visitJetElement(JetElement elem) {
|
||||||
elem.acceptChildren(this);
|
elem.acceptChildren(this);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.jetbrains.jet.lang.resolve;
|
package org.jetbrains.jet.lang.resolve;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -22,9 +23,10 @@ public class BindingTraceContext implements BindingContext, BindingTrace {
|
|||||||
private final Map<DeclarationDescriptor, PsiElement> descriptorToDeclarations = new HashMap<DeclarationDescriptor, PsiElement>();
|
private final Map<DeclarationDescriptor, PsiElement> descriptorToDeclarations = new HashMap<DeclarationDescriptor, PsiElement>();
|
||||||
private final Map<PsiElement, DeclarationDescriptor> declarationsToDescriptors = new HashMap<PsiElement, DeclarationDescriptor>();
|
private final Map<PsiElement, DeclarationDescriptor> declarationsToDescriptors = new HashMap<PsiElement, DeclarationDescriptor>();
|
||||||
private final Map<PsiElement, ConstructorDescriptor> constructorDeclarationsToDescriptors = new HashMap<PsiElement, ConstructorDescriptor>();
|
private final Map<PsiElement, ConstructorDescriptor> constructorDeclarationsToDescriptors = new HashMap<PsiElement, ConstructorDescriptor>();
|
||||||
|
private final Map<PsiElement, PropertyDescriptor> propertyDeclarationsToDescriptors = Maps.newHashMap();
|
||||||
private final Set<JetFunctionLiteralExpression> blocks = new HashSet<JetFunctionLiteralExpression>();
|
private final Set<JetFunctionLiteralExpression> blocks = new HashSet<JetFunctionLiteralExpression>();
|
||||||
private final Set<JetElement> statements = new HashSet<JetElement>();
|
private final Set<JetElement> statements = new HashSet<JetElement>();
|
||||||
private final Set<PropertyDescriptor> fieldMentionedInAccessor = new HashSet<PropertyDescriptor>();
|
private final Set<PropertyDescriptor> backingFieldRequired = new HashSet<PropertyDescriptor>();
|
||||||
|
|
||||||
private JetScope toplevelScope;
|
private JetScope toplevelScope;
|
||||||
|
|
||||||
@@ -58,23 +60,32 @@ public class BindingTraceContext implements BindingContext, BindingTrace {
|
|||||||
@Override
|
@Override
|
||||||
public void recordDeclarationResolution(@NotNull PsiElement declaration, @NotNull DeclarationDescriptor descriptor) {
|
public void recordDeclarationResolution(@NotNull PsiElement declaration, @NotNull DeclarationDescriptor descriptor) {
|
||||||
safePut(descriptorToDeclarations, descriptor.getOriginal(), declaration);
|
safePut(descriptorToDeclarations, descriptor.getOriginal(), declaration);
|
||||||
if (descriptor instanceof ConstructorDescriptor) {
|
descriptor.accept(new DeclarationDescriptorVisitor<Void, PsiElement>() {
|
||||||
safePut(constructorDeclarationsToDescriptors, declaration, (ConstructorDescriptor) descriptor);
|
@Override
|
||||||
}
|
public Void visitConstructorDescriptor(ConstructorDescriptor constructorDescriptor, PsiElement declaration) {
|
||||||
else {
|
safePut(constructorDeclarationsToDescriptors, declaration, constructorDescriptor);
|
||||||
safePut(declarationsToDescriptors, declaration, descriptor.getOriginal());
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visitDeclarationDescriptor(DeclarationDescriptor descriptor, PsiElement declaration) {
|
||||||
|
safePut(declarationsToDescriptors, declaration, descriptor.getOriginal());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, declaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <K, V> void safePut(Map<K, V> map, K key, V value) {
|
private <K, V> void safePut(Map<K, V> map, K key, V value) {
|
||||||
V oldValue = map.put(key, value);
|
V oldValue = map.put(key, value);
|
||||||
// TODO:
|
// TODO:
|
||||||
// assert oldValue == null || oldValue == value : key + ": " + oldValue + " and " + value;
|
assert oldValue == null || oldValue == value : key + ": " + oldValue + " and " + value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void recordFieldAccessFromAccessor(@NotNull PropertyDescriptor propertyDescriptor) {
|
public void requireBackingField(@NotNull PropertyDescriptor propertyDescriptor) {
|
||||||
fieldMentionedInAccessor.add(propertyDescriptor);
|
backingFieldRequired.add(propertyDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -195,6 +206,6 @@ public class BindingTraceContext implements BindingContext, BindingTrace {
|
|||||||
else if (!getter.hasBody()) {
|
else if (!getter.hasBody()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return fieldMentionedInAccessor.contains(propertyDescriptor);
|
return backingFieldRequired.contains(propertyDescriptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ public class TopDownAnalyzer {
|
|||||||
private final Map<JetProperty, PropertyDescriptor> properties = new LinkedHashMap<JetProperty, PropertyDescriptor>();
|
private final Map<JetProperty, PropertyDescriptor> properties = new LinkedHashMap<JetProperty, PropertyDescriptor>();
|
||||||
private final Map<JetDeclaration, WritableScope> declaringScopes = new HashMap<JetDeclaration, WritableScope>();
|
private final Map<JetDeclaration, WritableScope> declaringScopes = new HashMap<JetDeclaration, WritableScope>();
|
||||||
private final Multimap<DeclarationDescriptor, PropertyDescriptor> declaringScopesToProperties = ArrayListMultimap.create();
|
private final Multimap<DeclarationDescriptor, PropertyDescriptor> declaringScopesToProperties = ArrayListMultimap.create();
|
||||||
|
private final Set<PropertyDescriptor> primaryConstructorParameterProperties = Sets.newHashSet();
|
||||||
|
|
||||||
private final JetSemanticServices semanticServices;
|
private final JetSemanticServices semanticServices;
|
||||||
private final ClassDescriptorResolver classDescriptorResolver;
|
private final ClassDescriptorResolver classDescriptorResolver;
|
||||||
@@ -35,6 +36,7 @@ public class TopDownAnalyzer {
|
|||||||
private final JetControlFlowDataTraceFactory flowDataTraceFactory;
|
private final JetControlFlowDataTraceFactory flowDataTraceFactory;
|
||||||
private boolean readyToProcessExpressions = false;
|
private boolean readyToProcessExpressions = false;
|
||||||
private final BindingTraceAdapter traceForConstructors;
|
private final BindingTraceAdapter traceForConstructors;
|
||||||
|
private final BindingTraceAdapter traceForMembers;
|
||||||
|
|
||||||
public TopDownAnalyzer(JetSemanticServices semanticServices, @NotNull BindingTraceContext bindingTrace, @NotNull JetControlFlowDataTraceFactory flowDataTraceFactory) {
|
public TopDownAnalyzer(JetSemanticServices semanticServices, @NotNull BindingTraceContext bindingTrace, @NotNull JetControlFlowDataTraceFactory flowDataTraceFactory) {
|
||||||
this.semanticServices = semanticServices;
|
this.semanticServices = semanticServices;
|
||||||
@@ -43,7 +45,7 @@ public class TopDownAnalyzer {
|
|||||||
this.flowDataTraceFactory = flowDataTraceFactory;
|
this.flowDataTraceFactory = flowDataTraceFactory;
|
||||||
|
|
||||||
// This allows access to backing fields
|
// This allows access to backing fields
|
||||||
this.traceForConstructors = new BindingTraceAdapter(trace) {
|
this.traceForConstructors = new BindingTraceAdapter(bindingTrace) {
|
||||||
@Override
|
@Override
|
||||||
public void recordReferenceResolution(@NotNull JetReferenceExpression expression, @NotNull DeclarationDescriptor descriptor) {
|
public void recordReferenceResolution(@NotNull JetReferenceExpression expression, @NotNull DeclarationDescriptor descriptor) {
|
||||||
super.recordReferenceResolution(expression, descriptor);
|
super.recordReferenceResolution(expression, descriptor);
|
||||||
@@ -57,6 +59,20 @@ public class TopDownAnalyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This tracks access to properties in order to register primary constructor parameters that yield real fields (JET-9)
|
||||||
|
this.traceForMembers = new BindingTraceAdapter(bindingTrace) {
|
||||||
|
@Override
|
||||||
|
public void recordReferenceResolution(@NotNull JetReferenceExpression expression, @NotNull DeclarationDescriptor descriptor) {
|
||||||
|
super.recordReferenceResolution(expression, descriptor);
|
||||||
|
if (descriptor instanceof PropertyDescriptor) {
|
||||||
|
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
|
||||||
|
if (primaryConstructorParameterProperties.contains(propertyDescriptor)) {
|
||||||
|
requireBackingField(propertyDescriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public TopDownAnalyzer(JetSemanticServices semanticServices, @NotNull BindingTraceContext bindingTrace) {
|
public TopDownAnalyzer(JetSemanticServices semanticServices, @NotNull BindingTraceContext bindingTrace) {
|
||||||
@@ -242,13 +258,14 @@ public class TopDownAnalyzer {
|
|||||||
WritableScope memberScope = classDescriptor.getWritableUnsubstitutedMemberScope(); // TODO : this is REALLY questionable
|
WritableScope memberScope = classDescriptor.getWritableUnsubstitutedMemberScope(); // TODO : this is REALLY questionable
|
||||||
ConstructorDescriptor constructorDescriptor = classDescriptorResolver.resolvePrimaryConstructorDescriptor(memberScope, classDescriptor, klass);
|
ConstructorDescriptor constructorDescriptor = classDescriptorResolver.resolvePrimaryConstructorDescriptor(memberScope, classDescriptor, klass);
|
||||||
for (JetParameter parameter : klass.getPrimaryConstructorParameters()) {
|
for (JetParameter parameter : klass.getPrimaryConstructorParameters()) {
|
||||||
VariableDescriptor propertyDescriptor = classDescriptorResolver.resolvePrimaryConstructorParameterToAProperty(
|
PropertyDescriptor propertyDescriptor = classDescriptorResolver.resolvePrimaryConstructorParameterToAProperty(
|
||||||
classDescriptor,
|
classDescriptor,
|
||||||
memberScope,
|
memberScope,
|
||||||
parameter
|
parameter
|
||||||
);
|
);
|
||||||
memberScope.addVariableDescriptor(
|
memberScope.addVariableDescriptor(
|
||||||
propertyDescriptor);
|
propertyDescriptor);
|
||||||
|
primaryConstructorParameterProperties.add(propertyDescriptor);
|
||||||
}
|
}
|
||||||
if (constructorDescriptor != null) {
|
if (constructorDescriptor != null) {
|
||||||
classDescriptor.setPrimaryConstructor(constructorDescriptor);
|
classDescriptor.setPrimaryConstructor(constructorDescriptor);
|
||||||
@@ -552,7 +569,7 @@ public class TopDownAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private BindingTraceAdapter createFieldTrackingTrace(final PropertyDescriptor propertyDescriptor) {
|
private BindingTraceAdapter createFieldTrackingTrace(final PropertyDescriptor propertyDescriptor) {
|
||||||
return new BindingTraceAdapter(trace) {
|
return new BindingTraceAdapter(traceForMembers) {
|
||||||
@Override
|
@Override
|
||||||
public void recordReferenceResolution(@NotNull JetReferenceExpression expression, @NotNull DeclarationDescriptor descriptor) {
|
public void recordReferenceResolution(@NotNull JetReferenceExpression expression, @NotNull DeclarationDescriptor descriptor) {
|
||||||
super.recordReferenceResolution(expression, descriptor);
|
super.recordReferenceResolution(expression, descriptor);
|
||||||
@@ -561,7 +578,7 @@ public class TopDownAnalyzer {
|
|||||||
if (simpleNameExpression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER) {
|
if (simpleNameExpression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER) {
|
||||||
// This check may be considered redundant as long as $x is only accessible from accessors to $x
|
// This check may be considered redundant as long as $x is only accessible from accessors to $x
|
||||||
if (descriptor == propertyDescriptor) { // TODO : original?
|
if (descriptor == propertyDescriptor) { // TODO : original?
|
||||||
recordFieldAccessFromAccessor(propertyDescriptor);
|
requireBackingField(propertyDescriptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -571,7 +588,7 @@ public class TopDownAnalyzer {
|
|||||||
|
|
||||||
private void resolvePropertyInitializer(JetProperty property, PropertyDescriptor propertyDescriptor, JetExpression initializer, JetScope scope) {
|
private void resolvePropertyInitializer(JetProperty property, PropertyDescriptor propertyDescriptor, JetExpression initializer, JetScope scope) {
|
||||||
JetFlowInformationProvider flowInformationProvider = computeFlowData(property, initializer); // TODO : flow JET-15
|
JetFlowInformationProvider flowInformationProvider = computeFlowData(property, initializer); // TODO : flow JET-15
|
||||||
JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(trace, flowInformationProvider);
|
JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(traceForConstructors, flowInformationProvider);
|
||||||
JetType type = typeInferrer.getType(scope, initializer, false);
|
JetType type = typeInferrer.getType(scope, initializer, false);
|
||||||
|
|
||||||
JetType expectedType;
|
JetType expectedType;
|
||||||
@@ -599,7 +616,7 @@ public class TopDownAnalyzer {
|
|||||||
WritableScope declaringScope = declaringScopes.get(declaration);
|
WritableScope declaringScope = declaringScopes.get(declaration);
|
||||||
assert declaringScope != null;
|
assert declaringScope != null;
|
||||||
|
|
||||||
resolveFunctionBody(trace, (JetFunction) declaration, (FunctionDescriptorImpl) descriptor, declaringScope);
|
resolveFunctionBody(traceForMembers, (JetFunction) declaration, (FunctionDescriptorImpl) descriptor, declaringScope);
|
||||||
|
|
||||||
assert descriptor.getUnsubstitutedReturnType() != null;
|
assert descriptor.getUnsubstitutedReturnType() != null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ public interface BindingTrace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void recordFieldAccessFromAccessor(@NotNull PropertyDescriptor propertyDescriptor) {
|
public void requireBackingField(@NotNull PropertyDescriptor propertyDescriptor) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -76,5 +76,5 @@ public interface BindingTrace {
|
|||||||
|
|
||||||
public void removeReferenceResolution(@NotNull JetReferenceExpression referenceExpression);
|
public void removeReferenceResolution(@NotNull JetReferenceExpression referenceExpression);
|
||||||
|
|
||||||
public void recordFieldAccessFromAccessor(@NotNull PropertyDescriptor propertyDescriptor);
|
public void requireBackingField(@NotNull PropertyDescriptor propertyDescriptor);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ public class BindingTraceAdapter implements BindingTrace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void recordFieldAccessFromAccessor(@NotNull PropertyDescriptor propertyDescriptor) {
|
public void requireBackingField(@NotNull PropertyDescriptor propertyDescriptor) {
|
||||||
originalTrace.recordFieldAccessFromAccessor(propertyDescriptor);
|
originalTrace.requireBackingField(propertyDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -4,28 +4,32 @@ package org.jetbrains.jet.lang.types;
|
|||||||
* @author abreslav
|
* @author abreslav
|
||||||
*/
|
*/
|
||||||
public class DeclarationDescriptorVisitor<R, D> {
|
public class DeclarationDescriptorVisitor<R, D> {
|
||||||
public R visitVariableDescriptor(VariableDescriptor descriptor, D data) {
|
public R visitDeclarationDescriptor(DeclarationDescriptor descriptor, D data) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public R visitVariableDescriptor(VariableDescriptor descriptor, D data) {
|
||||||
|
return visitDeclarationDescriptor(descriptor, data);
|
||||||
|
}
|
||||||
|
|
||||||
public R visitFunctionDescriptor(FunctionDescriptor descriptor, D data) {
|
public R visitFunctionDescriptor(FunctionDescriptor descriptor, D data) {
|
||||||
return null;
|
return visitDeclarationDescriptor(descriptor, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public R visitTypeParameterDescriptor(TypeParameterDescriptor descriptor, D data) {
|
public R visitTypeParameterDescriptor(TypeParameterDescriptor descriptor, D data) {
|
||||||
return null;
|
return visitDeclarationDescriptor(descriptor, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public R visitNamespaceDescriptor(NamespaceDescriptor namespaceDescriptor, D data) {
|
public R visitNamespaceDescriptor(NamespaceDescriptor descriptor, D data) {
|
||||||
return null;
|
return visitDeclarationDescriptor(descriptor, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public R visitClassDescriptor(ClassDescriptor descriptor, D data) {
|
public R visitClassDescriptor(ClassDescriptor descriptor, D data) {
|
||||||
return null;
|
return visitDeclarationDescriptor(descriptor, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public R visitModuleDeclaration(ModuleDescriptor descriptor, D data) {
|
public R visitModuleDeclaration(ModuleDescriptor descriptor, D data) {
|
||||||
return null;
|
return visitDeclarationDescriptor(descriptor, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public R visitConstructorDescriptor(ConstructorDescriptor constructorDescriptor, D data) {
|
public R visitConstructorDescriptor(ConstructorDescriptor constructorDescriptor, D data) {
|
||||||
|
|||||||
Reference in New Issue
Block a user