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 kotlin.Pair;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
023    import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
024    import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor;
025    import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
026    import org.jetbrains.kotlin.diagnostics.DiagnosticSink;
027    import org.jetbrains.kotlin.name.Name;
028    import org.jetbrains.kotlin.psi.Call;
029    import org.jetbrains.kotlin.psi.JetExpression;
030    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
031    import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
032    import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt;
033    import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
034    import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
035    import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver;
036    import org.jetbrains.kotlin.resolve.validation.OperatorValidator;
037    import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator;
038    import org.jetbrains.kotlin.types.DynamicTypesKt;
039    import org.jetbrains.kotlin.types.ErrorUtils;
040    import org.jetbrains.kotlin.types.JetType;
041    import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
042    
043    import java.util.Collections;
044    
045    import static org.jetbrains.kotlin.diagnostics.Errors.*;
046    import static org.jetbrains.kotlin.resolve.BindingContext.*;
047    
048    public class ForLoopConventionsChecker {
049    
050        @NotNull private final KotlinBuiltIns builtIns;
051        @NotNull private final SymbolUsageValidator symbolUsageValidator;
052        @NotNull private final FakeCallResolver fakeCallResolver;
053    
054        public ForLoopConventionsChecker(
055                @NotNull KotlinBuiltIns builtIns,
056                @NotNull FakeCallResolver fakeCallResolver,
057                @NotNull SymbolUsageValidator symbolUsageValidator
058        ) {
059            this.builtIns = builtIns;
060            this.fakeCallResolver = fakeCallResolver;
061            this.symbolUsageValidator = symbolUsageValidator;
062        }
063    
064        @Nullable
065        public JetType checkIterableConvention(@NotNull ExpressionReceiver loopRange, ExpressionTypingContext context) {
066            JetExpression loopRangeExpression = loopRange.getExpression();
067    
068            // Make a fake call loopRange.iterator(), and try to resolve it
069            Name iterator = Name.identifier("iterator");
070            Pair<Call, OverloadResolutionResults<FunctionDescriptor>> calls =
071                    fakeCallResolver.makeAndResolveFakeCall(loopRange, context, Collections.<JetExpression>emptyList(), iterator,
072                                                                 loopRange.getExpression());
073            OverloadResolutionResults<FunctionDescriptor> iteratorResolutionResults = calls.getSecond();
074    
075            if (iteratorResolutionResults.isSuccess()) {
076                ResolvedCall<FunctionDescriptor> iteratorResolvedCall = iteratorResolutionResults.getResultingCall();
077                context.trace.record(LOOP_RANGE_ITERATOR_RESOLVED_CALL, loopRangeExpression, iteratorResolvedCall);
078                FunctionDescriptor iteratorFunction = iteratorResolvedCall.getResultingDescriptor();
079    
080                checkIfOperatorModifierPresent(loopRangeExpression, iteratorFunction, context.trace);
081    
082                symbolUsageValidator.validateCall(iteratorFunction, context.trace, loopRangeExpression);
083    
084                JetType iteratorType = iteratorFunction.getReturnType();
085                JetType hasNextType = checkConventionForIterator(context, loopRangeExpression, iteratorType, "hasNext",
086                                                                 HAS_NEXT_FUNCTION_AMBIGUITY, HAS_NEXT_MISSING, HAS_NEXT_FUNCTION_NONE_APPLICABLE,
087                                                                 LOOP_RANGE_HAS_NEXT_RESOLVED_CALL);
088                if (hasNextType != null && !builtIns.isBooleanOrSubtype(hasNextType)) {
089                    context.trace.report(HAS_NEXT_FUNCTION_TYPE_MISMATCH.on(loopRangeExpression, hasNextType));
090                }
091                return checkConventionForIterator(context, loopRangeExpression, iteratorType, "next",
092                                                  NEXT_AMBIGUITY, NEXT_MISSING, NEXT_NONE_APPLICABLE,
093                                                  LOOP_RANGE_NEXT_RESOLVED_CALL);
094            }
095            else {
096                if (iteratorResolutionResults.isAmbiguity()) {
097                    context.trace.report(ITERATOR_AMBIGUITY.on(loopRangeExpression, iteratorResolutionResults.getResultingCalls()));
098                }
099                else {
100                    context.trace.report(ITERATOR_MISSING.on(loopRangeExpression));
101                }
102            }
103            return null;
104        }
105    
106        private static void checkIfOperatorModifierPresent(JetExpression expression, FunctionDescriptor descriptor, DiagnosticSink sink) {
107            if (ErrorUtils.isError(descriptor)) return;
108            ReceiverParameterDescriptor extensionReceiverParameter = descriptor.getExtensionReceiverParameter();
109            if ((extensionReceiverParameter != null) && (DynamicTypesKt.isDynamic(extensionReceiverParameter.getType()))) return;
110    
111            if (!descriptor.isOperator()) {
112                OperatorValidator.Companion.report(expression, descriptor, sink);
113            }
114        }
115    
116        @Nullable
117        private JetType checkConventionForIterator(
118                @NotNull ExpressionTypingContext context,
119                @NotNull JetExpression loopRangeExpression,
120                @NotNull JetType iteratorType,
121                @NotNull String name,
122                @NotNull DiagnosticFactory1<JetExpression, JetType> ambiguity,
123                @NotNull DiagnosticFactory1<JetExpression, JetType> missing,
124                @NotNull DiagnosticFactory1<JetExpression, JetType> noneApplicable,
125                @NotNull WritableSlice<JetExpression, ResolvedCall<FunctionDescriptor>> resolvedCallKey
126        ) {
127            OverloadResolutionResults<FunctionDescriptor> nextResolutionResults = fakeCallResolver.resolveFakeCall(
128                    context, new TransientReceiver(iteratorType), Name.identifier(name), loopRangeExpression);
129            if (nextResolutionResults.isAmbiguity()) {
130                context.trace.report(ambiguity.on(loopRangeExpression, iteratorType));
131            }
132            else if (nextResolutionResults.isNothing()) {
133                context.trace.report(missing.on(loopRangeExpression, iteratorType));
134            }
135            else if (!nextResolutionResults.isSuccess()) {
136                context.trace.report(noneApplicable.on(loopRangeExpression, iteratorType));
137            }
138            else {
139                assert nextResolutionResults.isSuccess();
140                ResolvedCall<FunctionDescriptor> resolvedCall = nextResolutionResults.getResultingCall();
141                context.trace.record(resolvedCallKey, loopRangeExpression, resolvedCall);
142                FunctionDescriptor functionDescriptor = resolvedCall.getResultingDescriptor();
143                symbolUsageValidator.validateCall(functionDescriptor, context.trace, loopRangeExpression);
144    
145                checkIfOperatorModifierPresent(loopRangeExpression, functionDescriptor, context.trace);
146    
147                return functionDescriptor.getReturnType();
148            }
149            return null;
150        }
151    }