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