/* * Copyright 2010-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jetbrains.kotlin.types.expressions; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.FunctionDescriptor; import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor; import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1; import org.jetbrains.kotlin.diagnostics.DiagnosticSink; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.resolve.calls.checkers.OperatorCallChecker; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults; import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver; import org.jetbrains.kotlin.types.DynamicTypesKt; import org.jetbrains.kotlin.types.ErrorUtils; import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.util.OperatorNameConventions; import org.jetbrains.kotlin.util.slicedMap.WritableSlice; import java.util.Collections; import static org.jetbrains.kotlin.diagnostics.Errors.*; import static org.jetbrains.kotlin.resolve.BindingContext.*; public class ForLoopConventionsChecker { private final KotlinBuiltIns builtIns; private final FakeCallResolver fakeCallResolver; public ForLoopConventionsChecker( @NotNull KotlinBuiltIns builtIns, @NotNull FakeCallResolver fakeCallResolver ) { this.builtIns = builtIns; this.fakeCallResolver = fakeCallResolver; } @Nullable public KotlinType checkIterableConvention(@NotNull ExpressionReceiver loopRange, @NotNull ExpressionTypingContext context) { KtExpression loopRangeExpression = loopRange.getExpression(); // Make a fake call loopRange.iterator(), and try to resolve it OverloadResolutionResults<FunctionDescriptor> iteratorResolutionResults = fakeCallResolver.resolveFakeCall( context, loopRange, OperatorNameConventions.ITERATOR, loopRangeExpression, loopRangeExpression, FakeCallKind.ITERATOR, Collections.emptyList() ); if (!iteratorResolutionResults.isSuccess()) return null; ResolvedCall<FunctionDescriptor> iteratorResolvedCall = iteratorResolutionResults.getResultingCall(); context.trace.record(LOOP_RANGE_ITERATOR_RESOLVED_CALL, loopRangeExpression, iteratorResolvedCall); FunctionDescriptor iteratorFunction = iteratorResolvedCall.getResultingDescriptor(); checkIfOperatorModifierPresent(loopRangeExpression, iteratorFunction, context.trace); KotlinType iteratorType = iteratorFunction.getReturnType(); //noinspection ConstantConditions KotlinType hasNextType = checkConventionForIterator( context, loopRangeExpression, iteratorType, OperatorNameConventions.HAS_NEXT, HAS_NEXT_FUNCTION_AMBIGUITY, HAS_NEXT_MISSING, HAS_NEXT_FUNCTION_NONE_APPLICABLE, LOOP_RANGE_HAS_NEXT_RESOLVED_CALL ); if (hasNextType != null && !builtIns.isBooleanOrSubtype(hasNextType)) { context.trace.report(HAS_NEXT_FUNCTION_TYPE_MISMATCH.on(loopRangeExpression, hasNextType)); } return checkConventionForIterator( context, loopRangeExpression, iteratorType, OperatorNameConventions.NEXT, NEXT_AMBIGUITY, NEXT_MISSING, NEXT_NONE_APPLICABLE, LOOP_RANGE_NEXT_RESOLVED_CALL ); } private static void checkIfOperatorModifierPresent(KtExpression expression, FunctionDescriptor descriptor, DiagnosticSink sink) { if (ErrorUtils.isError(descriptor)) return; ReceiverParameterDescriptor extensionReceiverParameter = descriptor.getExtensionReceiverParameter(); if ((extensionReceiverParameter != null) && (DynamicTypesKt.isDynamic(extensionReceiverParameter.getType()))) return; if (!descriptor.isOperator()) { OperatorCallChecker.Companion.report(expression, descriptor, sink); } } @Nullable private KotlinType checkConventionForIterator( @NotNull ExpressionTypingContext context, @NotNull KtExpression loopRangeExpression, @NotNull KotlinType iteratorType, @NotNull Name name, @NotNull DiagnosticFactory1<KtExpression, KotlinType> ambiguity, @NotNull DiagnosticFactory1<KtExpression, KotlinType> missing, @NotNull DiagnosticFactory1<KtExpression, KotlinType> noneApplicable, @NotNull WritableSlice<KtExpression, ResolvedCall<FunctionDescriptor>> resolvedCallKey ) { OverloadResolutionResults<FunctionDescriptor> nextResolutionResults = fakeCallResolver.resolveFakeCall( context, new TransientReceiver(iteratorType), name, loopRangeExpression, loopRangeExpression, FakeCallKind.OTHER, Collections.emptyList() ); if (nextResolutionResults.isAmbiguity()) { context.trace.report(ambiguity.on(loopRangeExpression, iteratorType)); } else if (nextResolutionResults.isNothing()) { context.trace.report(missing.on(loopRangeExpression, iteratorType)); } else if (!nextResolutionResults.isSuccess()) { context.trace.report(noneApplicable.on(loopRangeExpression, iteratorType)); } else { assert nextResolutionResults.isSuccess(); ResolvedCall<FunctionDescriptor> resolvedCall = nextResolutionResults.getResultingCall(); context.trace.record(resolvedCallKey, loopRangeExpression, resolvedCall); FunctionDescriptor functionDescriptor = resolvedCall.getResultingDescriptor(); checkIfOperatorModifierPresent(loopRangeExpression, functionDescriptor, context.trace); return functionDescriptor.getReturnType(); } return null; } }