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