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.resolve.BindingTraceContext;
029 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
030 import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
031 import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowInfo;
032 import org.jetbrains.jet.lang.resolve.name.Name;
033 import org.jetbrains.jet.lang.resolve.scopes.JetScope;
034 import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
035 import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver;
036 import org.jetbrains.jet.lang.types.JetType;
037 import org.jetbrains.jet.lang.types.TypeUtils;
038 import org.jetbrains.jet.util.slicedmap.WritableSlice;
039
040 import javax.inject.Inject;
041 import java.util.Collections;
042
043 import static org.jetbrains.jet.lang.diagnostics.Errors.*;
044 import static org.jetbrains.jet.lang.psi.PsiPackage.JetPsiFactory;
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(project).createExpression("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
097 FunctionDescriptor iteratorFunction = iteratorResolvedCall.getResultingDescriptor();
098 JetType iteratorType = iteratorFunction.getReturnType();
099 JetType hasNextType = checkConventionForIterator(context, loopRangeExpression, iteratorType, "hasNext",
100 HAS_NEXT_FUNCTION_AMBIGUITY, HAS_NEXT_MISSING, HAS_NEXT_FUNCTION_NONE_APPLICABLE,
101 LOOP_RANGE_HAS_NEXT_RESOLVED_CALL);
102 if (hasNextType != null && !isBoolean(hasNextType)) {
103 context.trace.report(HAS_NEXT_FUNCTION_TYPE_MISMATCH.on(loopRangeExpression, hasNextType));
104 }
105 return checkConventionForIterator(context, loopRangeExpression, iteratorType, "next",
106 NEXT_AMBIGUITY, NEXT_MISSING, NEXT_NONE_APPLICABLE,
107 LOOP_RANGE_NEXT_RESOLVED_CALL);
108 }
109 else {
110 if (iteratorResolutionResults.isAmbiguity()) {
111 // StringBuffer stringBuffer = new StringBuffer("Method 'iterator()' is ambiguous for this expression: ");
112 // for (FunctionDescriptor functionDescriptor : iteratorResolutionResults.getResultingCalls()) {
113 // stringBuffer.append(DescriptorRenderer.FQ_NAMES_IN_TYPES.render(functionDescriptor)).append(" ");
114 // }
115 // errorMessage = stringBuffer.toString();
116 context.trace.report(ITERATOR_AMBIGUITY.on(loopRangeExpression, iteratorResolutionResults.getResultingCalls()));
117 }
118 else {
119 context.trace.report(ITERATOR_MISSING.on(loopRangeExpression));
120 }
121 }
122 return null;
123 }
124
125 @Nullable
126 private JetType checkConventionForIterator(
127 @NotNull ExpressionTypingContext context,
128 @NotNull JetExpression loopRangeExpression,
129 @NotNull JetType iteratorType,
130 @NotNull String name,
131 @NotNull DiagnosticFactory1<JetExpression, JetType> ambiguity,
132 @NotNull DiagnosticFactory1<JetExpression, JetType> missing,
133 @NotNull DiagnosticFactory1<JetExpression, JetType> noneApplicable,
134 @NotNull WritableSlice<JetExpression, ResolvedCall<FunctionDescriptor>> resolvedCallKey
135 ) {
136 OverloadResolutionResults<FunctionDescriptor> nextResolutionResults = expressionTypingUtils.resolveFakeCall(
137 context, new TransientReceiver(iteratorType), Name.identifier(name));
138 if (nextResolutionResults.isAmbiguity()) {
139 context.trace.report(ambiguity.on(loopRangeExpression, iteratorType));
140 }
141 else if (nextResolutionResults.isNothing()) {
142 context.trace.report(missing.on(loopRangeExpression, iteratorType));
143 }
144 else if (!nextResolutionResults.isSuccess()) {
145 context.trace.report(noneApplicable.on(loopRangeExpression, iteratorType));
146 }
147 else {
148 assert nextResolutionResults.isSuccess();
149 ResolvedCall<FunctionDescriptor> resolvedCall = nextResolutionResults.getResultingCall();
150 context.trace.record(resolvedCallKey, loopRangeExpression, resolvedCall);
151 return resolvedCall.getResultingDescriptor().getReturnType();
152 }
153 return null;
154 }
155 }