Qualified 'this' for classes
This commit is contained in:
@@ -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 "<init>" -- 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user