From 1d948b01539322d7e544bf63ed108fb265c03a4e Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Tue, 24 May 2011 19:07:53 +0400 Subject: [PATCH] JET-49 Resolve extension functions with generic receivers + Error report fixed for 'new java.util.List' --- .../org/jetbrains/jet/lang/psi/JetCall.java | 3 ++ .../jet/lang/psi/JetCallExpression.java | 6 +++ .../jet/lang/psi/JetDelegatorToSuperCall.java | 6 +++ .../jet/lang/psi/JetDelegatorToThisCall.java | 6 +++ .../jet/lang/psi/JetNewExpression.java | 6 +++ .../jet/lang/resolve/OverloadResolver.java | 29 +++++++++++--- .../jet/lang/resolve/ScopeWithReceiver.java | 29 +++++++------- .../jet/lang/types/JetTypeInferrer.java | 14 +++++-- .../jet/resolve/DescriptorRenderer.java | 35 ++++++++++++----- idea/testData/checker/ExtensionFunctions.jet | 38 ++++++++++++++++++- idea/testData/checker/ResolveToJava.jet | 2 + idea/testData/resolve/ExtensionFunctions.jet | 4 +- 12 files changed, 141 insertions(+), 37 deletions(-) diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetCall.java b/idea/src/org/jetbrains/jet/lang/psi/JetCall.java index 9b8b625b805..f9721c838e1 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetCall.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetCall.java @@ -17,4 +17,7 @@ public interface JetCall { @NotNull List getFunctionLiteralArguments(); + + @NotNull + JetElement asElement(); } diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetCallExpression.java b/idea/src/org/jetbrains/jet/lang/psi/JetCallExpression.java index 0d7cbf51015..9315f82655f 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetCallExpression.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetCallExpression.java @@ -45,6 +45,12 @@ public class JetCallExpression extends JetExpression implements JetCall { return findChildrenByType(JetNodeTypes.FUNCTION_LITERAL); } + @NotNull + @Override + public JetElement asElement() { + return this; + } + @Override @NotNull public List getValueArguments() { diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetDelegatorToSuperCall.java b/idea/src/org/jetbrains/jet/lang/psi/JetDelegatorToSuperCall.java index 97a9c3f4725..a37f1c57e48 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetDelegatorToSuperCall.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetDelegatorToSuperCall.java @@ -37,4 +37,10 @@ public class JetDelegatorToSuperCall extends JetDelegationSpecifier implements J public List getFunctionLiteralArguments() { return Collections.emptyList(); } + + @NotNull + @Override + public JetElement asElement() { + return this; + } } diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetDelegatorToThisCall.java b/idea/src/org/jetbrains/jet/lang/psi/JetDelegatorToThisCall.java index dc6ecf53252..0373cc0c7ec 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetDelegatorToThisCall.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetDelegatorToThisCall.java @@ -39,6 +39,12 @@ public class JetDelegatorToThisCall extends JetDelegationSpecifier implements Je return Collections.emptyList(); } + @NotNull + @Override + public JetElement asElement() { + return this; + } + public JetReferenceExpression getThisReference() { return findChildByClass(JetThisReferenceExpression.class); } diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetNewExpression.java b/idea/src/org/jetbrains/jet/lang/psi/JetNewExpression.java index 704a6006bff..c2eb0ea4c8b 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetNewExpression.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetNewExpression.java @@ -45,4 +45,10 @@ public class JetNewExpression extends JetExpression implements JetCall { return findChildrenByType(JetNodeTypes.FUNCTION_LITERAL); } + @NotNull + @Override + public JetElement asElement() { + return this; + } + } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/OverloadResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/OverloadResolver.java index a5eecae34a7..3f461a35e75 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/OverloadResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/OverloadResolver.java @@ -5,9 +5,13 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.descriptors.FunctionGroup; import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; -import org.jetbrains.jet.lang.types.*; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.JetTypeChecker; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; /** * @author abreslav @@ -21,17 +25,17 @@ public class OverloadResolver { } @NotNull - public OverloadDomain getOverloadDomain(JetType receiverType, @NotNull JetScope outerScope, @NotNull String name) { + public OverloadDomain getOverloadDomain(@Nullable JetType receiverType, @NotNull JetScope outerScope, @NotNull String name) { // TODO : extension lookup JetScope scope = receiverType == null ? outerScope : new ScopeWithReceiver(outerScope, receiverType, typeChecker); final FunctionGroup functionGroup = scope.getFunctionGroup(name); - return getOverloadDomain(functionGroup); + return getOverloadDomain(receiverType, functionGroup); } @NotNull - public OverloadDomain getOverloadDomain(@NotNull final FunctionGroup functionGroup) { + public OverloadDomain getOverloadDomain(@Nullable final JetType receiverType, @NotNull final FunctionGroup functionGroup) { if (functionGroup.isEmpty()) { return OverloadDomain.EMPTY; } @@ -56,6 +60,21 @@ public class OverloadResolver { // ASSERT: type arguments are figured out and substituted by this time!!! assert descriptor.getTypeParameters().isEmpty(); + if (receiverType != null) { + // ASSERT : either the receiver in not present or we are in a scope with no top-level functions + final JetType functionReceiverType = descriptor.getReceiverType(); +// final ClassDescriptor containingDeclaration = (ClassDescriptor) descriptor.getContainingDeclaration(); +// assert functionReceiverType != null || containingDeclaration != null && +// containingDeclaration.getTypeConstructor().equals(receiverType.getConstructor()); + + if (functionReceiverType != null && !typeChecker.isSubtypeOf(receiverType, functionReceiverType)) { + continue; + } + } + else if (descriptor.getReceiverType() != null) { + continue; + } + List parameters = descriptor.getUnsubstitutedValueParameters(); if (parameters.size() >= positionedValueArgumentTypes.size()) { // possibly, some default values diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ScopeWithReceiver.java b/idea/src/org/jetbrains/jet/lang/resolve/ScopeWithReceiver.java index 20af13963f4..f888b0ff26e 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ScopeWithReceiver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ScopeWithReceiver.java @@ -1,8 +1,6 @@ package org.jetbrains.jet.lang.resolve; -import com.google.common.base.Function; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.JetTypeChecker; @@ -27,19 +25,20 @@ public class ScopeWithReceiver extends JetScopeImpl { public FunctionGroup getFunctionGroup(@NotNull String name) { FunctionGroup functionGroup = receiverType.getMemberScope().getFunctionGroup(name); if (functionGroup.isEmpty()) { - return FunctionDescriptorUtil.filteredFunctionGroup(outerScope.getFunctionGroup(name), - new Function() { - @Override - public Boolean apply(@Nullable FunctionDescriptor functionDescriptor) { - if (functionDescriptor == null) return false; - JetType functionReceiverType = functionDescriptor.getReceiverType(); - if (functionReceiverType == null) { - return false; - } - // TODO : in case of inferred type arguments, substitute the receiver type first - return typeChecker.isSubtypeOf(receiverType, functionReceiverType); - } - }); + return outerScope.getFunctionGroup(name); +// return FunctionDescriptorUtil.filteredFunctionGroup(outerScope.getFunctionGroup(name), +// new Function() { +// @Override +// public Boolean apply(@Nullable FunctionDescriptor functionDescriptor) { +// if (functionDescriptor == null) return false; +// JetType functionReceiverType = functionDescriptor.getReceiverType(); +// if (functionReceiverType == null) { +// return false; +// } +// // TODO : in case of inferred type arguments, substitute the receiver type first +// return typeChecker.isSubtypeOf(receiverType, functionReceiverType); +// } +// }); } return functionGroup; // TODO } diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index 1dc68d9e3dd..e76e29cfdd3 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -128,6 +128,7 @@ public class JetTypeInferrer { } private OverloadDomain getOverloadDomain( + @Nullable final JetType receiverType, @NotNull final JetScope scope, @NotNull JetExpression calleeExpression, @Nullable PsiElement argumentList @@ -150,6 +151,8 @@ public class JetTypeInferrer { @Override public void visitQualifiedExpression(JetQualifiedExpression expression) { + trace.getErrorHandler().genericError(expression.getNode(), "Unsupported [JetTypeInferrer]"); + // . or ?. JetType receiverType = getType(scope, expression.getReceiverExpression(), false); checkNullSafety(receiverType, expression.getOperationTokenNode()); @@ -173,7 +176,7 @@ public class JetTypeInferrer { // a -- create a hierarchical lookup domain for this.a String referencedName = expression.getReferencedName(); if (referencedName != null) { - result[0] = semanticServices.getOverloadResolver().getOverloadDomain(null, scope, referencedName); + result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName); reference[0] = expression; } } @@ -539,7 +542,7 @@ public class JetTypeInferrer { } FunctionGroup constructors = classDescriptor.getConstructors(projectionsStripped); - OverloadDomain constructorsOverloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(constructors); + OverloadDomain constructorsOverloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(null, constructors); JetType constructorReturnedType = resolveOverloads( scope, wrapForTracing(constructorsOverloadDomain, referenceExpression, call.getValueArgumentList(), false), @@ -555,6 +558,9 @@ public class JetTypeInferrer { if (argumentList != null) { trace.getErrorHandler().genericError(argumentList.getNode(), "Cannot find an overload for these arguments"); } + else { + trace.getErrorHandler().genericError(call.asElement().getNode(), "Cannot find an overload for these arguments"); + } constructorReturnedType = receiverType; } // If no upcast needed: @@ -1391,7 +1397,7 @@ public class JetTypeInferrer { JetScope compositeScope = new ScopeWithReceiver(scope, receiverType, semanticServices.getTypeChecker()); if (selectorExpression instanceof JetCallExpression) { JetCallExpression callExpression = (JetCallExpression) selectorExpression; - OverloadDomain overloadDomain = getOverloadDomain(compositeScope, callExpression.getCalleeExpression(), callExpression.getValueArgumentList()); + OverloadDomain overloadDomain = getOverloadDomain(receiverType, compositeScope, callExpression.getCalleeExpression(), callExpression.getValueArgumentList()); return resolveOverloads(scope, callExpression, overloadDomain); } else if (selectorExpression instanceof JetSimpleNameExpression) { @@ -1407,7 +1413,7 @@ public class JetTypeInferrer { @Override public void visitCallExpression(JetCallExpression expression) { JetExpression calleeExpression = expression.getCalleeExpression(); - OverloadDomain overloadDomain = getOverloadDomain(scope, calleeExpression, expression.getValueArgumentList()); + OverloadDomain overloadDomain = getOverloadDomain(null, scope, calleeExpression, expression.getValueArgumentList()); result = resolveOverloads(scope, expression, overloadDomain); } diff --git a/idea/src/org/jetbrains/jet/resolve/DescriptorRenderer.java b/idea/src/org/jetbrains/jet/resolve/DescriptorRenderer.java index 88fd9e9ba65..bf6596bda56 100644 --- a/idea/src/org/jetbrains/jet/resolve/DescriptorRenderer.java +++ b/idea/src/org/jetbrains/jet/resolve/DescriptorRenderer.java @@ -37,6 +37,11 @@ public class DescriptorRenderer { public String renderKeyword(String keyword) { return "" + keyword + ""; } + + @Override + public String renderMessage(String s) { + return "" + s + ""; + } }; @@ -45,11 +50,6 @@ public class DescriptorRenderer { private final DeclarationDescriptorVisitor rootVisitor = new RenderDeclarationDescriptorVisitor(); private final DeclarationDescriptorVisitor subVisitor = new RenderDeclarationDescriptorVisitor() { - @Override - protected void renderName(DeclarationDescriptor descriptor, StringBuilder stringBuilder) { - stringBuilder.append(descriptor.getName()); - } - @Override public Void visitTypeParameterDescriptor(TypeParameterDescriptor descriptor, StringBuilder builder) { renderTypeParameter(descriptor, builder); @@ -80,10 +80,30 @@ public class DescriptorRenderer { if (declarationDescriptor == null) return lt() + "null>"; StringBuilder stringBuilder = new StringBuilder(); declarationDescriptor.accept(rootVisitor, stringBuilder); + stringBuilder.append(" " + renderMessage("defined in") + " "); + + final DeclarationDescriptor containingDeclaration = declarationDescriptor.getContainingDeclaration(); + if (containingDeclaration != null) { + renderFullyQualifiedName(containingDeclaration, stringBuilder); + } return stringBuilder.toString(); } + public String renderMessage(String s) { + return s; + } + + private void renderFullyQualifiedName(DeclarationDescriptor descriptor, StringBuilder stringBuilder) { + DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); + if (containingDeclaration != null) { + renderFullyQualifiedName(containingDeclaration, stringBuilder); + stringBuilder.append("."); + } + stringBuilder.append(escape(descriptor.getName())); + } + private class RenderDeclarationDescriptorVisitor extends DeclarationDescriptorVisitor { + @Override public Void visitValueParameterDescriptor(ValueParameterDescriptor descriptor, StringBuilder builder) { builder.append(renderKeyword("value-parameter")).append(" "); @@ -217,11 +237,6 @@ public class DescriptorRenderer { } protected void renderName(DeclarationDescriptor descriptor, StringBuilder stringBuilder) { - DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); - if (containingDeclaration != null) { - renderName(containingDeclaration, stringBuilder); - stringBuilder.append("::"); - } stringBuilder.append(escape(descriptor.getName())); } diff --git a/idea/testData/checker/ExtensionFunctions.jet b/idea/testData/checker/ExtensionFunctions.jet index 3958be90ab5..bb877dabcf3 100644 --- a/idea/testData/checker/ExtensionFunctions.jet +++ b/idea/testData/checker/ExtensionFunctions.jet @@ -1,2 +1,38 @@ fun Int?.optint() : Unit -val Int?.optval : Unit \ No newline at end of file +val Int?.optval : Unit + +fun T.foo(x : E, y : A) : T { + y.plus(1) + y plus 1 + y + 1.0 + + this?.minus(this) + + this +} + +class A + +fun A.plus(a : Any) { + + 1.foo() + true.foo() + + 1 +} + +fun A.plus(a : Int) { + 1 +} + +fun T.minus(t : T) : Int = 1 + +fun test() { + val y = 1.abs +} +val Int.abs : Int + get() = if (this > 0) this else -this; + +val T.foo : T + +fun Int.foo() = this \ No newline at end of file diff --git a/idea/testData/checker/ResolveToJava.jet b/idea/testData/checker/ResolveToJava.jet index dba318a2148..5ae9aaba181 100644 --- a/idea/testData/checker/ResolveToJava.jet +++ b/idea/testData/checker/ResolveToJava.jet @@ -20,4 +20,6 @@ fun test(l : java.util.List) { Collections.singleton(1) : Set? Collections.singleton(1.0) + + new List } diff --git a/idea/testData/resolve/ExtensionFunctions.jet b/idea/testData/resolve/ExtensionFunctions.jet index 8418f76d6d5..1570a549f4e 100644 --- a/idea/testData/resolve/ExtensionFunctions.jet +++ b/idea/testData/resolve/ExtensionFunctions.jet @@ -3,7 +3,7 @@ fun <~T~T, ~E~E> `T`T.foo(x : `E`E, y : `A`A) : `T`T { y `+`plus 1 y `+1`+ 1.0 - this?.minus(this) + this?.`-`minus(this) this } @@ -13,7 +13,7 @@ fun <~T~T, ~E~E> `T`T.foo(x : `E`E, y : `A`A) : `T`T { ~+1~fun `A`A.plus(a : Any) { 1.`foo`foo() - true.`!`foo() + true.`foo`foo() 1 }