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 final Map<String, ClassifierDescriptor> classifiers = Maps.newHashMap();
private Collection<DeclarationDescriptor> allDescriptors; private Collection<DeclarationDescriptor> allDescriptors;
public JavaClassMembersScope(@NotNull DeclarationDescriptor classDescriptor, PsiClass psiClass, JavaSemanticServices semanticServices, boolean staticMembers) { public JavaClassMembersScope(@NotNull DeclarationDescriptor classOrNamespaceDescriptor, PsiClass psiClass, JavaSemanticServices semanticServices, boolean staticMembers) {
this.containingDeclaration = classDescriptor; this.containingDeclaration = classOrNamespaceDescriptor;
this.psiClass = psiClass; this.psiClass = psiClass;
this.semanticServices = semanticServices; this.semanticServices = semanticServices;
this.staticMembers = staticMembers; this.staticMembers = staticMembers;
@@ -108,7 +108,6 @@ public class JavaDescriptorResolver {
name, name,
typeParameters, typeParameters,
supertypes supertypes
)); ));
classDescriptor.setModality(Modality.convertFromFlags( classDescriptor.setModality(Modality.convertFromFlags(
psiClass.hasModifierProperty(PsiModifier.ABSTRACT) || psiClass.isInterface(), psiClass.hasModifierProperty(PsiModifier.ABSTRACT) || psiClass.isInterface(),
@@ -11,7 +11,6 @@ import java.util.Set;
* @author abreslav * @author abreslav
*/ */
public class JavaPackageScope extends JetScopeImpl { public class JavaPackageScope extends JetScopeImpl {
private final JavaSemanticServices semanticServices; private final JavaSemanticServices semanticServices;
private final DeclarationDescriptor containingDescriptor; private final DeclarationDescriptor containingDescriptor;
private final String packagePrefix; private final String packagePrefix;
@@ -1,6 +1,7 @@
package org.jetbrains.jet.lang.psi; package org.jetbrains.jet.lang.psi;
import com.intellij.lang.ASTNode; import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
@@ -39,4 +40,7 @@ public interface Call {
@NotNull @NotNull
ASTNode getCallNode(); 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.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.intellij.lang.ASTNode; import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.JetSemanticServices; import org.jetbrains.jet.lang.JetSemanticServices;
@@ -47,16 +48,20 @@ public class CallResolver {
public VariableDescriptor resolveSimpleProperty( public VariableDescriptor resolveSimpleProperty(
@NotNull BindingTrace trace, @NotNull BindingTrace trace,
@NotNull JetScope scope, @NotNull JetScope scope,
@NotNull ReceiverDescriptor receiver, // @NotNull ReceiverDescriptor receiver,
@NotNull final JetSimpleNameExpression nameExpression, // @NotNull final JetSimpleNameExpression nameExpression,
@NotNull Call call,
@NotNull JetType expectedType) { @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(); String referencedName = nameExpression.getReferencedName();
if (referencedName == null) { if (referencedName == null) {
return 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); 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 @Nullable
@@ -79,7 +84,7 @@ public class CallResolver {
@NotNull String name, @NotNull String name,
@NotNull JetType expectedType) { @NotNull JetType expectedType) {
List<ResolutionTask<FunctionDescriptor>> tasks = TaskPrioritizers.FUNCTION_TASK_PRIORITIZER.computePrioritizedTasks(scope, call, name, trace.getBindingContext(), dataFlowInfo); 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 @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) { private FunctionDescriptor checkArgumentTypesAndFail(BindingTrace trace, JetScope scope, Call call) {
@@ -184,11 +189,10 @@ public class CallResolver {
@NotNull BindingTrace trace, @NotNull BindingTrace trace,
@NotNull JetScope scope, @NotNull JetScope scope,
@NotNull final Call call, @NotNull final Call call,
@NotNull final ASTNode callNode,
@NotNull JetType expectedType, @NotNull JetType expectedType,
@NotNull final List<ResolutionTask<D>> prioritizedTasks, // high to low priority @NotNull final List<ResolutionTask<D>> prioritizedTasks, // high to low priority
@NotNull final JetReferenceExpression reference) { @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(); return resolvedCall == null ? null : resolvedCall.getResultingDescriptor();
} }
@@ -197,7 +201,6 @@ public class CallResolver {
@NotNull BindingTrace trace, @NotNull BindingTrace trace,
@NotNull JetScope scope, @NotNull JetScope scope,
@NotNull final Call call, @NotNull final Call call,
@NotNull final ASTNode callNode,
@NotNull JetType expectedType, @NotNull JetType expectedType,
@NotNull final List<ResolutionTask<D>> prioritizedTasks, // high to low priority @NotNull final List<ResolutionTask<D>> prioritizedTasks, // high to low priority
@NotNull final JetReferenceExpression reference) { @NotNull final JetReferenceExpression reference) {
@@ -268,22 +271,22 @@ public class CallResolver {
@Override @Override
public <D extends CallableDescriptor> void ambiguity(@NotNull BindingTrace trace, @NotNull Set<ResolvedCall<D>> descriptors) { 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 @Override
public <D extends CallableDescriptor> void noneApplicable(@NotNull BindingTrace trace, @NotNull Set<ResolvedCall<D>> descriptors) { 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 @Override
public void instantiationOfAbstractClass(@NotNull BindingTrace trace) { 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 @Override
public void typeInferenceFailed(@NotNull BindingTrace trace) { public void typeInferenceFailed(@NotNull BindingTrace trace) {
trace.report(TYPE_INFERENCE_FAILED.on(callNode)); trace.report(TYPE_INFERENCE_FAILED.on(call.getCallNode()));
} }
@Override @Override
@@ -293,7 +296,16 @@ public class CallResolver {
trace.report(UNSAFE_CALL.on(callOperationNode, type)); trace.report(UNSAFE_CALL.on(callOperationNode, type));
} }
else { 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 { else {
if (checkAllValueArguments(scope, tracing, task, candidateCall, candidate)) { if (checkAllValueArguments(scope, tracing, task, candidateCall)) {
successfulCandidates.add(candidateCall.setResultingDescriptor(candidate)); successfulCandidates.add(candidateCall.setResultingDescriptor(candidate));
} }
else { else {
@@ -447,7 +459,7 @@ public class CallResolver {
candidateCall.setResultingDescriptor(substitutedDescriptor); candidateCall.setResultingDescriptor(substitutedDescriptor);
replaceValueParametersWithSubstitutedOnes(candidateCall, substitutedDescriptor); replaceValueParametersWithSubstitutedOnes(candidateCall, substitutedDescriptor);
if (checkAllValueArguments(scope, tracing, task, candidateCall, substitutedDescriptor)) { if (checkAllValueArguments(scope, tracing, task, candidateCall)) {
successfulCandidates.add(candidateCall); successfulCandidates.add(candidateCall);
} }
else { 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); boolean result = checkValueArgumentTypes(scope, candidateCall);
// result &= checkReceiver(task, candidateCall, tracing, substitutedDescriptor);
ReceiverDescriptor receiverArgument = candidateCall.getReceiverArgument(); result &= checkReceiver(tracing, candidateCall, candidateCall.getResultingDescriptor().getReceiverParameter(), candidateCall.getReceiverArgument(), task);
ReceiverDescriptor receiverParameter = candidateCall.getResultingDescriptor().getReceiverParameter(); result &= checkReceiver(tracing, candidateCall, DescriptorUtils.getExpectedThisObject(candidateCall.getResultingDescriptor()), candidateCall.getThisObject(), task);
boolean safeAccess = task.getCall().getCallOperationNode() == JetTokens.SAFE_ACCESS; 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()) { if (receiverParameter.exists() && receiverArgument.exists()) {
ASTNode callOperationNode = task.getCall().getCallOperationNode();
boolean safeAccess = callOperationNode != null && callOperationNode.getElementType() == JetTokens.SAFE_ACCESS;
JetType receiverArgumentType = receiverArgument.getType(); 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); tracing.unsafeCall(candidateCall.getTrace(), receiverArgumentType);
result = false; // result = false;
} }
else if (!semanticServices.getTypeChecker().isSubtypeOf(receiverArgument.getType(), receiverParameter.getType())) { else {
tracing.wrongReceiverType(candidateCall.getTrace(), receiverParameter, receiverArgument); JetType effectiveReceiverArgumentType = safeAccess
return false; ? 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())) { if (safeAccess && (receiverParameter.getType().isNullable() || !receiverArgumentType.isNullable())) {
tracing.unnecessarySafeCall(candidateCall.getTrace(), receiverArgumentType); tracing.unnecessarySafeCall(candidateCall.getTrace(), receiverArgumentType);
} }
} }
return result; return result;
} }
@@ -76,7 +76,7 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor
scope = explicitReceiver.getType().getMemberScope(); scope = explicitReceiver.getType().getMemberScope();
explicitReceiver = NO_RECEIVER; 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(); ReceiverDescriptor receiverToCast = explicitReceiver.exists() ? explicitReceiver : scope.getImplicitReceiver();
if (receiverToCast.exists()) { if (receiverToCast.exists()) {
@@ -85,43 +85,6 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor
return result; 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) { private void doComputeTasks(JetScope scope, ReceiverDescriptor receiver, Call call, String name, List<ResolutionTask<D>> result, @NotNull AutoCastService autoCastService) {
DataFlowInfo dataFlowInfo = autoCastService.getDataFlowInfo(); DataFlowInfo dataFlowInfo = autoCastService.getDataFlowInfo();
List<ReceiverDescriptor> implicitReceivers = Lists.newArrayList(); List<ReceiverDescriptor> implicitReceivers = Lists.newArrayList();
@@ -66,18 +66,25 @@ public class CallMaker {
private static class CallImpl implements Call { private static class CallImpl implements Call {
private final ASTNode callNode; private final ASTNode callNode;
private final PsiElement callElement;
private final ReceiverDescriptor explicitReceiver; private final ReceiverDescriptor explicitReceiver;
private ASTNode callOperationNode; private ASTNode callOperationNode;
private final JetExpression calleeExpression; private final JetExpression calleeExpression;
private final List<? extends ValueArgument> valueArguments; 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.callNode = callNode;
this.callElement = callElement;
this.explicitReceiver = explicitReceiver; this.explicitReceiver = explicitReceiver;
this.callOperationNode = callOperationNode; this.callOperationNode = callOperationNode;
this.calleeExpression = calleeExpression; this.calleeExpression = calleeExpression;
this.valueArguments = valueArguments; 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 @Override
public ASTNode getCallOperationNode() { public ASTNode getCallOperationNode() {
return callOperationNode; return callOperationNode;
@@ -106,6 +113,11 @@ public class CallMaker {
return callNode; return callNode;
} }
@Override
public PsiElement getCallElement() {
return callElement;
}
@Override @Override
public JetValueArgumentList getValueArgumentList() { public JetValueArgumentList getValueArgumentList() {
return null; 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) { 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) { public static Call makeCall(@NotNull ReceiverDescriptor leftAsReceiver, JetBinaryExpression expression) {
@@ -219,6 +231,11 @@ public class CallMaker {
public ASTNode getCallNode() { public ASTNode getCallNode() {
return callElement.getNode(); 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.ExpressionReceiver;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver; 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.lexer.JetTokens;
import org.jetbrains.jet.util.slicedmap.WritableSlice; import org.jetbrains.jet.util.slicedmap.WritableSlice;
@@ -159,6 +160,20 @@ public class JetTypeInferrer {
.put(JetTokens.PLUSEQ, JetTokens.PLUS) .put(JetTokens.PLUSEQ, JetTokens.PLUS)
.put(JetTokens.MINUSEQ, JetTokens.MINUS) .put(JetTokens.MINUSEQ, JetTokens.MINUS)
.build(); .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 JetSemanticServices semanticServices;
private final JetFlowInformationProvider flowInformationProvider; private final JetFlowInformationProvider flowInformationProvider;
@@ -680,13 +695,14 @@ public class JetTypeInferrer {
} }
@Nullable @Nullable
public JetType resolveCall(@NotNull ReceiverDescriptor receiver, @NotNull JetCallExpression callExpression) { public JetType resolveCall(@NotNull ReceiverDescriptor receiver, @Nullable ASTNode callOperationNode, @NotNull JetCallExpression callExpression) {
return getCallResolver().resolveCall(trace, scope, CallMaker.makeCall(receiver, (ASTNode) null, callExpression), expectedType); return getCallResolver().resolveCall(trace, scope, CallMaker.makeCall(receiver, callOperationNode, callExpression), expectedType);
} }
@Nullable @Nullable
public VariableDescriptor resolveSimpleProperty(@NotNull ReceiverDescriptor receiver, @NotNull JetSimpleNameExpression nameExpression) { public VariableDescriptor resolveSimpleProperty(@NotNull ReceiverDescriptor receiver, @Nullable ASTNode callOperationNode, @NotNull JetSimpleNameExpression nameExpression) {
return getCallResolver().resolveSimpleProperty(trace, scope, receiver, nameExpression, expectedType); Call call = CallMaker.makePropertyCall(receiver, callOperationNode, nameExpression);
return getCallResolver().resolveSimpleProperty(trace, scope, call, expectedType);
} }
@NotNull @NotNull
@@ -807,7 +823,7 @@ public class JetTypeInferrer {
} }
} }
else { 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(); // assert JetTokens.IDENTIFIER == expression.getReferencedNameElementType();
// if (referencedName != null) { // if (referencedName != null) {
// VariableDescriptor variable = context.scope.getVariable(referencedName); // VariableDescriptor variable = context.scope.getVariable(referencedName);
@@ -1439,7 +1455,7 @@ public class JetTypeInferrer {
if (callSuffixExpression != null) { if (callSuffixExpression != null) {
// JetType selectorReturnType = getType(compositeScope, callSuffixExpression, false, context); // JetType selectorReturnType = getType(compositeScope, callSuffixExpression, false, context);
assert subjectExpression != null; 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); ensureBooleanResultWithCustomSubject(callSuffixExpression, selectorReturnType, "This expression", context);
// context.services.checkNullSafety(subjectType, condition.getOperationTokenNode(), getCalleeFunctionDescriptor(callSuffixExpression, context), condition); // context.services.checkNullSafety(subjectType, condition.getOperationTokenNode(), getCalleeFunctionDescriptor(callSuffixExpression, context), condition);
} }
@@ -1518,7 +1534,7 @@ public class JetTypeInferrer {
JetExpression decomposerExpression = pattern.getDecomposerExpression(); JetExpression decomposerExpression = pattern.getDecomposerExpression();
if (decomposerExpression != null) { if (decomposerExpression != null) {
ReceiverDescriptor receiver = new TransientReceiver(subjectType); 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); 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 // Clean resolution: no autocasts
// TemporaryBindingTrace cleanResolutionTrace = TemporaryBindingTrace.create(context.trace); // TemporaryBindingTrace cleanResolutionTrace = TemporaryBindingTrace.create(context.trace);
// TypeInferenceContext cleanResolutionContext = context.replaceBindingTrace(cleanResolutionTrace); // 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 //TODO move further
if (expression.getOperationSign() == JetTokens.SAFE_ACCESS) { if (expression.getOperationSign() == JetTokens.SAFE_ACCESS) {
@@ -2187,16 +2203,16 @@ public class JetTypeInferrer {
// } // }
@Nullable @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) { if (selectorExpression instanceof JetCallExpression) {
JetCallExpression callExpression = (JetCallExpression) selectorExpression; JetCallExpression callExpression = (JetCallExpression) selectorExpression;
return context.resolveCall(receiver, callExpression); return context.resolveCall(receiver, callOperationNode, callExpression);
} }
else if (selectorExpression instanceof JetSimpleNameExpression) { else if (selectorExpression instanceof JetSimpleNameExpression) {
JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) selectorExpression; JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) selectorExpression;
TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace); 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) { if (variableDescriptor != null) {
temporaryTrace.commit(); temporaryTrace.commit();
return context.services.checkType(variableDescriptor.getOutType(), nameExpression, context); return context.services.checkType(variableDescriptor.getOutType(), nameExpression, context);
@@ -2213,10 +2229,10 @@ public class JetTypeInferrer {
else if (selectorExpression instanceof JetQualifiedExpression) { else if (selectorExpression instanceof JetQualifiedExpression) {
JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) selectorExpression; JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) selectorExpression;
JetExpression newReceiverExpression = qualifiedExpression.getReceiverExpression(); 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(); JetExpression newSelectorExpression = qualifiedExpression.getSelectorExpression();
if (newReceiverType != null && newSelectorExpression != null) { if (newReceiverType != null && newSelectorExpression != null) {
return getSelectorReturnType(new ExpressionReceiver(newReceiverExpression, newReceiverType), newSelectorExpression, context); return getSelectorReturnType(new ExpressionReceiver(newReceiverExpression, newReceiverType), qualifiedExpression.getOperationTokenNode(), newSelectorExpression, context);
} }
} }
else { else {
@@ -2229,7 +2245,7 @@ public class JetTypeInferrer {
@Override @Override
public JetType visitCallExpression(JetCallExpression expression, TypeInferenceContext context) { 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); return context.services.checkType(expressionType, expression, context);
} }
@@ -2512,18 +2528,18 @@ public class JetTypeInferrer {
name, name,
receiver); receiver);
if (functionDescriptor != null) { if (functionDescriptor != null) {
if (receiver.getType().isNullable()) { // if (receiver.getType().isNullable()) {
// TODO : better error message for '1 + nullableVar' case // // TODO : better error message for '1 + nullableVar' case
JetExpression right = binaryExpression.getRight(); // JetExpression right = binaryExpression.getRight();
String rightText = right == null ? "" : right.getText(); // String rightText = right == null ? "" : right.getText();
String leftText = binaryExpression.getLeft().getText(); // String leftText = binaryExpression.getLeft().getText();
// context.trace.getErrorHandler().genericError(binaryExpression.getOperationReference().getNode(), //// context.trace.getErrorHandler().genericError(binaryExpression.getOperationReference().getNode(),
// "Infix call corresponds to a dot-qualified call '" + //// "Infix call corresponds to a dot-qualified call '" +
// leftText + "." + name + "(" + rightText + ")'" + //// leftText + "." + name + "(" + rightText + ")'" +
// " which is not allowed on a nullable receiver '" + leftText + "'." + //// " which is not allowed on a nullable receiver '" + leftText + "'." +
// " Use '?.'-qualified call instead"); //// " Use '?.'-qualified call instead");
context.trace.report(UNSAFE_INFIX_CALL.on(binaryExpression.getOperationReference(), leftText, name, rightText)); // context.trace.report(UNSAFE_INFIX_CALL.on(binaryExpression.getOperationReference(), leftText, name, rightText));
} // }
return functionDescriptor.getReturnType(); 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
}