From 36470388c4358c463d1660f8fe23ab0d81efa696 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Fri, 3 Jun 2011 15:09:47 +0400 Subject: [PATCH] Tests for objects + delegation specifier resolution + override bindings for objects --- .../annotations/JetLineMarkerProvider.java | 8 +- .../jet/lang/resolve/AnalyzingUtils.java | 31 ++-- .../jet/lang/resolve/TopDownAnalyzer.java | 146 ++++++++++-------- .../jet/lang/types/JetStandardLibrary.java | 4 +- idea/testData/checker/Objects.jet | 59 +++++++ idea/testData/resolve/NestedObjects.jet | 28 ++++ idea/testData/resolve/Objects.jet | 28 ++++ 7 files changed, 218 insertions(+), 86 deletions(-) create mode 100644 idea/testData/checker/Objects.jet create mode 100644 idea/testData/resolve/NestedObjects.jet create mode 100644 idea/testData/resolve/Objects.jet diff --git a/idea/src/org/jetbrains/jet/lang/annotations/JetLineMarkerProvider.java b/idea/src/org/jetbrains/jet/lang/annotations/JetLineMarkerProvider.java index 7aa3127370f..7c8ce8ebedc 100644 --- a/idea/src/org/jetbrains/jet/lang/annotations/JetLineMarkerProvider.java +++ b/idea/src/org/jetbrains/jet/lang/annotations/JetLineMarkerProvider.java @@ -173,15 +173,19 @@ public class JetLineMarkerProvider implements LineMarkerProvider { ); } - if (element instanceof JetObjectDeclaration) { + if (element instanceof JetObjectDeclaration && !(element.getParent() instanceof JetExpression)) { JetObjectDeclaration jetObjectDeclaration = (JetObjectDeclaration) element; + return new LineMarkerInfo( jetObjectDeclaration, jetObjectDeclaration.getTextOffset(), Icons.ANONYMOUS_CLASS_ICON, Pass.UPDATE_ALL, new Function() { @Override public String fun(JetObjectDeclaration jetObjectDeclaration) { ClassDescriptor classDescriptor = bindingContext.getClassDescriptor(jetObjectDeclaration); - return DescriptorRenderer.HTML.renderAsObject(classDescriptor); + if (classDescriptor != null) { + return DescriptorRenderer.HTML.renderAsObject(classDescriptor); + } + return "<none>"; } }, new GutterIconNavigationHandler() { diff --git a/idea/src/org/jetbrains/jet/lang/resolve/AnalyzingUtils.java b/idea/src/org/jetbrains/jet/lang/resolve/AnalyzingUtils.java index 9104bdeacca..a0b83f3a7ea 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/AnalyzingUtils.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/AnalyzingUtils.java @@ -29,29 +29,30 @@ import java.util.Collections; */ public class AnalyzingUtils { private final static Key> BINDING_CONTEXT = Key.create("BINDING_CONTEXT"); + private static final Object lock = new Object(); - synchronized // TODO public static BindingContext analyzeFileWithCache(@NotNull final JetFile file) { // TODO : Synchronization? CachedValue bindingContextCachedValue = file.getUserData(BINDING_CONTEXT); if (bindingContextCachedValue == null) { bindingContextCachedValue = CachedValuesManager.getManager(file.getProject()).createCachedValue(new CachedValueProvider() { @Override - synchronized // TODO : make it more granular public Result compute() { - try { - JetNamespace rootNamespace = file.getRootNamespace(); - BindingContext bindingContext = analyzeNamespace(rootNamespace, JetControlFlowDataTraceFactory.EMPTY); - return new Result(bindingContext, PsiModificationTracker.MODIFICATION_COUNT); - } - catch (ProcessCanceledException e) { - throw e; - } - catch (Throwable e) { - e.printStackTrace(); - BindingTraceContext bindingTraceContext = new BindingTraceContext(); - bindingTraceContext.getErrorHandler().genericError(file.getNode(), e.getClass().getSimpleName() + ": " + e.getMessage()); - return new Result(bindingTraceContext, PsiModificationTracker.MODIFICATION_COUNT); + synchronized (lock) { + try { + JetNamespace rootNamespace = file.getRootNamespace(); + BindingContext bindingContext = analyzeNamespace(rootNamespace, JetControlFlowDataTraceFactory.EMPTY); + return new Result(bindingContext, PsiModificationTracker.MODIFICATION_COUNT); + } + catch (ProcessCanceledException e) { + throw e; + } + catch (Throwable e) { + e.printStackTrace(); + BindingTraceContext bindingTraceContext = new BindingTraceContext(); + bindingTraceContext.getErrorHandler().genericError(file.getNode(), e.getClass().getSimpleName() + ": " + e.getMessage()); + return new Result(bindingTraceContext, PsiModificationTracker.MODIFICATION_COUNT); + } } } }, false); diff --git a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java index 6c94f102b6e..dd6788ff844 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java @@ -411,15 +411,20 @@ public class TopDownAnalyzer { private void bindOverrides() { for (Map.Entry entry : classes.entrySet()) { - MutableClassDescriptor classDescriptor = entry.getValue(); -// JetClass jetClass = entry.getKey(); + bindOverridesInAClass(entry.getValue()); + } + for (Map.Entry entry : objects.entrySet()) { + bindOverridesInAClass(entry.getValue()); + } + } - for (FunctionDescriptor declaredFunction : classDescriptor.getFunctions()) { - for (JetType supertype : classDescriptor.getTypeConstructor().getSupertypes()) { - FunctionDescriptor overridden = findFunctionOverridableBy(declaredFunction, supertype); - if (overridden != null) { - ((FunctionDescriptorImpl) declaredFunction).addOverriddenFunction(overridden); - } + private void bindOverridesInAClass(MutableClassDescriptor classDescriptor) { + + for (FunctionDescriptor declaredFunction : classDescriptor.getFunctions()) { + for (JetType supertype : classDescriptor.getTypeConstructor().getSupertypes()) { + FunctionDescriptor overridden = findFunctionOverridableBy(declaredFunction, supertype); + if (overridden != null) { + ((FunctionDescriptorImpl) declaredFunction).addOverriddenFunction(overridden); } } } @@ -458,74 +463,79 @@ public class TopDownAnalyzer { private void resolveDelegationSpecifierLists() { // TODO : Make sure the same thing is not initialized twice for (Map.Entry entry : classes.entrySet()) { - final JetClass jetClass = entry.getKey(); - final MutableClassDescriptor descriptor = entry.getValue(); - final ConstructorDescriptor primaryConstructor = descriptor.getUnsubstitutedPrimaryConstructor(); - final JetScope scopeForConstructor = primaryConstructor == null - ? null - : getInnerScopeForConstructor(primaryConstructor, descriptor.getScopeForMemberResolution(), true); - final JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(traceForConstructors, JetFlowInformationProvider.NONE); // TODO : flow + resolveDelegationSpecifierList(entry.getKey(), entry.getValue()); + } + for (Map.Entry entry : objects.entrySet()) { + resolveDelegationSpecifierList(entry.getKey(), entry.getValue()); + } + } - for (JetDelegationSpecifier delegationSpecifier : jetClass.getDelegationSpecifiers()) { - delegationSpecifier.accept(new JetVisitor() { - @Override - public void visitDelegationByExpressionSpecifier(JetDelegatorByExpressionSpecifier specifier) { - JetExpression delegateExpression = specifier.getDelegateExpression(); - if (delegateExpression != null) { - JetScope scope = scopeForConstructor == null ? descriptor.getScopeForMemberResolution() : scopeForConstructor; - JetType type = typeInferrer.getType(scope, delegateExpression, false); - JetType supertype = trace.getBindingContext().resolveTypeReference(specifier.getTypeReference()); - if (type != null && !semanticServices.getTypeChecker().isSubtypeOf(type, supertype)) { // TODO : Convertible? - trace.getErrorHandler().typeMismatch(delegateExpression, supertype, type); - } - } - } + private void resolveDelegationSpecifierList(final JetClassOrObject jetClass, final MutableClassDescriptor descriptor) { + final ConstructorDescriptor primaryConstructor = descriptor.getUnsubstitutedPrimaryConstructor(); + final JetScope scopeForConstructor = primaryConstructor == null + ? null + : getInnerScopeForConstructor(primaryConstructor, descriptor.getScopeForMemberResolution(), true); + final JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(traceForConstructors, JetFlowInformationProvider.NONE); // TODO : flow - @Override - public void visitDelegationToSuperCallSpecifier(JetDelegatorToSuperCall call) { - JetTypeReference typeReference = call.getTypeReference(); - if (typeReference != null) { - if (jetClass.hasPrimaryConstructor()) { - typeInferrer.checkConstructorCall(scopeForConstructor, typeReference, call); - } - else { - JetArgumentList valueArgumentList = call.getValueArgumentList(); - assert valueArgumentList != null; - trace.getErrorHandler().genericError(valueArgumentList.getNode(), - "Class " + JetPsiUtil.safeName(jetClass.getName()) + " must have a constructor in order to be able to initialize supertypes"); - } - } - } - - @Override - public void visitDelegationToSuperClassSpecifier(JetDelegatorToSuperClass specifier) { + for (JetDelegationSpecifier delegationSpecifier : jetClass.getDelegationSpecifiers()) { + delegationSpecifier.accept(new JetVisitor() { + @Override + public void visitDelegationByExpressionSpecifier(JetDelegatorByExpressionSpecifier specifier) { + JetExpression delegateExpression = specifier.getDelegateExpression(); + if (delegateExpression != null) { + JetScope scope = scopeForConstructor == null ? descriptor.getScopeForMemberResolution() : scopeForConstructor; + JetType type = typeInferrer.getType(scope, delegateExpression, false); JetType supertype = trace.getBindingContext().resolveTypeReference(specifier.getTypeReference()); - if (supertype != null) { - DeclarationDescriptor declarationDescriptor = supertype.getConstructor().getDeclarationDescriptor(); - if (declarationDescriptor instanceof ClassDescriptor) { - ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor; - if (classDescriptor.hasConstructors() && !ErrorUtils.isError(classDescriptor.getTypeConstructor())) { - trace.getErrorHandler().genericError(specifier.getNode(), "This type has a constructor, and thus must be initialized here"); - } - } - else { - trace.getErrorHandler().genericError(specifier.getNode(), "Only classes may serve as supertypes"); - } - + if (type != null && !semanticServices.getTypeChecker().isSubtypeOf(type, supertype)) { // TODO : Convertible? + trace.getErrorHandler().typeMismatch(delegateExpression, supertype, type); } } + } - @Override - public void visitDelegationToThisCall(JetDelegatorToThisCall thisCall) { - throw new IllegalStateException("This-calls should be prohibited by the parser"); + @Override + public void visitDelegationToSuperCallSpecifier(JetDelegatorToSuperCall call) { + JetTypeReference typeReference = call.getTypeReference(); + if (typeReference != null) { + if (jetClass.hasPrimaryConstructor()) { + typeInferrer.checkConstructorCall(scopeForConstructor, typeReference, call); + } + else { + JetArgumentList valueArgumentList = call.getValueArgumentList(); + assert valueArgumentList != null; + trace.getErrorHandler().genericError(valueArgumentList.getNode(), + "Class " + JetPsiUtil.safeName(jetClass.getName()) + " must have a constructor in order to be able to initialize supertypes"); + } } + } + + @Override + public void visitDelegationToSuperClassSpecifier(JetDelegatorToSuperClass specifier) { + JetType supertype = trace.getBindingContext().resolveTypeReference(specifier.getTypeReference()); + if (supertype != null) { + DeclarationDescriptor declarationDescriptor = supertype.getConstructor().getDeclarationDescriptor(); + if (declarationDescriptor instanceof ClassDescriptor) { + ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor; + if (classDescriptor.hasConstructors() && !ErrorUtils.isError(classDescriptor.getTypeConstructor())) { + trace.getErrorHandler().genericError(specifier.getNode(), "This type has a constructor, and thus must be initialized here"); + } + } + else { + trace.getErrorHandler().genericError(specifier.getNode(), "Only classes may serve as supertypes"); + } - @Override - public void visitJetElement(JetElement element) { - throw new UnsupportedOperationException(element.getText() + " : " + element); } - }); - } + } + + @Override + public void visitDelegationToThisCall(JetDelegatorToThisCall thisCall) { + throw new IllegalStateException("This-calls should be prohibited by the parser"); + } + + @Override + public void visitJetElement(JetElement element) { + throw new UnsupportedOperationException(element.getText() + " : " + element); + } + }); } } @@ -688,7 +698,7 @@ public class TopDownAnalyzer { } } - // Top-level properties + // Top-level properties & properties of objects for (Map.Entry entry : properties.entrySet()) { JetProperty property = entry.getKey(); if (processed.contains(property)) continue; diff --git a/idea/src/org/jetbrains/jet/lang/types/JetStandardLibrary.java b/idea/src/org/jetbrains/jet/lang/types/JetStandardLibrary.java index 8f390d1a342..06925c0d5da 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetStandardLibrary.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetStandardLibrary.java @@ -27,7 +27,9 @@ public class JetStandardLibrary { // TODO : consider releasing this memory private static final Map standardLibraryCache = new HashMap(); - public static JetStandardLibrary getJetStandardLibrary(@NotNull Project project) { + + // TODO : double checked locking + synchronized public static JetStandardLibrary getJetStandardLibrary(@NotNull Project project) { JetStandardLibrary standardLibrary = standardLibraryCache.get(project); if (standardLibrary == null) { standardLibrary = new JetStandardLibrary(project); diff --git a/idea/testData/checker/Objects.jet b/idea/testData/checker/Objects.jet new file mode 100644 index 00000000000..145260bcb84 --- /dev/null +++ b/idea/testData/checker/Objects.jet @@ -0,0 +1,59 @@ +namespace toplevelObjectDeclarations { + class Foo(y : Int) { + virtual fun foo() : Int = 1 + } + + class T : Foo {} + + object A : Foo { + val x : Int = 2 + + fun test() { + return x + foo() + } + } + + object B : A {} + + val x = A.foo() + + val y = object : Foo(x) { + { + x + 12 + } + + override fun foo() : Int = 1 + } + + val z = y.foo() +} + +namespace nestedObejcts { + object A { + val b = B + val d = A.B.A + + object B { + val a = A + val e = B.A + + object A { + val a = A + val b = B + val x = nestedObejcts.A.B.A + val y = this@A + } + } + + } + object B { + val b = B + val c = A.B + } + + val a = A + val b = B + val c = A.B + val d = A.B.A + val e = B.A.B +} diff --git a/idea/testData/resolve/NestedObjects.jet b/idea/testData/resolve/NestedObjects.jet new file mode 100644 index 00000000000..fa005f5c703 --- /dev/null +++ b/idea/testData/resolve/NestedObjects.jet @@ -0,0 +1,28 @@ +~ns~namespace nestedObjects { + object ~A~A { + val b = `A.B`B + val d = `A`A.`A.B`B.`A.B.A`A + + object ~A.B~B { + val a = `A.B.A`A + val e = `A.B`B.`A.B.A`A + + object ~A.B.A~A { + val a = `A.B.A`A + val b = `A.B`B + val x = `ns`nestedObjects.`A`A.`A.B`B.`A.B.A`A + } + } + + } + object ~B~B { + val b = `B`B + val c = `A`A.`A.B`B + } + + val a = `A`A + val b = `B`B + val c = `A`A.`A.B`B + val d = A.B.`A.B.A`A + val e = B.`!`A.B +} diff --git a/idea/testData/resolve/Objects.jet b/idea/testData/resolve/Objects.jet new file mode 100644 index 00000000000..92bbae39532 --- /dev/null +++ b/idea/testData/resolve/Objects.jet @@ -0,0 +1,28 @@ +namespace toplevelObjectDeclarations { + class Foo(y : Int) { + ~foo()~virtual fun foo() : Int = 1 + } + + object ~A~A : Foo(0) { + ~x~val x : Int = 2 + + fun test() { + return `x`x + `foo()`foo() + } + } + + object B : `!`A {} + + ~ns.x~val x = `A`A.`foo()`foo() + + ~y~val y = object : Foo(`ns.x`x) { + { + `ns.x`x + 12 + } + + ~y.foo()~override fun foo() : Int = 1 + } + + val z = `y`y.`y.foo()`foo() +} +