diff --git a/idea/src/org/jetbrains/jet/JetNodeTypes.java b/idea/src/org/jetbrains/jet/JetNodeTypes.java index 18565905b46..6f489e41407 100644 --- a/idea/src/org/jetbrains/jet/JetNodeTypes.java +++ b/idea/src/org/jetbrains/jet/JetNodeTypes.java @@ -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); diff --git a/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java b/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java index 859bd54beb5..bd555071718 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java @@ -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)) { diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetSimpleNameExpression.java b/idea/src/org/jetbrains/jet/lang/psi/JetSimpleNameExpression.java index 0668e4ce102..f4da4414792 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetSimpleNameExpression.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetSimpleNameExpression.java @@ -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); diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetThisExpression.java b/idea/src/org/jetbrains/jet/lang/psi/JetThisExpression.java index 34e1b857445..83c9905dc6e 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetThisExpression.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetThisExpression.java @@ -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); } } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java index d67a460def7..33166e3a92b 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java @@ -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 typeParameterDescriptors = resolveTypeParameters(functionDescriptor, parameterScope, function.getTypeParameters()); - List valueParameterDescriptors = resolveValueParameters(functionDescriptor, parameterScope, function.getValueParameters()); + List typeParameterDescriptors = resolveTypeParameters(functionDescriptor, innerScope, function.getTypeParameters()); + List 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()); } } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/JetScope.java b/idea/src/org/jetbrains/jet/lang/resolve/JetScope.java index d047e2ec013..18f361e9451 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/JetScope.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/JetScope.java @@ -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 getDeclarationsByLabel(String labelName); } \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/lang/resolve/JetScopeAdapter.java b/idea/src/org/jetbrains/jet/lang/resolve/JetScopeAdapter.java index 6a02bcc4e45..aba189f348c 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/JetScopeAdapter.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/JetScopeAdapter.java @@ -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 getDeclarationsByLabel(String labelName) { + return scope.getDeclarationsByLabel(labelName); + } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/lang/resolve/JetScopeImpl.java b/idea/src/org/jetbrains/jet/lang/resolve/JetScopeImpl.java index 57d755b546f..85726ffe20e 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/JetScopeImpl.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/JetScopeImpl.java @@ -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 getDeclarationsByLabel(String labelName) { + return Collections.emptyList(); + } } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/LazySubstitutingClassDescriptor.java b/idea/src/org/jetbrains/jet/lang/resolve/LazySubstitutingClassDescriptor.java index 0b30c6c7a08..0aa16209bc1 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/LazySubstitutingClassDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/LazySubstitutingClassDescriptor.java @@ -55,6 +55,7 @@ public class LazySubstitutingClassDescriptor implements ClassDescriptor { throw new UnsupportedOperationException(); // TODO } + @NotNull @Override public String getName() { return original.getName(); diff --git a/idea/src/org/jetbrains/jet/lang/resolve/MutableDeclarationDescriptor.java b/idea/src/org/jetbrains/jet/lang/resolve/MutableDeclarationDescriptor.java index 636f3ce02c8..76b5ac43e50 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/MutableDeclarationDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/MutableDeclarationDescriptor.java @@ -23,6 +23,7 @@ public abstract class MutableDeclarationDescriptor implements DeclarationDescrip throw new UnsupportedOperationException(); // TODO } + @NotNull @Override public String getName() { return name; diff --git a/idea/src/org/jetbrains/jet/lang/resolve/WritableScope.java b/idea/src/org/jetbrains/jet/lang/resolve/WritableScope.java index 6a4506e3ede..956bf4a8c8e 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/WritableScope.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/WritableScope.java @@ -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 propertyDescriptors; @Nullable @@ -26,13 +26,13 @@ public class WritableScope extends JetScopeAdapter { @Nullable private Map namespaceDescriptors; @Nullable + private Map> labelsToDescriptors; + @Nullable private JetType thisType; + @Nullable private List 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> getLabelsToDescriptors() { + if (labelsToDescriptors == null) { + labelsToDescriptors = new HashMap>(); + } + return labelsToDescriptors; + } + + @NotNull + @Override + public Collection getDeclarationsByLabel(@NotNull String labelName) { + Map> labelsToDescriptors = getLabelsToDescriptors(); + Collection superResult = super.getDeclarationsByLabel(labelName); + List declarationDescriptors = labelsToDescriptors.get(labelName); + if (declarationDescriptors == null) { + return superResult; + } + if (superResult.isEmpty()) return declarationDescriptors; + List result = new ArrayList(declarationDescriptors); + result.addAll(superResult); + return result; + } + + public void addLabeledDeclaration(@NotNull DeclarationDescriptor descriptor) { + Map> labelsToDescriptors = getLabelsToDescriptors(); + String name = descriptor.getName(); + assert name != null; + List declarationDescriptors = labelsToDescriptors.get(name); + if (declarationDescriptors == null) { + declarationDescriptors = new ArrayList(); + labelsToDescriptors.put(name, declarationDescriptors); + } + declarationDescriptors.add(descriptor); + } + public void importScope(@NotNull JetScope imported) { getImports().add(0, imported); } diff --git a/idea/src/org/jetbrains/jet/lang/types/ConstructorDescriptor.java b/idea/src/org/jetbrains/jet/lang/types/ConstructorDescriptor.java index 5d1be022c42..10bdf70915c 100644 --- a/idea/src/org/jetbrains/jet/lang/types/ConstructorDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/types/ConstructorDescriptor.java @@ -29,6 +29,7 @@ public interface ConstructorDescriptor extends FunctionDescriptor { /** * @return "<init>" -- name is not stored for constructors */ + @NotNull @Override String getName(); diff --git a/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java b/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java index e20ccf12440..e4cbb6797f9 100644 --- a/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java +++ b/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java @@ -47,6 +47,12 @@ public class ErrorUtils { return ERROR_MODULE; } + @NotNull + @Override + public Collection getDeclarationsByLabel(String labelName) { + return Collections.emptyList(); + } + }; private static final FunctionGroup ERROR_FUNCTION_GROUP = new FunctionGroup() { diff --git a/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java b/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java index 6f80ff73f32..502d9c7d5c7 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java @@ -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(); } diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index 9cab53c33ce..670a9e0b87c 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -523,33 +523,67 @@ public class JetTypeInferrer { @Override public void visitThisExpression(JetThisExpression expression) { // TODO : qualified this, e.g. this@Foo - 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 supertypes = thisType.getConstructor().getSupertypes(); - Map 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 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 supertypes = thisType.getConstructor().getSupertypes(); + Map 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; } } diff --git a/idea/testData/checker/QualifiedThis.jet b/idea/testData/checker/QualifiedThis.jet new file mode 100644 index 00000000000..7ee958a4a17 --- /dev/null +++ b/idea/testData/checker/QualifiedThis.jet @@ -0,0 +1,22 @@ +class Dup { + fun Dup() : Unit { + this@Dup + } +} + +class A { + fun foo() { + this@A + this@a + this + } + + val x = this@A.foo() + val y = this.foo() + val z = foo() +} + +fun foo() { + this + this@a +} diff --git a/idea/testData/psi/Labels.txt b/idea/testData/psi/Labels.txt index e8d66a12abc..ad500e937ff 100644 --- a/idea/testData/psi/Labels.txt +++ b/idea/testData/psi/Labels.txt @@ -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 diff --git a/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java b/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java index 40884999179..fb257b919d0 100644 --- a/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java @@ -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); + } }