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