001    /*
002     * Copyright 2010-2014 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.jet.lang.types.expressions;
018    
019    import com.intellij.openapi.project.Project;
020    import com.intellij.openapi.util.Pair;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
024    import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
025    import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory1;
026    import org.jetbrains.jet.lang.psi.Call;
027    import org.jetbrains.jet.lang.psi.JetExpression;
028    import org.jetbrains.jet.lang.psi.JetPsiFactory;
029    import org.jetbrains.jet.lang.resolve.BindingTraceContext;
030    import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
031    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
032    import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
033    import org.jetbrains.jet.lang.resolve.name.Name;
034    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
035    import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
036    import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver;
037    import org.jetbrains.jet.lang.types.JetType;
038    import org.jetbrains.jet.lang.types.TypeUtils;
039    import org.jetbrains.jet.util.slicedmap.WritableSlice;
040    
041    import javax.inject.Inject;
042    import java.util.Collections;
043    
044    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
045    import static org.jetbrains.jet.lang.resolve.BindingContext.*;
046    import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.isBoolean;
047    
048    public class ForLoopConventionsChecker {
049    
050        private Project project;
051        private ExpressionTypingServices expressionTypingServices;
052        private ExpressionTypingUtils expressionTypingUtils;
053    
054        @Inject
055        public void setProject(@NotNull Project project) {
056            this.project = project;
057        }
058    
059        @Inject
060        public void setExpressionTypingUtils(@NotNull ExpressionTypingUtils expressionTypingUtils) {
061            this.expressionTypingUtils = expressionTypingUtils;
062        }
063    
064        @Inject
065        public void setExpressionTypingServices(@NotNull ExpressionTypingServices expressionTypingServices) {
066            this.expressionTypingServices = expressionTypingServices;
067        }
068    
069        public boolean isVariableIterable(@NotNull VariableDescriptor variableDescriptor, @NotNull JetScope scope) {
070            JetExpression expression = JetPsiFactory.createExpression(project, "fake");
071            ExpressionReceiver expressionReceiver = new ExpressionReceiver(expression, variableDescriptor.getType());
072            ExpressionTypingContext context = ExpressionTypingContext.newContext(
073                    expressionTypingServices,
074                    new BindingTraceContext(),
075                    scope,
076                    DataFlowInfo.EMPTY,
077                    TypeUtils.NO_EXPECTED_TYPE
078            );
079            return checkIterableConvention(expressionReceiver, context) != null;
080        }
081    
082        @Nullable
083        /*package*/ JetType checkIterableConvention(@NotNull ExpressionReceiver loopRange, ExpressionTypingContext context) {
084            JetExpression loopRangeExpression = loopRange.getExpression();
085    
086            // Make a fake call loopRange.iterator(), and try to resolve it
087            Name iterator = Name.identifier("iterator");
088            Pair<Call, OverloadResolutionResults<FunctionDescriptor>> calls =
089                    expressionTypingUtils.makeAndResolveFakeCall(loopRange, context, Collections.<JetExpression>emptyList(), iterator);
090            Call iteratorCall = calls.getFirst();
091            OverloadResolutionResults<FunctionDescriptor> iteratorResolutionResults = calls.getSecond();
092    
093            if (iteratorResolutionResults.isSuccess()) {
094                ResolvedCall<FunctionDescriptor> iteratorResolvedCall = iteratorResolutionResults.getResultingCall();
095                context.trace.record(LOOP_RANGE_ITERATOR_RESOLVED_CALL, loopRangeExpression, iteratorResolvedCall);
096                context.trace.record(LOOP_RANGE_ITERATOR_CALL, loopRangeExpression, iteratorCall);
097    
098                FunctionDescriptor iteratorFunction = iteratorResolvedCall.getResultingDescriptor();
099                JetType iteratorType = iteratorFunction.getReturnType();
100                JetType hasNextType = checkConventionForIterator(context, loopRangeExpression, iteratorType, "hasNext",
101                                                                 HAS_NEXT_FUNCTION_AMBIGUITY, HAS_NEXT_MISSING, HAS_NEXT_FUNCTION_NONE_APPLICABLE,
102                                                                 LOOP_RANGE_HAS_NEXT_RESOLVED_CALL);
103                if (hasNextType != null && !isBoolean(hasNextType)) {
104                    context.trace.report(HAS_NEXT_FUNCTION_TYPE_MISMATCH.on(loopRangeExpression, hasNextType));
105                }
106                return checkConventionForIterator(context, loopRangeExpression, iteratorType, "next",
107                                                  NEXT_AMBIGUITY, NEXT_MISSING, NEXT_NONE_APPLICABLE,
108                                                  LOOP_RANGE_NEXT_RESOLVED_CALL);
109            }
110            else {
111                if (iteratorResolutionResults.isAmbiguity()) {
112    //                    StringBuffer stringBuffer = new StringBuffer("Method 'iterator()' is ambiguous for this expression: ");
113    //                    for (FunctionDescriptor functionDescriptor : iteratorResolutionResults.getResultingCalls()) {
114    //                        stringBuffer.append(DescriptorRenderer.FQ_NAMES_IN_TYPES.render(functionDescriptor)).append(" ");
115    //                    }
116    //                    errorMessage = stringBuffer.toString();
117                    context.trace.report(ITERATOR_AMBIGUITY.on(loopRangeExpression, iteratorResolutionResults.getResultingCalls()));
118                }
119                else {
120                    context.trace.report(ITERATOR_MISSING.on(loopRangeExpression));
121                }
122            }
123            return null;
124        }
125    
126        @Nullable
127        private JetType checkConventionForIterator(
128                @NotNull ExpressionTypingContext context,
129                @NotNull JetExpression loopRangeExpression,
130                @NotNull JetType iteratorType,
131                @NotNull String name,
132                @NotNull DiagnosticFactory1<JetExpression, JetType> ambiguity,
133                @NotNull DiagnosticFactory1<JetExpression, JetType> missing,
134                @NotNull DiagnosticFactory1<JetExpression, JetType> noneApplicable,
135                @NotNull WritableSlice<JetExpression, ResolvedCall<FunctionDescriptor>> resolvedCallKey
136        ) {
137            OverloadResolutionResults<FunctionDescriptor> nextResolutionResults = expressionTypingUtils.resolveFakeCall(
138                    context, new TransientReceiver(iteratorType), Name.identifier(name));
139            if (nextResolutionResults.isAmbiguity()) {
140                context.trace.report(ambiguity.on(loopRangeExpression, iteratorType));
141            }
142            else if (nextResolutionResults.isNothing()) {
143                context.trace.report(missing.on(loopRangeExpression, iteratorType));
144            }
145            else if (!nextResolutionResults.isSuccess()) {
146                context.trace.report(noneApplicable.on(loopRangeExpression, iteratorType));
147            }
148            else {
149                assert nextResolutionResults.isSuccess();
150                ResolvedCall<FunctionDescriptor> resolvedCall = nextResolutionResults.getResultingCall();
151                context.trace.record(resolvedCallKey, loopRangeExpression, resolvedCall);
152                return resolvedCall.getResultingDescriptor().getReturnType();
153            }
154            return null;
155        }
156    }