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