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