001 /*
002 * Copyright 2010-2016 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.resolve.calls.results;
018
019 import com.google.common.collect.Sets;
020 import kotlin.jvm.functions.Function1;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.kotlin.descriptors.CallableDescriptor;
023 import org.jetbrains.kotlin.resolve.BindingTrace;
024 import org.jetbrains.kotlin.resolve.OverridingUtil;
025 import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
026 import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext;
027 import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode;
028 import org.jetbrains.kotlin.resolve.calls.model.MutableResolvedCall;
029 import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall;
030 import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy;
031
032 import java.util.Collection;
033 import java.util.EnumSet;
034 import java.util.Set;
035
036 import static org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.*;
037
038 public class ResolutionResultsHandler {
039 private static final Function1<MutableResolvedCall<?>, CallableDescriptor> MAP_RESOLVED_CALL_TO_RESULTING_DESCRIPTOR =
040 new Function1<MutableResolvedCall<?>, CallableDescriptor>() {
041 @Override
042 public CallableDescriptor invoke(MutableResolvedCall<?> resolvedCall) {
043 return resolvedCall.getResultingDescriptor();
044 }
045 };
046
047 private final OverloadingConflictResolver overloadingConflictResolver;
048
049 public ResolutionResultsHandler(@NotNull OverloadingConflictResolver overloadingConflictResolver) {
050 this.overloadingConflictResolver = overloadingConflictResolver;
051 }
052
053 @NotNull
054 public <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> computeResultAndReportErrors(
055 @NotNull CallResolutionContext context,
056 @NotNull TracingStrategy tracing,
057 @NotNull Collection<MutableResolvedCall<D>> candidates
058 ) {
059 Set<MutableResolvedCall<D>> successfulCandidates = Sets.newLinkedHashSet();
060 Set<MutableResolvedCall<D>> failedCandidates = Sets.newLinkedHashSet();
061 Set<MutableResolvedCall<D>> incompleteCandidates = Sets.newLinkedHashSet();
062 Set<MutableResolvedCall<D>> candidatesWithWrongReceiver = Sets.newLinkedHashSet();
063 for (MutableResolvedCall<D> candidateCall : candidates) {
064 ResolutionStatus status = candidateCall.getStatus();
065 assert status != UNKNOWN_STATUS : "No resolution for " + candidateCall.getCandidateDescriptor();
066 if (status.isSuccess()) {
067 successfulCandidates.add(candidateCall);
068 }
069 else if (status == INCOMPLETE_TYPE_INFERENCE) {
070 incompleteCandidates.add(candidateCall);
071 }
072 else if (candidateCall.getStatus() == RECEIVER_TYPE_ERROR) {
073 candidatesWithWrongReceiver.add(candidateCall);
074 }
075 else if (candidateCall.getStatus() != RECEIVER_PRESENCE_ERROR) {
076 failedCandidates.add(candidateCall);
077 }
078 }
079 // TODO : maybe it's better to filter overrides out first, and only then look for the maximally specific
080
081 if (!successfulCandidates.isEmpty() || !incompleteCandidates.isEmpty()) {
082 return computeSuccessfulResult(context, tracing, successfulCandidates, incompleteCandidates, context.checkArguments);
083 }
084 else if (!failedCandidates.isEmpty()) {
085 return computeFailedResult(tracing, context.trace, failedCandidates, context.checkArguments);
086 }
087 if (!candidatesWithWrongReceiver.isEmpty()) {
088 tracing.unresolvedReferenceWrongReceiver(context.trace, candidatesWithWrongReceiver);
089 return OverloadResolutionResultsImpl.candidatesWithWrongReceiver(candidatesWithWrongReceiver);
090 }
091 tracing.unresolvedReference(context.trace);
092 return OverloadResolutionResultsImpl.nameNotFound();
093 }
094
095 @NotNull
096 private <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> computeSuccessfulResult(
097 @NotNull CallResolutionContext context,
098 @NotNull TracingStrategy tracing,
099 @NotNull Set<MutableResolvedCall<D>> successfulCandidates,
100 @NotNull Set<MutableResolvedCall<D>> incompleteCandidates,
101 @NotNull CheckArgumentTypesMode checkArgumentsMode
102 ) {
103 Set<MutableResolvedCall<D>> successfulAndIncomplete = Sets.newLinkedHashSet();
104 successfulAndIncomplete.addAll(successfulCandidates);
105 successfulAndIncomplete.addAll(incompleteCandidates);
106 OverloadResolutionResultsImpl<D> results = chooseAndReportMaximallySpecific(
107 successfulAndIncomplete, true, context.isDebuggerContext, checkArgumentsMode);
108 if (results.isSingleResult()) {
109 MutableResolvedCall<D> resultingCall = results.getResultingCall();
110 resultingCall.getTrace().moveAllMyDataTo(context.trace);
111 if (resultingCall.getStatus() == INCOMPLETE_TYPE_INFERENCE) {
112 return OverloadResolutionResultsImpl.incompleteTypeInference(resultingCall);
113 }
114 }
115 if (results.isAmbiguity()) {
116 tracing.recordAmbiguity(context.trace, results.getResultingCalls());
117 boolean allCandidatesIncomplete = allIncomplete(results.getResultingCalls());
118 // This check is needed for the following case:
119 // x.foo(unresolved) -- if there are multiple foo's, we'd report an ambiguity, and it does not make sense here
120 if (context.checkArguments != CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS ||
121 !CallUtilKt.hasUnresolvedArguments(context.call, context)) {
122 if (allCandidatesIncomplete) {
123 tracing.cannotCompleteResolve(context.trace, results.getResultingCalls());
124 }
125 else {
126 tracing.ambiguity(context.trace, results.getResultingCalls());
127 }
128 }
129 if (allCandidatesIncomplete) {
130 return OverloadResolutionResultsImpl.incompleteTypeInference(results.getResultingCalls());
131 }
132 }
133 return results;
134 }
135
136 @NotNull
137 private <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> computeFailedResult(
138 @NotNull TracingStrategy tracing,
139 @NotNull BindingTrace trace,
140 @NotNull Set<MutableResolvedCall<D>> failedCandidates,
141 @NotNull CheckArgumentTypesMode checkArgumentsMode
142 ) {
143 if (failedCandidates.size() == 1) {
144 return recordFailedInfo(tracing, trace, failedCandidates);
145 }
146
147 for (EnumSet<ResolutionStatus> severityLevel : SEVERITY_LEVELS) {
148 Set<MutableResolvedCall<D>> thisLevel = Sets.newLinkedHashSet();
149 for (MutableResolvedCall<D> candidate : failedCandidates) {
150 if (severityLevel.contains(candidate.getStatus())) {
151 thisLevel.add(candidate);
152 }
153 }
154 if (!thisLevel.isEmpty()) {
155 if (severityLevel.contains(ARGUMENTS_MAPPING_ERROR)) {
156 return recordFailedInfo(tracing, trace, thisLevel);
157 }
158 OverloadResolutionResultsImpl<D> results = chooseAndReportMaximallySpecific(thisLevel, false, false, checkArgumentsMode);
159 return recordFailedInfo(tracing, trace, results.getResultingCalls());
160 }
161 }
162
163 throw new AssertionError("Should not be reachable, cause every status must belong to some level: " + failedCandidates);
164 }
165
166 @NotNull
167 private static <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> recordFailedInfo(
168 @NotNull TracingStrategy tracing,
169 @NotNull BindingTrace trace,
170 @NotNull Collection<MutableResolvedCall<D>> candidates
171 ) {
172 if (candidates.size() == 1) {
173 MutableResolvedCall<D> failed = candidates.iterator().next();
174 failed.getTrace().moveAllMyDataTo(trace);
175 return OverloadResolutionResultsImpl.singleFailedCandidate(failed);
176 }
177 tracing.noneApplicable(trace, candidates);
178 tracing.recordAmbiguity(trace, candidates);
179 return OverloadResolutionResultsImpl.manyFailedCandidates(candidates);
180 }
181
182 private static <D extends CallableDescriptor> boolean allIncomplete(@NotNull Collection<MutableResolvedCall<D>> results) {
183 for (MutableResolvedCall<D> result : results) {
184 if (result.getStatus() != INCOMPLETE_TYPE_INFERENCE) return false;
185 }
186 return true;
187 }
188
189 @NotNull
190 private <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> chooseAndReportMaximallySpecific(
191 @NotNull Set<MutableResolvedCall<D>> candidates,
192 boolean discriminateGenerics,
193 boolean isDebuggerContext,
194 @NotNull CheckArgumentTypesMode checkArgumentsMode
195 ) {
196 if (candidates.size() == 1) {
197 return OverloadResolutionResultsImpl.success(candidates.iterator().next());
198 }
199
200 if (candidates.iterator().next() instanceof VariableAsFunctionResolvedCall) {
201 candidates = overloadingConflictResolver.findMaximallySpecificVariableAsFunctionCalls(candidates);
202 }
203
204 Set<MutableResolvedCall<D>> noEquivalentCalls = overloadingConflictResolver.filterOutEquivalentCalls(candidates);
205 Set<MutableResolvedCall<D>> noOverrides =
206 OverridingUtil.filterOverrides(noEquivalentCalls, MAP_RESOLVED_CALL_TO_RESULTING_DESCRIPTOR);
207 if (noOverrides.size() == 1) {
208 return OverloadResolutionResultsImpl.success(noOverrides.iterator().next());
209 }
210
211 MutableResolvedCall<D> maximallySpecific = overloadingConflictResolver.findMaximallySpecific(noOverrides, checkArgumentsMode, false, isDebuggerContext);
212 if (maximallySpecific != null) {
213 return OverloadResolutionResultsImpl.success(maximallySpecific);
214 }
215
216 if (discriminateGenerics) {
217 MutableResolvedCall<D> maximallySpecificGenericsDiscriminated = overloadingConflictResolver.findMaximallySpecific(
218 noOverrides, checkArgumentsMode, true, isDebuggerContext);
219 if (maximallySpecificGenericsDiscriminated != null) {
220 return OverloadResolutionResultsImpl.success(maximallySpecificGenericsDiscriminated);
221 }
222 }
223
224 return OverloadResolutionResultsImpl.ambiguity(noOverrides);
225 }
226 }