diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaClassMembersScope.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaClassMembersScope.java index c5485602ba5..ea643a2d011 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaClassMembersScope.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaClassMembersScope.java @@ -27,8 +27,8 @@ public class JavaClassMembersScope implements JetScope { private final Map classifiers = Maps.newHashMap(); private Collection 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; diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java index 738ee857c9d..c65dca68bc8 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java @@ -108,7 +108,6 @@ public class JavaDescriptorResolver { name, typeParameters, supertypes - )); classDescriptor.setModality(Modality.convertFromFlags( psiClass.hasModifierProperty(PsiModifier.ABSTRACT) || psiClass.isInterface(), diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaPackageScope.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaPackageScope.java index 2b69b86e161..11b92aee25a 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaPackageScope.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaPackageScope.java @@ -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; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/Call.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/Call.java index 4983bafe6d2..ebc74358f2f 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/Call.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/Call.java @@ -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(); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastService.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastService.java new file mode 100644 index 00000000000..f5b928cda82 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastService.java @@ -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 getVariantsForReceiver(ReceiverDescriptor receiverDescriptor) { + return Collections.singletonList(receiverDescriptor); + } + }; + + List getVariantsForReceiver(ReceiverDescriptor receiverDescriptor); + DataFlowInfo getDataFlowInfo(); + + boolean isNotNull(@NotNull ReceiverDescriptor receiver); +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastServiceImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastServiceImpl.java new file mode 100644 index 00000000000..dadd7ece9da --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastServiceImpl.java @@ -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 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 autoCastVariants = AutoCastUtils.getAutoCastVariants(bindingContext, dataFlowInfo, receiver); + for (ReceiverDescriptor autoCastVariant : autoCastVariants) { + if (!autoCastVariant.getType().isNullable()) return true; + } + return false; + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java index 9788fb2b164..f63b861f707 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java @@ -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> 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> 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> prioritizedTasks, // high to low priority @NotNull final JetReferenceExpression reference) { - ResolvedCall resolvedCall = doResolveCall(trace, scope, call, callNode, expectedType, prioritizedTasks, reference); + ResolvedCall 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> prioritizedTasks, // high to low priority @NotNull final JetReferenceExpression reference) { @@ -268,22 +271,22 @@ public class CallResolver { @Override public void ambiguity(@NotNull BindingTrace trace, @NotNull Set> descriptors) { - trace.report(OVERLOAD_RESOLUTION_AMBIGUITY.on(callNode, descriptors)); + trace.report(OVERLOAD_RESOLUTION_AMBIGUITY.on(call.getCallNode(), descriptors)); } @Override public void noneApplicable(@NotNull BindingTrace trace, @NotNull Set> 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 boolean checkAllValueArguments(JetScope scope, TracingStrategy tracing, ResolutionTask task, ResolvedCall candidateCall, D substitutedDescriptor) { + private boolean checkAllValueArguments(JetScope scope, TracingStrategy tracing, ResolutionTask task, ResolvedCall 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 boolean checkReceiver(TracingStrategy tracing, ResolvedCall candidateCall, ReceiverDescriptor receiverParameter, ReceiverDescriptor receiverArgument, ResolutionTask 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; } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizer.java index adc25b80ce0..e622bcd6cf8 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizer.java @@ -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 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 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 getVariantsForReceiver(ReceiverDescriptor receiverDescriptor) { - return Collections.singletonList(receiverDescriptor); - } - }; - private void doComputeTasks(JetScope scope, ReceiverDescriptor receiver, Call call, String name, List> result, @NotNull AutoCastService autoCastService) { DataFlowInfo dataFlowInfo = autoCastService.getDataFlowInfo(); List implicitReceivers = Lists.newArrayList(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/CallMaker.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/CallMaker.java index 4aa1f807e2d..dcfe5818a21 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/CallMaker.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/CallMaker.java @@ -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 valueArguments; - protected CallImpl(@NotNull ASTNode callNode, @NotNull ReceiverDescriptor explicitReceiver, @Nullable ASTNode callOperationNode, @NotNull JetExpression calleeExpression, @NotNull List valueArguments) { + + protected CallImpl(@NotNull ASTNode callNode, @Nullable PsiElement callElement, @NotNull ReceiverDescriptor explicitReceiver, @Nullable ASTNode callOperationNode, @NotNull JetExpression calleeExpression, @NotNull List 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 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 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; + } }; } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index d890e7dc64f..5532832d79f 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -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(); diff --git a/idea/testData/checkerWithErrorTypes/quick/nullabilityAndAutoCasts/ReceiverNullability.jet b/idea/testData/checkerWithErrorTypes/quick/nullabilityAndAutoCasts/ReceiverNullability.jet new file mode 100644 index 00000000000..0995252deff --- /dev/null +++ b/idea/testData/checkerWithErrorTypes/quick/nullabilityAndAutoCasts/ReceiverNullability.jet @@ -0,0 +1,44 @@ +class A { + fun foo() {} +} + +fun A.bar() {} +fun A?.buzz() {} + +fun test(a : A?) { + a.foo() // error + a.bar() // error + a.buzz() + + a?.foo() + a?.bar() + a?.buzz() // warning +} + +fun A.test() { + foo() + bar() + buzz() + + this.foo() + this.bar() + this.buzz() + + this?.foo() // warning + this?.bar() // warning + this?.buzz() // warning +} + +fun A?.test() { + foo() // error + bar() // error + buzz() + + this.foo() // error + this.bar() // error + this.buzz() + + this?.foo() + this?.bar() + this?.buzz() // warning +}