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