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