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