Nullability properties of receivers checked

This commit is contained in:
Andrey Breslav
2011-10-12 16:17:21 +04:00
parent c53754b3e8
commit 7427f3b3d6
11 changed files with 239 additions and 98 deletions
@@ -27,8 +27,8 @@ public class JavaClassMembersScope implements JetScope {
private final Map<String, ClassifierDescriptor> classifiers = Maps.newHashMap();
private Collection<DeclarationDescriptor> allDescriptors;
public JavaClassMembersScope(@NotNull DeclarationDescriptor classDescriptor, PsiClass psiClass, JavaSemanticServices semanticServices, boolean staticMembers) {
this.containingDeclaration = classDescriptor;
public JavaClassMembersScope(@NotNull DeclarationDescriptor classOrNamespaceDescriptor, PsiClass psiClass, JavaSemanticServices semanticServices, boolean staticMembers) {
this.containingDeclaration = classOrNamespaceDescriptor;
this.psiClass = psiClass;
this.semanticServices = semanticServices;
this.staticMembers = staticMembers;
@@ -108,7 +108,6 @@ public class JavaDescriptorResolver {
name,
typeParameters,
supertypes
));
classDescriptor.setModality(Modality.convertFromFlags(
psiClass.hasModifierProperty(PsiModifier.ABSTRACT) || psiClass.isInterface(),
@@ -11,7 +11,6 @@ import java.util.Set;
* @author abreslav
*/
public class JavaPackageScope extends JetScopeImpl {
private final JavaSemanticServices semanticServices;
private final DeclarationDescriptor containingDescriptor;
private final String packagePrefix;
@@ -1,6 +1,7 @@
package org.jetbrains.jet.lang.psi;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
@@ -39,4 +40,7 @@ public interface Call {
@NotNull
ASTNode getCallNode();
@Nullable
PsiElement getCallElement();
}
@@ -0,0 +1,35 @@
package org.jetbrains.jet.lang.resolve.calls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
import org.jetbrains.jet.lang.types.DataFlowInfo;
import java.util.Collections;
import java.util.List;
/**
* @author abreslav
*/
public interface AutoCastService {
AutoCastService NO_AUTO_CASTS = new AutoCastService() {
@Override
public DataFlowInfo getDataFlowInfo() {
return DataFlowInfo.getEmpty();
}
@Override
public boolean isNotNull(@NotNull ReceiverDescriptor receiver) {
return !receiver.getType().isNullable();
}
@Override
public List<ReceiverDescriptor> getVariantsForReceiver(ReceiverDescriptor receiverDescriptor) {
return Collections.singletonList(receiverDescriptor);
}
};
List<ReceiverDescriptor> getVariantsForReceiver(ReceiverDescriptor receiverDescriptor);
DataFlowInfo getDataFlowInfo();
boolean isNotNull(@NotNull ReceiverDescriptor receiver);
}
@@ -0,0 +1,42 @@
package org.jetbrains.jet.lang.resolve.calls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
import org.jetbrains.jet.lang.types.DataFlowInfo;
import java.util.List;
/**
* @author abreslav
*/
public class AutoCastServiceImpl implements AutoCastService {
private final DataFlowInfo dataFlowInfo;
private final BindingContext bindingContext;
AutoCastServiceImpl(DataFlowInfo dataFlowInfo, BindingContext bindingContext) {
this.dataFlowInfo = dataFlowInfo;
this.bindingContext = bindingContext;
}
@Override
public List<ReceiverDescriptor> getVariantsForReceiver(ReceiverDescriptor receiverDescriptor) {
return AutoCastUtils.getAutoCastVariants(bindingContext, dataFlowInfo, receiverDescriptor);
}
@Override
public DataFlowInfo getDataFlowInfo() {
return dataFlowInfo;
}
@Override
public boolean isNotNull(@NotNull ReceiverDescriptor receiver) {
if (!receiver.getType().isNullable()) return true;
List<ReceiverDescriptor> autoCastVariants = AutoCastUtils.getAutoCastVariants(bindingContext, dataFlowInfo, receiver);
for (ReceiverDescriptor autoCastVariant : autoCastVariants) {
if (!autoCastVariant.getType().isNullable()) return true;
}
return false;
}
}
@@ -4,6 +4,7 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.JetSemanticServices;
@@ -47,16 +48,20 @@ public class CallResolver {
public VariableDescriptor resolveSimpleProperty(
@NotNull BindingTrace trace,
@NotNull JetScope scope,
@NotNull ReceiverDescriptor receiver,
@NotNull final JetSimpleNameExpression nameExpression,
// @NotNull ReceiverDescriptor receiver,
// @NotNull final JetSimpleNameExpression nameExpression,
@NotNull Call call,
@NotNull JetType expectedType) {
// Call call = CallMaker.makePropertyCall(receiver, null, nameExpression);
JetExpression calleeExpression = call.getCalleeExpression();
assert calleeExpression instanceof JetSimpleNameExpression;
JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) calleeExpression;
String referencedName = nameExpression.getReferencedName();
if (referencedName == null) {
return null;
}
Call call = CallMaker.makePropertyCall(receiver, null, nameExpression);
List<ResolutionTask<VariableDescriptor>> prioritizedTasks = TaskPrioritizers.PROPERTY_TASK_PRIORITIZER.computePrioritizedTasks(scope, call, referencedName, trace.getBindingContext(), dataFlowInfo);
return resolveCallToDescriptor(trace, scope, call, nameExpression.getNode(), expectedType, prioritizedTasks, nameExpression);
return resolveCallToDescriptor(trace, scope, call, expectedType, prioritizedTasks, nameExpression);
}
@Nullable
@@ -79,7 +84,7 @@ public class CallResolver {
@NotNull String name,
@NotNull JetType expectedType) {
List<ResolutionTask<FunctionDescriptor>> tasks = TaskPrioritizers.FUNCTION_TASK_PRIORITIZER.computePrioritizedTasks(scope, call, name, trace.getBindingContext(), dataFlowInfo);
return resolveCallToDescriptor(trace, scope, call, functionReference.getNode(), expectedType, tasks, functionReference);
return resolveCallToDescriptor(trace, scope, call, expectedType, tasks, functionReference);
}
@Nullable
@@ -171,7 +176,7 @@ public class CallResolver {
}
}
return resolveCallToDescriptor(trace, scope, call, call.getCallNode(), expectedType, prioritizedTasks, functionReference);
return resolveCallToDescriptor(trace, scope, call, expectedType, prioritizedTasks, functionReference);
}
private FunctionDescriptor checkArgumentTypesAndFail(BindingTrace trace, JetScope scope, Call call) {
@@ -184,11 +189,10 @@ public class CallResolver {
@NotNull BindingTrace trace,
@NotNull JetScope scope,
@NotNull final Call call,
@NotNull final ASTNode callNode,
@NotNull JetType expectedType,
@NotNull final List<ResolutionTask<D>> prioritizedTasks, // high to low priority
@NotNull final JetReferenceExpression reference) {
ResolvedCall<D> resolvedCall = doResolveCall(trace, scope, call, callNode, expectedType, prioritizedTasks, reference);
ResolvedCall<D> resolvedCall = doResolveCall(trace, scope, call, expectedType, prioritizedTasks, reference);
return resolvedCall == null ? null : resolvedCall.getResultingDescriptor();
}
@@ -197,7 +201,6 @@ public class CallResolver {
@NotNull BindingTrace trace,
@NotNull JetScope scope,
@NotNull final Call call,
@NotNull final ASTNode callNode,
@NotNull JetType expectedType,
@NotNull final List<ResolutionTask<D>> prioritizedTasks, // high to low priority
@NotNull final JetReferenceExpression reference) {
@@ -268,22 +271,22 @@ public class CallResolver {
@Override
public <D extends CallableDescriptor> void ambiguity(@NotNull BindingTrace trace, @NotNull Set<ResolvedCall<D>> descriptors) {
trace.report(OVERLOAD_RESOLUTION_AMBIGUITY.on(callNode, descriptors));
trace.report(OVERLOAD_RESOLUTION_AMBIGUITY.on(call.getCallNode(), descriptors));
}
@Override
public <D extends CallableDescriptor> void noneApplicable(@NotNull BindingTrace trace, @NotNull Set<ResolvedCall<D>> descriptors) {
trace.report(NONE_APPLICABLE.on(callNode, descriptors));
trace.report(NONE_APPLICABLE.on(call.getCallNode(), descriptors));
}
@Override
public void instantiationOfAbstractClass(@NotNull BindingTrace trace) {
trace.report(CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS.on(callNode));
trace.report(CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS.on(call.getCallNode()));
}
@Override
public void typeInferenceFailed(@NotNull BindingTrace trace) {
trace.report(TYPE_INFERENCE_FAILED.on(callNode));
trace.report(TYPE_INFERENCE_FAILED.on(call.getCallNode()));
}
@Override
@@ -293,7 +296,16 @@ public class CallResolver {
trace.report(UNSAFE_CALL.on(callOperationNode, type));
}
else {
trace.report(UNSAFE_CALL.on(reference, type));
PsiElement callElement = call.getCallElement();
if (callElement instanceof JetBinaryExpression) {
JetBinaryExpression binaryExpression = (JetBinaryExpression) callElement;
JetSimpleNameExpression operationReference = binaryExpression.getOperationReference();
String operationString = operationReference.getReferencedNameElementType() == JetTokens.IDENTIFIER ? operationReference.getText() : JetTypeInferrer.getNameForOperationSymbol(operationReference.getReferencedNameElementType());
trace.report(UNSAFE_INFIX_CALL.on(reference, binaryExpression.getLeft().getText(), operationString, binaryExpression.getRight().getText()));
}
else {
trace.report(UNSAFE_CALL.on(reference, type));
}
}
}
@@ -411,7 +423,7 @@ public class CallResolver {
}
}
else {
if (checkAllValueArguments(scope, tracing, task, candidateCall, candidate)) {
if (checkAllValueArguments(scope, tracing, task, candidateCall)) {
successfulCandidates.add(candidateCall.setResultingDescriptor(candidate));
}
else {
@@ -447,7 +459,7 @@ public class CallResolver {
candidateCall.setResultingDescriptor(substitutedDescriptor);
replaceValueParametersWithSubstitutedOnes(candidateCall, substitutedDescriptor);
if (checkAllValueArguments(scope, tracing, task, candidateCall, substitutedDescriptor)) {
if (checkAllValueArguments(scope, tracing, task, candidateCall)) {
successfulCandidates.add(candidateCall);
}
else {
@@ -541,29 +553,39 @@ public class CallResolver {
}
}
private <D extends CallableDescriptor> boolean checkAllValueArguments(JetScope scope, TracingStrategy tracing, ResolutionTask<D> task, ResolvedCall<D> candidateCall, D substitutedDescriptor) {
private <D extends CallableDescriptor> boolean checkAllValueArguments(JetScope scope, TracingStrategy tracing, ResolutionTask<D> task, ResolvedCall<D> candidateCall) {
boolean result = checkValueArgumentTypes(scope, candidateCall);
// result &= checkReceiver(task, candidateCall, tracing, substitutedDescriptor);
ReceiverDescriptor receiverArgument = candidateCall.getReceiverArgument();
ReceiverDescriptor receiverParameter = candidateCall.getResultingDescriptor().getReceiverParameter();
boolean safeAccess = task.getCall().getCallOperationNode() == JetTokens.SAFE_ACCESS;
result &= checkReceiver(tracing, candidateCall, candidateCall.getResultingDescriptor().getReceiverParameter(), candidateCall.getReceiverArgument(), task);
result &= checkReceiver(tracing, candidateCall, DescriptorUtils.getExpectedThisObject(candidateCall.getResultingDescriptor()), candidateCall.getThisObject(), task);
return result;
}
private <D extends CallableDescriptor> boolean checkReceiver(TracingStrategy tracing, ResolvedCall<D> candidateCall, ReceiverDescriptor receiverParameter, ReceiverDescriptor receiverArgument, ResolutionTask<D> task) {
boolean result = true;
if (receiverParameter.exists() && receiverArgument.exists()) {
ASTNode callOperationNode = task.getCall().getCallOperationNode();
boolean safeAccess = callOperationNode != null && callOperationNode.getElementType() == JetTokens.SAFE_ACCESS;
JetType receiverArgumentType = receiverArgument.getType();
if (!safeAccess && !receiverParameter.getType().isNullable() && receiverArgumentType.isNullable()) {
AutoCastServiceImpl autoCastService = new AutoCastServiceImpl(task.getDataFlowInfo(), candidateCall.getTrace().getBindingContext());
if (!safeAccess && !receiverParameter.getType().isNullable() && !autoCastService.isNotNull(receiverArgument)) {
tracing.unsafeCall(candidateCall.getTrace(), receiverArgumentType);
result = false;
// result = false;
}
else if (!semanticServices.getTypeChecker().isSubtypeOf(receiverArgument.getType(), receiverParameter.getType())) {
tracing.wrongReceiverType(candidateCall.getTrace(), receiverParameter, receiverArgument);
return false;
else {
JetType effectiveReceiverArgumentType = safeAccess
? TypeUtils.makeNotNullable(receiverArgumentType)
: receiverArgumentType;
if (!semanticServices.getTypeChecker().isSubtypeOf(effectiveReceiverArgumentType, receiverParameter.getType())) {
tracing.wrongReceiverType(candidateCall.getTrace(), receiverParameter, receiverArgument);
result = false;
}
}
if (safeAccess && (receiverParameter.getType().isNullable() || !receiverArgumentType.isNullable())) {
tracing.unnecessarySafeCall(candidateCall.getTrace(), receiverArgumentType);
}
}
return result;
}
@@ -76,7 +76,7 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor
scope = explicitReceiver.getType().getMemberScope();
explicitReceiver = NO_RECEIVER;
}
doComputeTasks(scope, explicitReceiver, call, name, result, NO_AUTO_CASTS);
doComputeTasks(scope, explicitReceiver, call, name, result, AutoCastService.NO_AUTO_CASTS);
ReceiverDescriptor receiverToCast = explicitReceiver.exists() ? explicitReceiver : scope.getImplicitReceiver();
if (receiverToCast.exists()) {
@@ -85,43 +85,6 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor
return result;
}
private interface AutoCastService {
List<ReceiverDescriptor> getVariantsForReceiver(ReceiverDescriptor receiverDescriptor);
DataFlowInfo getDataFlowInfo();
}
private static class AutoCastServiceImpl implements AutoCastService {
private final DataFlowInfo dataFlowInfo;
private final BindingContext bindingContext;
private AutoCastServiceImpl(DataFlowInfo dataFlowInfo, BindingContext bindingContext) {
this.dataFlowInfo = dataFlowInfo;
this.bindingContext = bindingContext;
}
@Override
public List<ReceiverDescriptor> getVariantsForReceiver(ReceiverDescriptor receiverDescriptor) {
return AutoCastUtils.getAutoCastVariants(bindingContext, dataFlowInfo, receiverDescriptor);
}
@Override
public DataFlowInfo getDataFlowInfo() {
return dataFlowInfo;
}
}
private static AutoCastService NO_AUTO_CASTS = new AutoCastService() {
@Override
public DataFlowInfo getDataFlowInfo() {
return DataFlowInfo.getEmpty();
}
@Override
public List<ReceiverDescriptor> getVariantsForReceiver(ReceiverDescriptor receiverDescriptor) {
return Collections.singletonList(receiverDescriptor);
}
};
private void doComputeTasks(JetScope scope, ReceiverDescriptor receiver, Call call, String name, List<ResolutionTask<D>> result, @NotNull AutoCastService autoCastService) {
DataFlowInfo dataFlowInfo = autoCastService.getDataFlowInfo();
List<ReceiverDescriptor> implicitReceivers = Lists.newArrayList();
@@ -66,18 +66,25 @@ public class CallMaker {
private static class CallImpl implements Call {
private final ASTNode callNode;
private final PsiElement callElement;
private final ReceiverDescriptor explicitReceiver;
private ASTNode callOperationNode;
private final JetExpression calleeExpression;
private final List<? extends ValueArgument> valueArguments;
protected CallImpl(@NotNull ASTNode callNode, @NotNull ReceiverDescriptor explicitReceiver, @Nullable ASTNode callOperationNode, @NotNull JetExpression calleeExpression, @NotNull List<? extends ValueArgument> valueArguments) {
protected CallImpl(@NotNull ASTNode callNode, @Nullable PsiElement callElement, @NotNull ReceiverDescriptor explicitReceiver, @Nullable ASTNode callOperationNode, @NotNull JetExpression calleeExpression, @NotNull List<? extends ValueArgument> valueArguments) {
this.callNode = callNode;
this.callElement = callElement;
this.explicitReceiver = explicitReceiver;
this.callOperationNode = callOperationNode;
this.calleeExpression = calleeExpression;
this.valueArguments = valueArguments;
}
protected CallImpl(@NotNull PsiElement callElement, @NotNull ReceiverDescriptor explicitReceiver, @Nullable ASTNode callOperationNode, @NotNull JetExpression calleeExpression, @NotNull List<? extends ValueArgument> valueArguments) {
this(callElement.getNode(), callElement, explicitReceiver, callOperationNode, calleeExpression, valueArguments);
}
@Override
public ASTNode getCallOperationNode() {
return callOperationNode;
@@ -106,6 +113,11 @@ public class CallMaker {
return callNode;
}
@Override
public PsiElement getCallElement() {
return callElement;
}
@Override
public JetValueArgumentList getValueArgumentList() {
return null;
@@ -142,7 +154,7 @@ public class CallMaker {
}
public static Call makeCall(JetElement callElement, ReceiverDescriptor explicitReceiver, @Nullable ASTNode callOperationNode, JetExpression calleeExpression, List<? extends ValueArgument> arguments) {
return new CallImpl(callElement.getNode(), explicitReceiver, callOperationNode, calleeExpression, arguments);
return new CallImpl(callElement, explicitReceiver, callOperationNode, calleeExpression, arguments);
}
public static Call makeCall(@NotNull ReceiverDescriptor leftAsReceiver, JetBinaryExpression expression) {
@@ -219,6 +231,11 @@ public class CallMaker {
public ASTNode getCallNode() {
return callElement.getNode();
}
@Override
public PsiElement getCallElement() {
return callElement;
}
};
}
}
@@ -30,6 +30,7 @@ import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver;
import org.jetbrains.jet.lexer.JetToken;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.jet.util.slicedmap.WritableSlice;
@@ -159,6 +160,20 @@ public class JetTypeInferrer {
.put(JetTokens.PLUSEQ, JetTokens.PLUS)
.put(JetTokens.MINUSEQ, JetTokens.MINUS)
.build();
@Nullable
public static String getNameForOperationSymbol(@NotNull IElementType token) {
String name = unaryOperationNames.get(token);
if (name != null) return name;
name = binaryOperationNames.get(token);
if (name != null) return name;
name = assignmentOperationNames.get(token);
if (name != null) return name;
if (comparisonOperations.contains(token)) return "compareTo";
if (equalsOperations.contains(token)) return "equals";
if (inOperations.contains(token)) return "contains";
return null;
}
private final JetSemanticServices semanticServices;
private final JetFlowInformationProvider flowInformationProvider;
@@ -680,13 +695,14 @@ public class JetTypeInferrer {
}
@Nullable
public JetType resolveCall(@NotNull ReceiverDescriptor receiver, @NotNull JetCallExpression callExpression) {
return getCallResolver().resolveCall(trace, scope, CallMaker.makeCall(receiver, (ASTNode) null, callExpression), expectedType);
public JetType resolveCall(@NotNull ReceiverDescriptor receiver, @Nullable ASTNode callOperationNode, @NotNull JetCallExpression callExpression) {
return getCallResolver().resolveCall(trace, scope, CallMaker.makeCall(receiver, callOperationNode, callExpression), expectedType);
}
@Nullable
public VariableDescriptor resolveSimpleProperty(@NotNull ReceiverDescriptor receiver, @NotNull JetSimpleNameExpression nameExpression) {
return getCallResolver().resolveSimpleProperty(trace, scope, receiver, nameExpression, expectedType);
public VariableDescriptor resolveSimpleProperty(@NotNull ReceiverDescriptor receiver, @Nullable ASTNode callOperationNode, @NotNull JetSimpleNameExpression nameExpression) {
Call call = CallMaker.makePropertyCall(receiver, callOperationNode, nameExpression);
return getCallResolver().resolveSimpleProperty(trace, scope, call, expectedType);
}
@NotNull
@@ -807,7 +823,7 @@ public class JetTypeInferrer {
}
}
else {
return getSelectorReturnType(NO_RECEIVER, expression, context); // TODO : Extensions to this
return getSelectorReturnType(NO_RECEIVER, null, expression, context); // TODO : Extensions to this
// assert JetTokens.IDENTIFIER == expression.getReferencedNameElementType();
// if (referencedName != null) {
// VariableDescriptor variable = context.scope.getVariable(referencedName);
@@ -1439,7 +1455,7 @@ public class JetTypeInferrer {
if (callSuffixExpression != null) {
// JetType selectorReturnType = getType(compositeScope, callSuffixExpression, false, context);
assert subjectExpression != null;
JetType selectorReturnType = getSelectorReturnType(new ExpressionReceiver(subjectExpression, subjectType), callSuffixExpression, context);//getType(compositeScope, callSuffixExpression, false, context);
JetType selectorReturnType = getSelectorReturnType(new ExpressionReceiver(subjectExpression, subjectType), condition.getOperationTokenNode(), callSuffixExpression, context);//getType(compositeScope, callSuffixExpression, false, context);
ensureBooleanResultWithCustomSubject(callSuffixExpression, selectorReturnType, "This expression", context);
// context.services.checkNullSafety(subjectType, condition.getOperationTokenNode(), getCalleeFunctionDescriptor(callSuffixExpression, context), condition);
}
@@ -1518,7 +1534,7 @@ public class JetTypeInferrer {
JetExpression decomposerExpression = pattern.getDecomposerExpression();
if (decomposerExpression != null) {
ReceiverDescriptor receiver = new TransientReceiver(subjectType);
JetType selectorReturnType = getSelectorReturnType(receiver, decomposerExpression, context);
JetType selectorReturnType = getSelectorReturnType(receiver, null, decomposerExpression, context);
result[0] = checkPatternType(pattern.getArgumentList(), selectorReturnType == null ? ErrorUtils.createErrorType("No type") : selectorReturnType, scopeToExtend, context);
}
@@ -2045,7 +2061,7 @@ public class JetTypeInferrer {
// Clean resolution: no autocasts
// TemporaryBindingTrace cleanResolutionTrace = TemporaryBindingTrace.create(context.trace);
// TypeInferenceContext cleanResolutionContext = context.replaceBindingTrace(cleanResolutionTrace);
JetType selectorReturnType = getSelectorReturnType(new ExpressionReceiver(receiverExpression, receiverType), selectorExpression, context);
JetType selectorReturnType = getSelectorReturnType(new ExpressionReceiver(receiverExpression, receiverType), expression.getOperationTokenNode(), selectorExpression, context);
//TODO move further
if (expression.getOperationSign() == JetTokens.SAFE_ACCESS) {
@@ -2187,16 +2203,16 @@ public class JetTypeInferrer {
// }
@Nullable
private JetType getSelectorReturnType(@NotNull ReceiverDescriptor receiver, @NotNull JetExpression selectorExpression, @NotNull TypeInferenceContext context) {
private JetType getSelectorReturnType(@NotNull ReceiverDescriptor receiver, @Nullable ASTNode callOperationNode, @NotNull JetExpression selectorExpression, @NotNull TypeInferenceContext context) {
if (selectorExpression instanceof JetCallExpression) {
JetCallExpression callExpression = (JetCallExpression) selectorExpression;
return context.resolveCall(receiver, callExpression);
return context.resolveCall(receiver, callOperationNode, callExpression);
}
else if (selectorExpression instanceof JetSimpleNameExpression) {
JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) selectorExpression;
TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace);
VariableDescriptor variableDescriptor = context.replaceBindingTrace(temporaryTrace).resolveSimpleProperty(receiver, nameExpression);
VariableDescriptor variableDescriptor = context.replaceBindingTrace(temporaryTrace).resolveSimpleProperty(receiver, callOperationNode, nameExpression);
if (variableDescriptor != null) {
temporaryTrace.commit();
return context.services.checkType(variableDescriptor.getOutType(), nameExpression, context);
@@ -2213,10 +2229,10 @@ public class JetTypeInferrer {
else if (selectorExpression instanceof JetQualifiedExpression) {
JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) selectorExpression;
JetExpression newReceiverExpression = qualifiedExpression.getReceiverExpression();
JetType newReceiverType = getSelectorReturnType(receiver, newReceiverExpression, context.replaceExpectedType(NO_EXPECTED_TYPE));
JetType newReceiverType = getSelectorReturnType(receiver, callOperationNode, newReceiverExpression, context.replaceExpectedType(NO_EXPECTED_TYPE));
JetExpression newSelectorExpression = qualifiedExpression.getSelectorExpression();
if (newReceiverType != null && newSelectorExpression != null) {
return getSelectorReturnType(new ExpressionReceiver(newReceiverExpression, newReceiverType), newSelectorExpression, context);
return getSelectorReturnType(new ExpressionReceiver(newReceiverExpression, newReceiverType), qualifiedExpression.getOperationTokenNode(), newSelectorExpression, context);
}
}
else {
@@ -2229,7 +2245,7 @@ public class JetTypeInferrer {
@Override
public JetType visitCallExpression(JetCallExpression expression, TypeInferenceContext context) {
JetType expressionType = context.resolveCall(NO_RECEIVER, expression);
JetType expressionType = context.resolveCall(NO_RECEIVER, null, expression);
return context.services.checkType(expressionType, expression, context);
}
@@ -2512,18 +2528,18 @@ public class JetTypeInferrer {
name,
receiver);
if (functionDescriptor != null) {
if (receiver.getType().isNullable()) {
// TODO : better error message for '1 + nullableVar' case
JetExpression right = binaryExpression.getRight();
String rightText = right == null ? "" : right.getText();
String leftText = binaryExpression.getLeft().getText();
// context.trace.getErrorHandler().genericError(binaryExpression.getOperationReference().getNode(),
// "Infix call corresponds to a dot-qualified call '" +
// leftText + "." + name + "(" + rightText + ")'" +
// " which is not allowed on a nullable receiver '" + leftText + "'." +
// " Use '?.'-qualified call instead");
context.trace.report(UNSAFE_INFIX_CALL.on(binaryExpression.getOperationReference(), leftText, name, rightText));
}
// if (receiver.getType().isNullable()) {
// // TODO : better error message for '1 + nullableVar' case
// JetExpression right = binaryExpression.getRight();
// String rightText = right == null ? "" : right.getText();
// String leftText = binaryExpression.getLeft().getText();
//// context.trace.getErrorHandler().genericError(binaryExpression.getOperationReference().getNode(),
//// "Infix call corresponds to a dot-qualified call '" +
//// leftText + "." + name + "(" + rightText + ")'" +
//// " which is not allowed on a nullable receiver '" + leftText + "'." +
//// " Use '?.'-qualified call instead");
// context.trace.report(UNSAFE_INFIX_CALL.on(binaryExpression.getOperationReference(), leftText, name, rightText));
// }
return functionDescriptor.getReturnType();
@@ -0,0 +1,44 @@
class A {
fun foo() {}
}
fun A.bar() {}
fun A?.buzz() {}
fun test(a : A?) {
a<!UNSAFE_CALL!>.<!>foo() // error
a<!UNSAFE_CALL!>.<!>bar() // error
a.buzz()
a?.foo()
a?.bar()
a<!UNNECESSARY_SAFE_CALL!>?.<!>buzz() // warning
}
fun A.test() {
foo()
bar()
buzz()
this.foo()
this.bar()
this.buzz()
this<!UNNECESSARY_SAFE_CALL!>?.<!>foo() // warning
this<!UNNECESSARY_SAFE_CALL!>?.<!>bar() // warning
this<!UNNECESSARY_SAFE_CALL!>?.<!>buzz() // warning
}
fun A?.test() {
<!UNSAFE_CALL!>foo<!>() // error
<!UNSAFE_CALL!>bar<!>() // error
buzz()
this<!UNSAFE_CALL!>.<!>foo() // error
this<!UNSAFE_CALL!>.<!>bar() // error
this.buzz()
this?.foo()
this?.bar()
this<!UNNECESSARY_SAFE_CALL!>?.<!>buzz() // warning
}