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(DescriptorRendererImpl.TEXT.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 }