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,
|
||||
WHEN_KEYWORD, RBRACKET, RBRACE, RPAR, PLUSPLUS, MINUSMINUS, MUL, PLUS, MINUS, EXCL, DIV, PERC, LTEQ,
|
||||
// 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,
|
||||
COLON
|
||||
);
|
||||
@@ -398,7 +398,9 @@ public class JetExpressionParsing extends AbstractJetParsing {
|
||||
int gtPos = matchTokenStreamPredicate(new FirstBefore(
|
||||
new At(GT),
|
||||
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
|
||||
public boolean isTopLevel(int openAngleBrackets, int openBrackets, int openBraces, int openParentheses) {
|
||||
@@ -412,7 +414,7 @@ public class JetExpressionParsing extends AbstractJetParsing {
|
||||
}
|
||||
});
|
||||
if (gtPos >= 0) {
|
||||
myJetParsing.parseTypeArgumentList();
|
||||
myJetParsing.parseTypeArgumentList(gtPos);
|
||||
if (!myBuilder.newlineBeforeCurrentToken() && at(LPAR)) parseValueArgumentList();
|
||||
parseCallWithClosure();
|
||||
}
|
||||
|
||||
@@ -1249,7 +1249,7 @@ public class JetParsing extends AbstractJetParsing {
|
||||
expect(IDENTIFIER, "Type name expected", TokenSet.orSet(JetExpressionParsing.EXPRESSION_FIRST, JetExpressionParsing.EXPRESSION_FOLLOW));
|
||||
reference.done(REFERENCE_EXPRESSION);
|
||||
|
||||
parseTypeArgumentList();
|
||||
parseTypeArgumentList(-1);
|
||||
if (!at(DOT)) {
|
||||
break;
|
||||
}
|
||||
@@ -1268,7 +1268,7 @@ public class JetParsing extends AbstractJetParsing {
|
||||
/*
|
||||
* (optionalProjection type){","}
|
||||
*/
|
||||
public PsiBuilder.Marker parseTypeArgumentList() {
|
||||
public PsiBuilder.Marker parseTypeArgumentList(int expectedGtOffset) {
|
||||
if (!at(LT)) return null;
|
||||
|
||||
PsiBuilder.Marker list = mark();
|
||||
@@ -1292,6 +1292,14 @@ public class JetParsing extends AbstractJetParsing {
|
||||
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 '>'");
|
||||
myBuilder.restoreNewlinesState();
|
||||
|
||||
|
||||
@@ -63,10 +63,6 @@ public class OverloadResolver {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -122,11 +122,19 @@ public class JetTypeInferrer {
|
||||
@NotNull List<JetType> argumentTypes,
|
||||
boolean reportUnresolved) {
|
||||
OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name);
|
||||
// No generics. Guaranteed
|
||||
overloadDomain = wrapForTracing(overloadDomain, reference, null, reportUnresolved);
|
||||
OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(Collections.<JetType>emptyList(), argumentTypes);
|
||||
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(
|
||||
@Nullable final JetType receiverType,
|
||||
@NotNull final JetScope scope,
|
||||
@@ -163,6 +171,7 @@ public class JetTypeInferrer {
|
||||
String referencedName = referenceExpression.getReferencedName();
|
||||
|
||||
if (receiverType != null && referencedName != null) {
|
||||
// No generics. Guaranteed
|
||||
result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName);
|
||||
reference[0] = referenceExpression;
|
||||
}
|
||||
@@ -176,6 +185,7 @@ public class JetTypeInferrer {
|
||||
// a -- create a hierarchical lookup domain for this.a
|
||||
String referencedName = expression.getReferencedName();
|
||||
if (referencedName != null) {
|
||||
// No generics. Guaranteed
|
||||
result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName);
|
||||
reference[0] = expression;
|
||||
}
|
||||
@@ -215,12 +225,10 @@ public class JetTypeInferrer {
|
||||
}
|
||||
|
||||
private OverloadDomain wrapForTracing(
|
||||
@Nullable final OverloadDomain overloadDomain,
|
||||
final JetReferenceExpression referenceExpression,
|
||||
@NotNull final OverloadDomain overloadDomain,
|
||||
@NotNull final JetReferenceExpression referenceExpression,
|
||||
@Nullable final PsiElement argumentList,
|
||||
final boolean reportErrors) {
|
||||
if (overloadDomain == null) return OverloadDomain.EMPTY;
|
||||
assert referenceExpression != null;
|
||||
return new OverloadDomain() {
|
||||
@NotNull
|
||||
@Override
|
||||
@@ -400,9 +408,9 @@ public class JetTypeInferrer {
|
||||
// 1) ends with a name -> (scope, name) to look up
|
||||
// 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) {
|
||||
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);
|
||||
} else {
|
||||
List<JetType> types = new ArrayList<JetType>();
|
||||
for (JetTypeProjection projection : typeArguments) {
|
||||
List<JetType> typeArguments = new ArrayList<JetType>();
|
||||
for (JetTypeProjection projection : jetTypeArguments) {
|
||||
// TODO : check that there's no projection
|
||||
JetTypeReference typeReference = projection.getTypeReference();
|
||||
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));
|
||||
}
|
||||
|
||||
OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(types, valueArgumentTypes);
|
||||
OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(typeArguments, valueArgumentTypes);
|
||||
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;
|
||||
@@ -1340,12 +1359,6 @@ public class JetTypeInferrer {
|
||||
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
|
||||
public void visitNewExpression(JetNewExpression expression) {
|
||||
// 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) {
|
||||
if (selectorExpression instanceof JetCallExpression) {
|
||||
JetCallExpression callExpression = (JetCallExpression) selectorExpression;
|
||||
OverloadDomain overloadDomain = getOverloadDomain(receiverType, scope, callExpression.getCalleeExpression(), callExpression.getValueArgumentList());
|
||||
return resolveCall(scope, overloadDomain, callExpression);
|
||||
return getCallExpressionType(receiverType, (JetCallExpression) selectorExpression);
|
||||
}
|
||||
else if (selectorExpression instanceof JetSimpleNameExpression) {
|
||||
JetScope compositeScope = new ScopeWithReceiver(scope, receiverType, semanticServices.getTypeChecker());
|
||||
@@ -1405,9 +1421,7 @@ public class JetTypeInferrer {
|
||||
|
||||
@Override
|
||||
public void visitCallExpression(JetCallExpression expression) {
|
||||
JetExpression calleeExpression = expression.getCalleeExpression();
|
||||
OverloadDomain overloadDomain = getOverloadDomain(null, scope, calleeExpression, expression.getValueArgumentList());
|
||||
result = resolveCall(scope, overloadDomain, expression);
|
||||
result = getCallExpressionType(null, expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,3 +23,18 @@ namespace boundsWithSubstitutors {
|
||||
|
||||
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() {
|
||||
foo<Int?>()
|
||||
fooo<Double?addddd>()
|
||||
dd<(Int, Int, Int)>(if (true) (1, 1, 1) else (2, 2, 2))
|
||||
foo(bar<a, b, c>(d))
|
||||
foo(bar<a, b+1, c>(d))
|
||||
|
||||
@@ -11,6 +11,42 @@ JetFile: TypeExpressionAmbiguities_ERR.jet
|
||||
BLOCK
|
||||
PsiElement(LBRACE)('{')
|
||||
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
|
||||
REFERENCE_EXPRESSION
|
||||
PsiElement(IDENTIFIER)('dd')
|
||||
|
||||
Reference in New Issue
Block a user