001    /*
002     * Copyright 2010-2015 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.kotlin.types.expressions;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
022    import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
023    import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor;
024    import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
025    import org.jetbrains.kotlin.diagnostics.DiagnosticSink;
026    import org.jetbrains.kotlin.name.Name;
027    import org.jetbrains.kotlin.psi.KtExpression;
028    import org.jetbrains.kotlin.resolve.calls.checkers.OperatorCallChecker;
029    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
030    import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
031    import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
032    import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver;
033    import org.jetbrains.kotlin.types.DynamicTypesKt;
034    import org.jetbrains.kotlin.types.ErrorUtils;
035    import org.jetbrains.kotlin.types.KotlinType;
036    import org.jetbrains.kotlin.util.OperatorNameConventions;
037    import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
038    
039    import java.util.Collections;
040    
041    import static org.jetbrains.kotlin.diagnostics.Errors.*;
042    import static org.jetbrains.kotlin.resolve.BindingContext.*;
043    
044    public class ForLoopConventionsChecker {
045        private final KotlinBuiltIns builtIns;
046        private final FakeCallResolver fakeCallResolver;
047    
048        public ForLoopConventionsChecker(
049                @NotNull KotlinBuiltIns builtIns,
050                @NotNull FakeCallResolver fakeCallResolver
051        ) {
052            this.builtIns = builtIns;
053            this.fakeCallResolver = fakeCallResolver;
054        }
055    
056        @Nullable
057        public KotlinType checkIterableConvention(@NotNull ExpressionReceiver loopRange, @NotNull ExpressionTypingContext context) {
058            KtExpression loopRangeExpression = loopRange.getExpression();
059    
060            // Make a fake call loopRange.iterator(), and try to resolve it
061            OverloadResolutionResults<FunctionDescriptor> iteratorResolutionResults = fakeCallResolver.resolveFakeCall(
062                    context, loopRange, OperatorNameConventions.ITERATOR, loopRangeExpression,
063                    loopRangeExpression, FakeCallKind.ITERATOR, Collections.<KtExpression>emptyList()
064            );
065            if (!iteratorResolutionResults.isSuccess()) return null;
066    
067            ResolvedCall<FunctionDescriptor> iteratorResolvedCall = iteratorResolutionResults.getResultingCall();
068            context.trace.record(LOOP_RANGE_ITERATOR_RESOLVED_CALL, loopRangeExpression, iteratorResolvedCall);
069            FunctionDescriptor iteratorFunction = iteratorResolvedCall.getResultingDescriptor();
070    
071            checkIfOperatorModifierPresent(loopRangeExpression, iteratorFunction, context.trace);
072    
073            KotlinType iteratorType = iteratorFunction.getReturnType();
074            //noinspection ConstantConditions
075            KotlinType hasNextType = checkConventionForIterator(
076                    context, loopRangeExpression, iteratorType, OperatorNameConventions.HAS_NEXT,
077                    HAS_NEXT_FUNCTION_AMBIGUITY, HAS_NEXT_MISSING, HAS_NEXT_FUNCTION_NONE_APPLICABLE, LOOP_RANGE_HAS_NEXT_RESOLVED_CALL
078            );
079            if (hasNextType != null && !builtIns.isBooleanOrSubtype(hasNextType)) {
080                context.trace.report(HAS_NEXT_FUNCTION_TYPE_MISMATCH.on(loopRangeExpression, hasNextType));
081            }
082            return checkConventionForIterator(
083                    context, loopRangeExpression, iteratorType, OperatorNameConventions.NEXT,
084                    NEXT_AMBIGUITY, NEXT_MISSING, NEXT_NONE_APPLICABLE, LOOP_RANGE_NEXT_RESOLVED_CALL
085            );
086        }
087    
088        private static void checkIfOperatorModifierPresent(KtExpression expression, FunctionDescriptor descriptor, DiagnosticSink sink) {
089            if (ErrorUtils.isError(descriptor)) return;
090            ReceiverParameterDescriptor extensionReceiverParameter = descriptor.getExtensionReceiverParameter();
091            if ((extensionReceiverParameter != null) && (DynamicTypesKt.isDynamic(extensionReceiverParameter.getType()))) return;
092    
093            if (!descriptor.isOperator()) {
094                OperatorCallChecker.Companion.report(expression, descriptor, sink);
095            }
096        }
097    
098        @Nullable
099        private KotlinType checkConventionForIterator(
100                @NotNull ExpressionTypingContext context,
101                @NotNull KtExpression loopRangeExpression,
102                @NotNull KotlinType iteratorType,
103                @NotNull Name name,
104                @NotNull DiagnosticFactory1<KtExpression, KotlinType> ambiguity,
105                @NotNull DiagnosticFactory1<KtExpression, KotlinType> missing,
106                @NotNull DiagnosticFactory1<KtExpression, KotlinType> noneApplicable,
107                @NotNull WritableSlice<KtExpression, ResolvedCall<FunctionDescriptor>> resolvedCallKey
108        ) {
109            OverloadResolutionResults<FunctionDescriptor> nextResolutionResults = fakeCallResolver.resolveFakeCall(
110                    context, new TransientReceiver(iteratorType), name, loopRangeExpression, loopRangeExpression, FakeCallKind.OTHER,
111                    Collections.<KtExpression>emptyList()
112            );
113            if (nextResolutionResults.isAmbiguity()) {
114                context.trace.report(ambiguity.on(loopRangeExpression, iteratorType));
115            }
116            else if (nextResolutionResults.isNothing()) {
117                context.trace.report(missing.on(loopRangeExpression, iteratorType));
118            }
119            else if (!nextResolutionResults.isSuccess()) {
120                context.trace.report(noneApplicable.on(loopRangeExpression, iteratorType));
121            }
122            else {
123                assert nextResolutionResults.isSuccess();
124                ResolvedCall<FunctionDescriptor> resolvedCall = nextResolutionResults.getResultingCall();
125                context.trace.record(resolvedCallKey, loopRangeExpression, resolvedCall);
126    
127                FunctionDescriptor functionDescriptor = resolvedCall.getResultingDescriptor();
128                checkIfOperatorModifierPresent(loopRangeExpression, functionDescriptor, context.trace);
129    
130                return functionDescriptor.getReturnType();
131            }
132            return null;
133        }
134    }