Nullability properties of receivers checked
This commit is contained in:
+2
-2
@@ -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;
|
||||
|
||||
-1
@@ -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();
|
||||
|
||||
+44
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user