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