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(
100 successfulAndIncomplete, true, context.isDebuggerContext, checkArgumentsMode);
101 if (results.isSingleResult()) {
102 MutableResolvedCall<D> resultingCall = results.getResultingCall();
103 resultingCall.getTrace().moveAllMyDataTo(context.trace);
104 if (resultingCall.getStatus() == INCOMPLETE_TYPE_INFERENCE) {
105 return OverloadResolutionResultsImpl.incompleteTypeInference(resultingCall);
106 }
107 }
108 if (results.isAmbiguity()) {
109 tracing.recordAmbiguity(context.trace, results.getResultingCalls());
110 boolean allCandidatesIncomplete = allIncomplete(results.getResultingCalls());
111 // This check is needed for the following case:
112 // x.foo(unresolved) -- if there are multiple foo's, we'd report an ambiguity, and it does not make sense here
113 if (context.checkArguments != CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS ||
114 !CallUtilKt.hasUnresolvedArguments(context.call, context)) {
115 if (allCandidatesIncomplete) {
116 tracing.cannotCompleteResolve(context.trace, results.getResultingCalls());
117 }
118 else {
119 tracing.ambiguity(context.trace, results.getResultingCalls());
120 }
121 }
122 if (allCandidatesIncomplete) {
123 return OverloadResolutionResultsImpl.incompleteTypeInference(results.getResultingCalls());
124 }
125 }
126 return results;
127 }
128
129 @NotNull
130 private <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> computeFailedResult(
131 @NotNull TracingStrategy tracing,
132 @NotNull BindingTrace trace,
133 @NotNull Set<MutableResolvedCall<D>> failedCandidates,
134 @NotNull CheckArgumentTypesMode checkArgumentsMode
135 ) {
136 if (failedCandidates.size() != 1) {
137 // This is needed when there are several overloads some of which are OK but for nullability of the receiver,
138 // and some are not OK at all. In this case we'd like to say "unsafe call" rather than "none applicable"
139 // Used to be: weak errors. Generalized for future extensions
140 for (EnumSet<ResolutionStatus> severityLevel : SEVERITY_LEVELS) {
141 Set<MutableResolvedCall<D>> thisLevel = Sets.newLinkedHashSet();
142 for (MutableResolvedCall<D> candidate : failedCandidates) {
143 if (severityLevel.contains(candidate.getStatus())) {
144 thisLevel.add(candidate);
145 }
146 }
147 if (!thisLevel.isEmpty()) {
148 if (severityLevel.contains(ARGUMENTS_MAPPING_ERROR)) {
149 return recordFailedInfo(tracing, trace, thisLevel);
150 }
151 OverloadResolutionResultsImpl<D> results = chooseAndReportMaximallySpecific(thisLevel, false, false, checkArgumentsMode);
152 return recordFailedInfo(tracing, trace, results.getResultingCalls());
153 }
154 }
155
156 assert false : "Should not be reachable, cause every status must belong to some level";
157
158 Set<MutableResolvedCall<D>> noOverrides = OverrideResolver.filterOutOverridden(failedCandidates, MAP_TO_CANDIDATE);
159 return recordFailedInfo(tracing, trace, noOverrides);
160 }
161
162 return recordFailedInfo(tracing, trace, failedCandidates);
163 }
164
165 @NotNull
166 private static <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> recordFailedInfo(
167 @NotNull TracingStrategy tracing,
168 @NotNull BindingTrace trace,
169 @NotNull Collection<MutableResolvedCall<D>> candidates
170 ) {
171 if (candidates.size() == 1) {
172 MutableResolvedCall<D> failed = candidates.iterator().next();
173 failed.getTrace().moveAllMyDataTo(trace);
174 return OverloadResolutionResultsImpl.singleFailedCandidate(failed);
175 }
176 tracing.noneApplicable(trace, candidates);
177 tracing.recordAmbiguity(trace, candidates);
178 return OverloadResolutionResultsImpl.manyFailedCandidates(candidates);
179 }
180
181 private static <D extends CallableDescriptor> boolean allIncomplete(@NotNull Collection<MutableResolvedCall<D>> results) {
182 for (MutableResolvedCall<D> result : results) {
183 if (result.getStatus() != INCOMPLETE_TYPE_INFERENCE) return false;
184 }
185 return true;
186 }
187
188 @NotNull
189 public <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> chooseAndReportMaximallySpecific(
190 @NotNull Set<MutableResolvedCall<D>> candidates,
191 boolean discriminateGenerics,
192 boolean isDebuggerContext,
193 @NotNull CheckArgumentTypesMode checkArgumentsMode
194 ) {
195 if (candidates.size() == 1) {
196 return OverloadResolutionResultsImpl.success(candidates.iterator().next());
197 }
198
199 if (candidates.iterator().next() instanceof VariableAsFunctionResolvedCall) {
200 candidates = overloadingConflictResolver.findMaximallySpecificVariableAsFunctionCalls(candidates);
201 }
202
203 Set<MutableResolvedCall<D>> noOverrides = OverrideResolver.filterOutOverridden(candidates, MAP_TO_RESULT);
204 if (noOverrides.size() == 1) {
205 return OverloadResolutionResultsImpl.success(noOverrides.iterator().next());
206 }
207
208 MutableResolvedCall<D> maximallySpecific = overloadingConflictResolver.findMaximallySpecific(noOverrides, checkArgumentsMode, false, isDebuggerContext);
209 if (maximallySpecific != null) {
210 return OverloadResolutionResultsImpl.success(maximallySpecific);
211 }
212
213 if (discriminateGenerics) {
214 MutableResolvedCall<D> maximallySpecificGenericsDiscriminated = overloadingConflictResolver.findMaximallySpecific(
215 noOverrides, checkArgumentsMode, true, isDebuggerContext);
216 if (maximallySpecificGenericsDiscriminated != null) {
217 return OverloadResolutionResultsImpl.success(maximallySpecificGenericsDiscriminated);
218 }
219 }
220
221 return OverloadResolutionResultsImpl.ambiguity(noOverrides);
222 }
223
224
225 }