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 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;
|
||||||
|
|||||||
-1
@@ -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();
|
||||||
|
|||||||
+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