Qualified 'this' for classes

This commit is contained in:
Andrey Breslav
2011-03-28 22:45:16 +04:00
parent f7c76a0d4d
commit ad7115ce20
18 changed files with 196 additions and 48 deletions
@@ -92,6 +92,7 @@ public interface JetNodeTypes {
JetNodeType REFERENCE_EXPRESSION = new JetNodeType("REFERENCE_EXPRESSION", JetSimpleNameExpression.class);
JetNodeType OPERATION_REFERENCE = new JetNodeType("OPERATION_REFERENCE", JetSimpleNameExpression.class);
JetNodeType LABEL_REFERENCE = new JetNodeType("LABEL_REFERENCE", JetSimpleNameExpression.class);
JetNodeType THIS_EXPRESSION = new JetNodeType("THIS_EXPRESSION", JetThisExpression.class);
JetNodeType BINARY_EXPRESSION = new JetNodeType("BINARY_EXPRESSION", JetBinaryExpression.class);
@@ -1464,7 +1464,9 @@ public class JetExpressionParsing extends AbstractJetParsing {
advance(); // THIS_KEYWORD
if (atSet(LABELS)) {
PsiBuilder.Marker label = mark();
advance(); // LABELS
label.done(LABEL_REFERENCE);
}
if (at(LT)) {
@@ -15,7 +15,7 @@ import org.jetbrains.jet.lexer.JetTokens;
* @author max
*/
public class JetSimpleNameExpression extends JetReferenceExpression {
private static final TokenSet REFERENCE_TOKENS = TokenSet.create(JetTokens.IDENTIFIER, JetTokens.FIELD_IDENTIFIER);
public static final TokenSet REFERENCE_TOKENS = TokenSet.orSet(JetTokens.LABELS, TokenSet.create(JetTokens.IDENTIFIER, JetTokens.FIELD_IDENTIFIER));
public JetSimpleNameExpression(@NotNull ASTNode node) {
super(node);
@@ -9,6 +9,7 @@ import org.jetbrains.jet.JetNodeTypes;
* @author max
*/
public class JetThisExpression extends JetLabelQualifiedExpression {
public JetThisExpression(@NotNull ASTNode node) {
super(node);
}
@@ -23,7 +24,15 @@ public class JetThisExpression extends JetLabelQualifiedExpression {
return (JetTypeReference) findChildByType(JetNodeTypes.TYPE_REFERENCE);
}
public boolean hasSuperTypeQualifier() {
return getSuperTypeQualifier() != null;
@Nullable
public JetSimpleNameExpression getLabelElement() {
return findChildByClass(JetSimpleNameExpression.class);
}
@Nullable
public String getLabelName() {
JetSimpleNameExpression labelElement = getLabelElement();
assert labelElement == null || labelElement.getText().startsWith("@");
return labelElement == null ? null : labelElement.getText().substring(1);
}
}
@@ -67,6 +67,7 @@ public class ClassDescriptorResolver {
public void resolveMutableClassDescriptor(@NotNull JetScope scope, @NotNull JetClass classElement, @NotNull MutableClassDescriptor descriptor) {
descriptor.setName(classElement.getName());
descriptor.getClassHeaderScope().addLabeledDeclaration(descriptor);
WritableScope parameterScope = descriptor.getClassHeaderScope();
@@ -103,6 +104,8 @@ public class ClassDescriptorResolver {
parameterScope.importScope(supertype.getMemberScope());
}
descriptor.getClassHeaderScope().setThisType(descriptor.getDefaultType());
trace.recordDeclarationResolution(classElement, descriptor);
}
@@ -156,20 +159,21 @@ public class ClassDescriptorResolver {
AttributeResolver.INSTANCE.resolveAttributes(function.getModifierList()),
safeName(function.getName())
);
WritableScope parameterScope = semanticServices.createWritableScope(scope, functionDescriptor);
WritableScope innerScope = semanticServices.createWritableScope(scope, functionDescriptor);
innerScope.addLabeledDeclaration(functionDescriptor);
// The two calls below have side-effects on parameterScope
List<TypeParameterDescriptor> typeParameterDescriptors = resolveTypeParameters(functionDescriptor, parameterScope, function.getTypeParameters());
List<ValueParameterDescriptor> valueParameterDescriptors = resolveValueParameters(functionDescriptor, parameterScope, function.getValueParameters());
List<TypeParameterDescriptor> typeParameterDescriptors = resolveTypeParameters(functionDescriptor, innerScope, function.getTypeParameters());
List<ValueParameterDescriptor> valueParameterDescriptors = resolveValueParameters(functionDescriptor, innerScope, function.getValueParameters());
JetType returnType;
JetTypeReference returnTypeRef = function.getReturnTypeRef();
JetExpression bodyExpression = function.getBodyExpression();
if (returnTypeRef != null) {
returnType = typeResolver.resolveType(parameterScope, returnTypeRef);
returnType = typeResolver.resolveType(innerScope, returnTypeRef);
// TODO : check body type, consider recursion
if (bodyExpression != null) {
semanticServices.getTypeInferrer(trace).getType(parameterScope, bodyExpression, function.hasBlockBody());
semanticServices.getTypeInferrer(trace).getType(innerScope, bodyExpression, function.hasBlockBody());
}
} else {
if (bodyExpression == null) {
@@ -177,7 +181,7 @@ public class ClassDescriptorResolver {
returnType = ErrorUtils.createErrorType("No type, no body");
} else {
// TODO : Recursion possible
returnType = semanticServices.getTypeInferrer(trace).safeGetType(parameterScope, bodyExpression, function.hasBlockBody());
returnType = semanticServices.getTypeInferrer(trace).safeGetType(innerScope, bodyExpression, function.hasBlockBody());
}
}
@@ -4,6 +4,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.types.*;
import java.util.Collection;
/**
* @author abreslav
*/
@@ -33,4 +35,7 @@ public interface JetScope {
@NotNull
DeclarationDescriptor getContainingDeclaration();
@NotNull
Collection<DeclarationDescriptor> getDeclarationsByLabel(String labelName);
}
@@ -3,6 +3,8 @@ package org.jetbrains.jet.lang.resolve;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.types.*;
import java.util.Collection;
/**
* @author abreslav
*/
@@ -46,4 +48,10 @@ public class JetScopeAdapter implements JetScope {
public DeclarationDescriptor getContainingDeclaration() {
return scope.getContainingDeclaration();
}
@NotNull
@Override
public Collection<DeclarationDescriptor> getDeclarationsByLabel(String labelName) {
return scope.getDeclarationsByLabel(labelName);
}
}
@@ -3,6 +3,9 @@ package org.jetbrains.jet.lang.resolve;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.types.*;
import java.util.Collection;
import java.util.Collections;
/**
* @author abreslav
*/
@@ -33,4 +36,10 @@ public abstract class JetScopeImpl implements JetScope {
public FunctionGroup getFunctionGroup(@NotNull String name) {
return FunctionGroup.EMPTY;
}
@NotNull
@Override
public Collection<DeclarationDescriptor> getDeclarationsByLabel(String labelName) {
return Collections.emptyList();
}
}
@@ -55,6 +55,7 @@ public class LazySubstitutingClassDescriptor implements ClassDescriptor {
throw new UnsupportedOperationException(); // TODO
}
@NotNull
@Override
public String getName() {
return original.getName();
@@ -23,6 +23,7 @@ public abstract class MutableDeclarationDescriptor implements DeclarationDescrip
throw new UnsupportedOperationException(); // TODO
}
@NotNull
@Override
public String getName() {
return name;
@@ -5,10 +5,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.ErrorHandler;
import org.jetbrains.jet.lang.types.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* @author abreslav
@@ -17,6 +14,9 @@ public class WritableScope extends JetScopeAdapter {
@NotNull
private final ErrorHandler errorHandler;
@NotNull
private final DeclarationDescriptor ownerDeclarationDescriptor;
@Nullable
private Map<String, PropertyDescriptor> propertyDescriptors;
@Nullable
@@ -26,13 +26,13 @@ public class WritableScope extends JetScopeAdapter {
@Nullable
private Map<String, NamespaceDescriptor> namespaceDescriptors;
@Nullable
private Map<String, List<DeclarationDescriptor>> labelsToDescriptors;
@Nullable
private JetType thisType;
@Nullable
private List<JetScope> imports;
@NotNull
private final DeclarationDescriptor ownerDeclarationDescriptor;
public WritableScope(@NotNull JetScope scope, @NotNull DeclarationDescriptor owner, @NotNull ErrorHandler errorHandler) {
super(scope);
this.ownerDeclarationDescriptor = owner;
@@ -45,6 +45,41 @@ public class WritableScope extends JetScopeAdapter {
return ownerDeclarationDescriptor;
}
@NotNull
private Map<String, List<DeclarationDescriptor>> getLabelsToDescriptors() {
if (labelsToDescriptors == null) {
labelsToDescriptors = new HashMap<String, List<DeclarationDescriptor>>();
}
return labelsToDescriptors;
}
@NotNull
@Override
public Collection<DeclarationDescriptor> getDeclarationsByLabel(@NotNull String labelName) {
Map<String, List<DeclarationDescriptor>> labelsToDescriptors = getLabelsToDescriptors();
Collection<DeclarationDescriptor> superResult = super.getDeclarationsByLabel(labelName);
List<DeclarationDescriptor> declarationDescriptors = labelsToDescriptors.get(labelName);
if (declarationDescriptors == null) {
return superResult;
}
if (superResult.isEmpty()) return declarationDescriptors;
List<DeclarationDescriptor> result = new ArrayList<DeclarationDescriptor>(declarationDescriptors);
result.addAll(superResult);
return result;
}
public void addLabeledDeclaration(@NotNull DeclarationDescriptor descriptor) {
Map<String, List<DeclarationDescriptor>> labelsToDescriptors = getLabelsToDescriptors();
String name = descriptor.getName();
assert name != null;
List<DeclarationDescriptor> declarationDescriptors = labelsToDescriptors.get(name);
if (declarationDescriptors == null) {
declarationDescriptors = new ArrayList<DeclarationDescriptor>();
labelsToDescriptors.put(name, declarationDescriptors);
}
declarationDescriptors.add(descriptor);
}
public void importScope(@NotNull JetScope imported) {
getImports().add(0, imported);
}
@@ -29,6 +29,7 @@ public interface ConstructorDescriptor extends FunctionDescriptor {
/**
* @return "&lt;init&gt;" -- name is not stored for constructors
*/
@NotNull
@Override
String getName();
@@ -47,6 +47,12 @@ public class ErrorUtils {
return ERROR_MODULE;
}
@NotNull
@Override
public Collection<DeclarationDescriptor> getDeclarationsByLabel(String labelName) {
return Collections.emptyList();
}
};
private static final FunctionGroup ERROR_FUNCTION_GROUP = new FunctionGroup() {
@@ -249,7 +249,7 @@ public class JetStandardClasses {
return NULLABLE_NOTHING_TYPE;
}
public static boolean isNothing(JetType type) {
public static boolean isNothing(@NotNull JetType type) {
return type.getConstructor() == NOTHING_CLASS.getTypeConstructor();
}
@@ -523,33 +523,67 @@ public class JetTypeInferrer {
@Override
public void visitThisExpression(JetThisExpression expression) {
// TODO : qualified this, e.g. this@Foo<Bar>
JetType thisType = scope.getThisType();
JetTypeReference superTypeQualifier = expression.getSuperTypeQualifier();
if (superTypeQualifier != null) {
JetTypeElement superTypeElement = superTypeQualifier.getTypeElement();
// Errors are reported by the parser
if (superTypeElement instanceof JetUserType) {
JetUserType typeElement = (JetUserType) superTypeElement;
ClassifierDescriptor classifierCandidate = typeResolver.resolveClass(scope, typeElement);
if (classifierCandidate instanceof ClassDescriptor) {
ClassDescriptor superclass = (ClassDescriptor) classifierCandidate;
Collection<? extends JetType> supertypes = thisType.getConstructor().getSupertypes();
Map<TypeConstructor, TypeProjection> substitutionContext = TypeUtils.buildSubstitutionContext(thisType);
for (JetType declaredSupertype : supertypes) {
if (declaredSupertype.getConstructor().equals(superclass.getTypeConstructor())) {
result = TypeSubstitutor.INSTANCE.safeSubstitute(substitutionContext, declaredSupertype, Variance.INVARIANT);
break;
}
}
if (result == null) {
semanticServices.getErrorHandler().genericError(superTypeElement.getNode(), "Not a superclass");
}
JetType thisType = null;
String labelName = expression.getLabelName();
if (labelName != null) {
Collection<DeclarationDescriptor> declarationsByLabel = scope.getDeclarationsByLabel(labelName);
int size = declarationsByLabel.size();
if (size == 1) {
DeclarationDescriptor declarationDescriptor = declarationsByLabel.iterator().next();
if (declarationDescriptor instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
thisType = classDescriptor.getDefaultType();
}
else {
throw new UnsupportedOperationException(); // TODO
}
}
else if (size == 0) {
semanticServices.getErrorHandler().unresolvedReference(expression.getLabelElement());
}
else {
JetSimpleNameExpression labelElement = expression.getLabelElement();
assert labelElement != null;
semanticServices.getErrorHandler().genericError(labelElement.getNode(), "Ambiguous label");
}
}
else {
thisType = scope.getThisType();
}
if (thisType != null) {
if (JetStandardClasses.isNothing(thisType)) {
semanticServices.getErrorHandler().genericError(expression.getNode(), "'this' is not defined in this context");
}
else {
JetTypeReference superTypeQualifier = expression.getSuperTypeQualifier();
if (superTypeQualifier != null) {
JetTypeElement superTypeElement = superTypeQualifier.getTypeElement();
// Errors are reported by the parser
if (superTypeElement instanceof JetUserType) {
JetUserType typeElement = (JetUserType) superTypeElement;
ClassifierDescriptor classifierCandidate = typeResolver.resolveClass(scope, typeElement);
if (classifierCandidate instanceof ClassDescriptor) {
ClassDescriptor superclass = (ClassDescriptor) classifierCandidate;
Collection<? extends JetType> supertypes = thisType.getConstructor().getSupertypes();
Map<TypeConstructor, TypeProjection> substitutionContext = TypeUtils.buildSubstitutionContext(thisType);
for (JetType declaredSupertype : supertypes) {
if (declaredSupertype.getConstructor().equals(superclass.getTypeConstructor())) {
result = TypeSubstitutor.INSTANCE.safeSubstitute(substitutionContext, declaredSupertype, Variance.INVARIANT);
break;
}
}
if (result == null) {
semanticServices.getErrorHandler().genericError(superTypeElement.getNode(), "Not a superclass");
}
}
}
} else {
result = thisType;
}
}
} else {
result = thisType;
}
}
+22
View File
@@ -0,0 +1,22 @@
class Dup {
fun Dup() : Unit {
this<error>@Dup</error>
}
}
class A {
fun foo() {
this@A
this<error>@a</error>
this
}
val x = this@A.foo()
val y = this.foo()
val z = foo()
}
fun foo() {
<error>this</error>
this<error>@a</error>
}
+12 -6
View File
@@ -341,15 +341,18 @@ JetFile: Labels.jet
PsiWhiteSpace('\n ')
THIS_EXPRESSION
PsiElement(this)('this')
PsiElement(AT)('@')
LABEL_REFERENCE
PsiElement(AT)('@')
PsiWhiteSpace('\n ')
THIS_EXPRESSION
PsiElement(this)('this')
PsiElement(LABEL_IDENTIFIER)('@a')
LABEL_REFERENCE
PsiElement(LABEL_IDENTIFIER)('@a')
PsiWhiteSpace('\n ')
THIS_EXPRESSION
PsiElement(this)('this')
PsiElement(ATAT)('@@')
LABEL_REFERENCE
PsiElement(ATAT)('@@')
PsiWhiteSpace('\n\n ')
THIS_EXPRESSION
PsiElement(this)('this')
@@ -362,7 +365,8 @@ JetFile: Labels.jet
PsiWhiteSpace('\n ')
THIS_EXPRESSION
PsiElement(this)('this')
PsiElement(AT)('@')
LABEL_REFERENCE
PsiElement(AT)('@')
PsiElement(LT)('<')
TYPE_REFERENCE
USER_TYPE
@@ -372,7 +376,8 @@ JetFile: Labels.jet
PsiWhiteSpace('\n ')
THIS_EXPRESSION
PsiElement(this)('this')
PsiElement(LABEL_IDENTIFIER)('@a')
LABEL_REFERENCE
PsiElement(LABEL_IDENTIFIER)('@a')
PsiElement(LT)('<')
TYPE_REFERENCE
USER_TYPE
@@ -382,7 +387,8 @@ JetFile: Labels.jet
PsiWhiteSpace('\n ')
THIS_EXPRESSION
PsiElement(this)('this')
PsiElement(ATAT)('@@')
LABEL_REFERENCE
PsiElement(ATAT)('@@')
PsiElement(LT)('<')
TYPE_REFERENCE
USER_TYPE
@@ -27,4 +27,8 @@ public class JetPsiCheckerTest extends LightDaemonAnalyzerTestCase {
public void testBinaryCallsOnNullableValues() throws Exception {
doTest("/checker/BinaryCallsOnNullableValues.jet", true, true);
}
public void testQualifiedThis() throws Exception {
doTest("/checker/QualifiedThis.jet", true, true);
}
}