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