JET-51 Check generic bounds in method calls
+ A bug in parseCallExpression() heuristics fixed
This commit is contained in:
@@ -26,7 +26,7 @@ public class JetExpressionParsing extends AbstractJetParsing {
|
|||||||
CONTINUE_KEYWORD, OBJECT_KEYWORD, IF_KEYWORD, TRY_KEYWORD, ELSE_KEYWORD, WHILE_KEYWORD, DO_KEYWORD,
|
CONTINUE_KEYWORD, OBJECT_KEYWORD, IF_KEYWORD, TRY_KEYWORD, ELSE_KEYWORD, WHILE_KEYWORD, DO_KEYWORD,
|
||||||
WHEN_KEYWORD, RBRACKET, RBRACE, RPAR, PLUSPLUS, MINUSMINUS, MUL, PLUS, MINUS, EXCL, DIV, PERC, LTEQ,
|
WHEN_KEYWORD, RBRACKET, RBRACE, RPAR, PLUSPLUS, MINUSMINUS, MUL, PLUS, MINUS, EXCL, DIV, PERC, LTEQ,
|
||||||
// TODO GTEQ, foo<bar, baz>=x
|
// TODO GTEQ, foo<bar, baz>=x
|
||||||
EQEQEQ, ARROW, DOUBLE_ARROW, EXCLEQEQEQ, EQEQ, EXCLEQ, ANDAND, OROR, SAFE_ACCESS, ELVIS, QUEST,
|
EQEQEQ, ARROW, DOUBLE_ARROW, EXCLEQEQEQ, EQEQ, EXCLEQ, ANDAND, OROR, SAFE_ACCESS, ELVIS,
|
||||||
SEMICOLON, RANGE, EQ, MULTEQ, DIVEQ, PERCEQ, PLUSEQ, MINUSEQ, NOT_IN, NOT_IS, HASH,
|
SEMICOLON, RANGE, EQ, MULTEQ, DIVEQ, PERCEQ, PLUSEQ, MINUSEQ, NOT_IN, NOT_IS, HASH,
|
||||||
COLON
|
COLON
|
||||||
);
|
);
|
||||||
@@ -398,7 +398,9 @@ public class JetExpressionParsing extends AbstractJetParsing {
|
|||||||
int gtPos = matchTokenStreamPredicate(new FirstBefore(
|
int gtPos = matchTokenStreamPredicate(new FirstBefore(
|
||||||
new At(GT),
|
new At(GT),
|
||||||
new AtSet(TYPE_ARGUMENT_LIST_STOPPERS, TokenSet.create(RPAR, RBRACE, RBRACKET))
|
new AtSet(TYPE_ARGUMENT_LIST_STOPPERS, TokenSet.create(RPAR, RBRACE, RBRACKET))
|
||||||
.or(new AtFirstTokenOfTokens(IDENTIFIER, LPAR))
|
.or(new AtFirstTokenOfTokens(IDENTIFIER, LPAR)
|
||||||
|
// .or(new AtFirstTokenOfTokens(QUEST, IDENTIFIER))
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
@Override
|
@Override
|
||||||
public boolean isTopLevel(int openAngleBrackets, int openBrackets, int openBraces, int openParentheses) {
|
public boolean isTopLevel(int openAngleBrackets, int openBrackets, int openBraces, int openParentheses) {
|
||||||
@@ -412,7 +414,7 @@ public class JetExpressionParsing extends AbstractJetParsing {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (gtPos >= 0) {
|
if (gtPos >= 0) {
|
||||||
myJetParsing.parseTypeArgumentList();
|
myJetParsing.parseTypeArgumentList(gtPos);
|
||||||
if (!myBuilder.newlineBeforeCurrentToken() && at(LPAR)) parseValueArgumentList();
|
if (!myBuilder.newlineBeforeCurrentToken() && at(LPAR)) parseValueArgumentList();
|
||||||
parseCallWithClosure();
|
parseCallWithClosure();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1249,7 +1249,7 @@ public class JetParsing extends AbstractJetParsing {
|
|||||||
expect(IDENTIFIER, "Type name expected", TokenSet.orSet(JetExpressionParsing.EXPRESSION_FIRST, JetExpressionParsing.EXPRESSION_FOLLOW));
|
expect(IDENTIFIER, "Type name expected", TokenSet.orSet(JetExpressionParsing.EXPRESSION_FIRST, JetExpressionParsing.EXPRESSION_FOLLOW));
|
||||||
reference.done(REFERENCE_EXPRESSION);
|
reference.done(REFERENCE_EXPRESSION);
|
||||||
|
|
||||||
parseTypeArgumentList();
|
parseTypeArgumentList(-1);
|
||||||
if (!at(DOT)) {
|
if (!at(DOT)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1268,7 +1268,7 @@ public class JetParsing extends AbstractJetParsing {
|
|||||||
/*
|
/*
|
||||||
* (optionalProjection type){","}
|
* (optionalProjection type){","}
|
||||||
*/
|
*/
|
||||||
public PsiBuilder.Marker parseTypeArgumentList() {
|
public PsiBuilder.Marker parseTypeArgumentList(int expectedGtOffset) {
|
||||||
if (!at(LT)) return null;
|
if (!at(LT)) return null;
|
||||||
|
|
||||||
PsiBuilder.Marker list = mark();
|
PsiBuilder.Marker list = mark();
|
||||||
@@ -1292,6 +1292,14 @@ public class JetParsing extends AbstractJetParsing {
|
|||||||
advance(); // COMMA
|
advance(); // COMMA
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (expectedGtOffset >= 0 && myBuilder.getCurrentOffset() < expectedGtOffset) {
|
||||||
|
final PsiBuilder.Marker error = mark();
|
||||||
|
while (myBuilder.getCurrentOffset() < expectedGtOffset) {
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
error.error("Expecting a '>'");
|
||||||
|
}
|
||||||
|
|
||||||
expect(GT, "Expecting a '>'");
|
expect(GT, "Expecting a '>'");
|
||||||
myBuilder.restoreNewlinesState();
|
myBuilder.restoreNewlinesState();
|
||||||
|
|
||||||
|
|||||||
@@ -63,10 +63,6 @@ public class OverloadResolver {
|
|||||||
if (receiverType != null) {
|
if (receiverType != null) {
|
||||||
// ASSERT : either the receiver in not present or we are in a scope with no top-level functions
|
// ASSERT : either the receiver in not present or we are in a scope with no top-level functions
|
||||||
final JetType functionReceiverType = descriptor.getReceiverType();
|
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)) {
|
if (functionReceiverType != null && !typeChecker.isSubtypeOf(receiverType, functionReceiverType)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,11 +122,19 @@ public class JetTypeInferrer {
|
|||||||
@NotNull List<JetType> argumentTypes,
|
@NotNull List<JetType> argumentTypes,
|
||||||
boolean reportUnresolved) {
|
boolean reportUnresolved) {
|
||||||
OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name);
|
OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name);
|
||||||
|
// No generics. Guaranteed
|
||||||
overloadDomain = wrapForTracing(overloadDomain, reference, null, reportUnresolved);
|
overloadDomain = wrapForTracing(overloadDomain, reference, null, reportUnresolved);
|
||||||
OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(Collections.<JetType>emptyList(), argumentTypes);
|
OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(Collections.<JetType>emptyList(), argumentTypes);
|
||||||
return resolutionResult.isSuccess() ? resolutionResult.getFunctionDescriptor() : null;
|
return resolutionResult.isSuccess() ? resolutionResult.getFunctionDescriptor() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private OverloadResolutionResult resolveNoParametersFunction(@NotNull JetType receiverType, @NotNull JetScope scope, @NotNull String name) {
|
||||||
|
OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name);
|
||||||
|
// No generics. Guaranteed
|
||||||
|
return overloadDomain.getFunctionDescriptorForPositionedArguments(Collections.<JetType>emptyList(), Collections.<JetType>emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
private OverloadDomain getOverloadDomain(
|
private OverloadDomain getOverloadDomain(
|
||||||
@Nullable final JetType receiverType,
|
@Nullable final JetType receiverType,
|
||||||
@NotNull final JetScope scope,
|
@NotNull final JetScope scope,
|
||||||
@@ -163,6 +171,7 @@ public class JetTypeInferrer {
|
|||||||
String referencedName = referenceExpression.getReferencedName();
|
String referencedName = referenceExpression.getReferencedName();
|
||||||
|
|
||||||
if (receiverType != null && referencedName != null) {
|
if (receiverType != null && referencedName != null) {
|
||||||
|
// No generics. Guaranteed
|
||||||
result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName);
|
result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName);
|
||||||
reference[0] = referenceExpression;
|
reference[0] = referenceExpression;
|
||||||
}
|
}
|
||||||
@@ -176,6 +185,7 @@ public class JetTypeInferrer {
|
|||||||
// a -- create a hierarchical lookup domain for this.a
|
// a -- create a hierarchical lookup domain for this.a
|
||||||
String referencedName = expression.getReferencedName();
|
String referencedName = expression.getReferencedName();
|
||||||
if (referencedName != null) {
|
if (referencedName != null) {
|
||||||
|
// No generics. Guaranteed
|
||||||
result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName);
|
result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName);
|
||||||
reference[0] = expression;
|
reference[0] = expression;
|
||||||
}
|
}
|
||||||
@@ -215,12 +225,10 @@ public class JetTypeInferrer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private OverloadDomain wrapForTracing(
|
private OverloadDomain wrapForTracing(
|
||||||
@Nullable final OverloadDomain overloadDomain,
|
@NotNull final OverloadDomain overloadDomain,
|
||||||
final JetReferenceExpression referenceExpression,
|
@NotNull final JetReferenceExpression referenceExpression,
|
||||||
@Nullable final PsiElement argumentList,
|
@Nullable final PsiElement argumentList,
|
||||||
final boolean reportErrors) {
|
final boolean reportErrors) {
|
||||||
if (overloadDomain == null) return OverloadDomain.EMPTY;
|
|
||||||
assert referenceExpression != null;
|
|
||||||
return new OverloadDomain() {
|
return new OverloadDomain() {
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
@@ -400,9 +408,9 @@ public class JetTypeInferrer {
|
|||||||
// 1) ends with a name -> (scope, name) to look up
|
// 1) ends with a name -> (scope, name) to look up
|
||||||
// 2) ends with something else -> just check types
|
// 2) ends with something else -> just check types
|
||||||
|
|
||||||
final List<JetTypeProjection> typeArguments = call.getTypeArguments();
|
final List<JetTypeProjection> jetTypeArguments = call.getTypeArguments();
|
||||||
|
|
||||||
for (JetTypeProjection typeArgument : typeArguments) {
|
for (JetTypeProjection typeArgument : jetTypeArguments) {
|
||||||
if (typeArgument.getProjectionKind() != JetProjectionKind.NONE) {
|
if (typeArgument.getProjectionKind() != JetProjectionKind.NONE) {
|
||||||
trace.getErrorHandler().genericError(typeArgument.getNode(), "Projections are not allowed on type parameters for methods"); // TODO : better positioning
|
trace.getErrorHandler().genericError(typeArgument.getNode(), "Projections are not allowed on type parameters for methods"); // TODO : better positioning
|
||||||
}
|
}
|
||||||
@@ -429,12 +437,12 @@ public class JetTypeInferrer {
|
|||||||
|
|
||||||
// result = overloadDomain.getFunctionDescriptorForNamedArguments(typeArguments, valueArguments, functionLiteralArgument);
|
// result = overloadDomain.getFunctionDescriptorForNamedArguments(typeArguments, valueArguments, functionLiteralArgument);
|
||||||
} else {
|
} else {
|
||||||
List<JetType> types = new ArrayList<JetType>();
|
List<JetType> typeArguments = new ArrayList<JetType>();
|
||||||
for (JetTypeProjection projection : typeArguments) {
|
for (JetTypeProjection projection : jetTypeArguments) {
|
||||||
// TODO : check that there's no projection
|
// TODO : check that there's no projection
|
||||||
JetTypeReference typeReference = projection.getTypeReference();
|
JetTypeReference typeReference = projection.getTypeReference();
|
||||||
if (typeReference != null) {
|
if (typeReference != null) {
|
||||||
types.add(typeResolver.resolveType(scope, typeReference));
|
typeArguments.add(typeResolver.resolveType(scope, typeReference));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,9 +461,20 @@ public class JetTypeInferrer {
|
|||||||
valueArgumentTypes.add(safeGetType(scope, valueArgument, false));
|
valueArgumentTypes.add(safeGetType(scope, valueArgument, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(types, valueArgumentTypes);
|
OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(typeArguments, valueArgumentTypes);
|
||||||
if (resolutionResult.isSuccess()) {
|
if (resolutionResult.isSuccess()) {
|
||||||
return resolutionResult.getFunctionDescriptor().getUnsubstitutedReturnType();
|
final FunctionDescriptor functionDescriptor = resolutionResult.getFunctionDescriptor();
|
||||||
|
|
||||||
|
List<TypeParameterDescriptor> typeParameters = functionDescriptor.getOriginal().getTypeParameters();
|
||||||
|
for (int i = 0, typeParametersSize = typeParameters.size(); i < typeParametersSize; i++) {
|
||||||
|
TypeParameterDescriptor typeParameterDescriptor = typeParameters.get(i);
|
||||||
|
final JetType typeArgument = typeArguments.get(i);
|
||||||
|
if (!semanticServices.getTypeChecker().isSubtypeOf(typeArgument, typeParameterDescriptor.getBoundsAsType())) {
|
||||||
|
trace.getErrorHandler().genericError(jetTypeArguments.get(i).getNode(), "Bound of the type parameter " + DescriptorRenderer.TEXT.render(typeParameterDescriptor) + " is not respected by the type " + typeArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return functionDescriptor.getUnsubstitutedReturnType();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -1340,12 +1359,6 @@ public class JetTypeInferrer {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private OverloadResolutionResult resolveNoParametersFunction(@NotNull JetType receiverType, @NotNull JetScope scope, @NotNull String name) {
|
|
||||||
OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name);
|
|
||||||
return overloadDomain.getFunctionDescriptorForPositionedArguments(Collections.<JetType>emptyList(), Collections.<JetType>emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitNewExpression(JetNewExpression expression) {
|
public void visitNewExpression(JetNewExpression expression) {
|
||||||
// TODO : type argument inference
|
// TODO : type argument inference
|
||||||
@@ -1386,11 +1399,14 @@ public class JetTypeInferrer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JetType getCallExpressionType(@Nullable JetType receiverType, @NotNull JetCallExpression callExpression) {
|
||||||
|
OverloadDomain overloadDomain = getOverloadDomain(receiverType, scope, callExpression.getCalleeExpression(), callExpression.getValueArgumentList());
|
||||||
|
return resolveCall(scope, overloadDomain, callExpression);
|
||||||
|
}
|
||||||
|
|
||||||
private JetType getSelectorReturnType(JetType receiverType, JetExpression selectorExpression) {
|
private JetType getSelectorReturnType(JetType receiverType, JetExpression selectorExpression) {
|
||||||
if (selectorExpression instanceof JetCallExpression) {
|
if (selectorExpression instanceof JetCallExpression) {
|
||||||
JetCallExpression callExpression = (JetCallExpression) selectorExpression;
|
return getCallExpressionType(receiverType, (JetCallExpression) selectorExpression);
|
||||||
OverloadDomain overloadDomain = getOverloadDomain(receiverType, scope, callExpression.getCalleeExpression(), callExpression.getValueArgumentList());
|
|
||||||
return resolveCall(scope, overloadDomain, callExpression);
|
|
||||||
}
|
}
|
||||||
else if (selectorExpression instanceof JetSimpleNameExpression) {
|
else if (selectorExpression instanceof JetSimpleNameExpression) {
|
||||||
JetScope compositeScope = new ScopeWithReceiver(scope, receiverType, semanticServices.getTypeChecker());
|
JetScope compositeScope = new ScopeWithReceiver(scope, receiverType, semanticServices.getTypeChecker());
|
||||||
@@ -1405,9 +1421,7 @@ public class JetTypeInferrer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitCallExpression(JetCallExpression expression) {
|
public void visitCallExpression(JetCallExpression expression) {
|
||||||
JetExpression calleeExpression = expression.getCalleeExpression();
|
result = getCallExpressionType(null, expression);
|
||||||
OverloadDomain overloadDomain = getOverloadDomain(null, scope, calleeExpression, expression.getValueArgumentList());
|
|
||||||
result = resolveCall(scope, overloadDomain, expression);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -23,3 +23,18 @@ namespace boundsWithSubstitutors {
|
|||||||
|
|
||||||
val x : {(B<<error>Char</error>>) : B<<error>Any</error>>}
|
val x : {(B<<error>Char</error>>) : B<<error>Any</error>>}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo<<error>Int?</error>>()
|
||||||
|
foo<Int>()
|
||||||
|
bar<Int?>()
|
||||||
|
bar<Int>()
|
||||||
|
bar<<error>Double?</error>>()
|
||||||
|
bar<<error>Double</error>>()
|
||||||
|
1.buzz<<error>Double</error>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo<T : Any>() {}
|
||||||
|
fun bar<T : Int?>() {}
|
||||||
|
fun <T : Int> Int.buzz() : Unit {}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
fun foo() {
|
fun foo() {
|
||||||
|
foo<Int?>()
|
||||||
|
fooo<Double?addddd>()
|
||||||
dd<(Int, Int, Int)>(if (true) (1, 1, 1) else (2, 2, 2))
|
dd<(Int, Int, Int)>(if (true) (1, 1, 1) else (2, 2, 2))
|
||||||
foo(bar<a, b, c>(d))
|
foo(bar<a, b, c>(d))
|
||||||
foo(bar<a, b+1, c>(d))
|
foo(bar<a, b+1, c>(d))
|
||||||
|
|||||||
@@ -11,6 +11,42 @@ JetFile: TypeExpressionAmbiguities_ERR.jet
|
|||||||
BLOCK
|
BLOCK
|
||||||
PsiElement(LBRACE)('{')
|
PsiElement(LBRACE)('{')
|
||||||
PsiWhiteSpace('\n ')
|
PsiWhiteSpace('\n ')
|
||||||
|
CALL_EXPRESSION
|
||||||
|
REFERENCE_EXPRESSION
|
||||||
|
PsiElement(IDENTIFIER)('foo')
|
||||||
|
TYPE_ARGUMENT_LIST
|
||||||
|
PsiElement(LT)('<')
|
||||||
|
TYPE_PROJECTION
|
||||||
|
TYPE_REFERENCE
|
||||||
|
NULLABLE_TYPE
|
||||||
|
USER_TYPE
|
||||||
|
REFERENCE_EXPRESSION
|
||||||
|
PsiElement(IDENTIFIER)('Int')
|
||||||
|
PsiElement(QUEST)('?')
|
||||||
|
PsiElement(GT)('>')
|
||||||
|
VALUE_ARGUMENT_LIST
|
||||||
|
PsiElement(LPAR)('(')
|
||||||
|
PsiElement(RPAR)(')')
|
||||||
|
PsiWhiteSpace('\n ')
|
||||||
|
CALL_EXPRESSION
|
||||||
|
REFERENCE_EXPRESSION
|
||||||
|
PsiElement(IDENTIFIER)('fooo')
|
||||||
|
TYPE_ARGUMENT_LIST
|
||||||
|
PsiElement(LT)('<')
|
||||||
|
TYPE_PROJECTION
|
||||||
|
TYPE_REFERENCE
|
||||||
|
NULLABLE_TYPE
|
||||||
|
USER_TYPE
|
||||||
|
REFERENCE_EXPRESSION
|
||||||
|
PsiElement(IDENTIFIER)('Double')
|
||||||
|
PsiElement(QUEST)('?')
|
||||||
|
PsiErrorElement:Expecting a '>'
|
||||||
|
PsiElement(IDENTIFIER)('addddd')
|
||||||
|
PsiElement(GT)('>')
|
||||||
|
VALUE_ARGUMENT_LIST
|
||||||
|
PsiElement(LPAR)('(')
|
||||||
|
PsiElement(RPAR)(')')
|
||||||
|
PsiWhiteSpace('\n ')
|
||||||
CALL_EXPRESSION
|
CALL_EXPRESSION
|
||||||
REFERENCE_EXPRESSION
|
REFERENCE_EXPRESSION
|
||||||
PsiElement(IDENTIFIER)('dd')
|
PsiElement(IDENTIFIER)('dd')
|
||||||
|
|||||||
Reference in New Issue
Block a user